Skip to content

Commit

Permalink
V10.0.7 (#21)
Browse files Browse the repository at this point in the history
* Allowing to join to an existing wallet

* wip

* wip

* wip

* ui improvements

* qr init

* Proper dev env usage

* css stuff

* QR modal

* current dialog reuse

* merge

* better vite config

* clear storage, no refresh button, call new device when join, qr updates

* yarn.lock

* upgrade-sdk

* add stop, upgrade sdk

* sdk version

* Takeover section

* sdk version

* add env

* change platform type to 'Web'

* vesrion

* wallet id input logic and typo

* reloading on clear, stop join button hidding

* No qr after join is finished

* Added refresh assets button

* css fix

* version

* New tx modal init

* small logic fix

* version

* version

* version

* wip

* fix

* fix

* fixing branch

* fixing .env

* version

* fix

---------

Co-authored-by: Gil Amran <gil@fireblocks.com>
Co-authored-by: Yuval Niezni <yniezni@fireblocks.com>
Co-authored-by: Vitaliy Paykov <vitaliy@fireblocks.com>
Co-authored-by: Github Actions <github-actions@github.com>
  • Loading branch information
5 people authored Jan 22, 2024
1 parent dd77fc4 commit b2453e9
Show file tree
Hide file tree
Showing 33 changed files with 3,785 additions and 4,940 deletions.
2 changes: 1 addition & 1 deletion .env
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ VITE_CLOUDKIT_ENV=production
# VITE_NCW_SDK_ENV=dev9
# VITE_CLOUDKIT_APITOKEN=572a0b5cfcb8992031640d1a14fd0ac3bb7c774cc929a0e23cb00af415da51cd
# VITE_CLOUDKIT_CONTAINER_ID=iCloud.com.fireblocks.ncw.demo
# VITE_CLOUDKIT_ENV=development
# VITE_CLOUDKIT_ENV=development
10 changes: 6 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,25 @@
"keywords": [],
"author": "Gil Amran",
"license": "MIT",
"packageManager": "yarn@3.3.1",
"packageManager": "yarn@1.22.19",
"scripts": {
"dev": "vite",
"dev": "vite --host",
"build": "tsc && vite build",
"build-tailwind": "yarn tailwindcss -i ./src/demo-app/index.css -o public/css/index.css",
"lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"format": "prettier src --write",
"preview": "vite preview"
},
"dependencies": {
"@fireblocks/ncw-js-sdk": "^10.0.0",
"@fireblocks/ncw-js-sdk": "10.0.7",
"base58-js": "^2.0.0",
"classnames": "^2.3.2",
"js-base64": "^3.7.5",
"memfs": "^4.6.0",
"node-forge": "^1.3.1",
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react-dom": "^18.2.0",
"react-qr-code": "^2.0.12"
},
"devDependencies": {
"@types/gapi": "^0.0.46",
Expand Down
99 changes: 96 additions & 3 deletions src/AppStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
ConsoleLogger,
FireblocksNCW,
IEventsHandler,
IJoinWalletEvent,
IKeyBackupEvent,
IKeyDescriptor,
IKeyRecoveryEvent,
Expand All @@ -12,16 +13,18 @@ import {
TMPCAlgorithm,
} from "@fireblocks/ncw-js-sdk";
import { create } from "zustand";
import { IAppState, IPassphraseInfo, TPassphrases } from "./IAppState";
import { IAppState, IPassphraseInfo, TPassphrases, TAppMode, INewTransactionData } from "./IAppState";
import { generateDeviceId, getOrCreateDeviceId, storeDeviceId } from "./deviceId";
import { ENV_CONFIG } from "./env_config";
import { ApiService, ITransactionData, IWalletAsset, TPassphraseLocation } from "./services/ApiService";
import { PasswordEncryptedLocalStorage } from "./services/PasswordEncryptedLocalStorage";
import { IAuthManager } from "./auth/IAuthManager";
import { FirebaseAuthManager } from "./auth/FirebaseAuthManager";
import { decode } from "js-base64";

export type TAsyncActionStatus = "not_started" | "started" | "success" | "failed";
export type TFireblocksNCWStatus = "sdk_not_ready" | "initializing_sdk" | "sdk_available" | "sdk_initialization_failed";
export type TRequestDecodedData = { email: string; requestId: string; platform: string };

export const useAppStore = create<IAppState>()((set, get) => {
let apiService: ApiService | null = null;
Expand All @@ -45,7 +48,9 @@ export const useAppStore = create<IAppState>()((set, get) => {
return {
fireblocksNCWSdkVersion: FireblocksNCW.version,
automateInitialization: ENV_CONFIG.AUTOMATE_INITIALIZATION,
joinExistingWalletMode: false,
loggedUser: authManager.loggedUser,
appMode: null,
userId: null,
walletId: null,
latestBackup: null,
Expand All @@ -56,9 +61,13 @@ export const useAppStore = create<IAppState>()((set, get) => {
deviceId: null,
loginToDemoAppServerStatus: "not_started",
assignDeviceStatus: "not_started",
joinWalletStatus: "not_started",
fireblocksNCWStatus: "sdk_not_ready",
addDeviceRequestId: null,
keysStatus: null,
passphrase: null,
passphrases: null,
regeneratePassphrase: () => {},
accounts: [],
supportedAssets: {},
initAppStore: () => {
Expand Down Expand Up @@ -103,6 +112,7 @@ export const useAppStore = create<IAppState>()((set, get) => {
appStoreInitialized: false,
loginToDemoAppServerStatus: "not_started",
assignDeviceStatus: "not_started",
joinWalletStatus: "not_started",
fireblocksNCWStatus: "sdk_not_ready",
keysStatus: null,
accounts: [],
Expand All @@ -125,6 +135,39 @@ export const useAppStore = create<IAppState>()((set, get) => {
set((state) => ({ ...state, walletId: null, assignDeviceStatus: "failed" }));
}
},
askToJoinWalletExisting: async () => {
set((state) => ({ ...state, joinWalletStatus: "started" }));
if (!apiService) {
throw new Error("apiService is not initialized");
}
const { walletId, deviceId } = get();
if (!walletId) {
throw new Error("walletId is not set");
}
if (!deviceId) {
throw new Error("deviceId is not set");
}

try {
await apiService.askToJoinWalletExisting(deviceId, walletId);
set((state) => ({
...state,
deviceId,
walletId,
assignDeviceStatus: "success",
joinWalletStatus: "success",
joinExistingWalletMode: true,
}));
} catch (e) {
set((state) => ({
...state,
deviceId: null,
walletId: null,
assignDeviceStatus: "failed",
joinWalletStatus: "failed",
}));
}
},
generateNewDeviceId: async () => {
const { userId } = get();
if (!userId) {
Expand All @@ -134,6 +177,9 @@ export const useAppStore = create<IAppState>()((set, get) => {
set((state) => ({ ...state, deviceId, walletId: null, assignDeviceStatus: "not_started" }));
storeDeviceId(deviceId, userId);
},
setAppMode: (appMode: TAppMode) => {
set((state) => ({ ...state, appMode, assignDeviceStatus: "not_started" }));
},
setDeviceId: (deviceId: string) => {
const { userId } = get();
if (!userId) {
Expand All @@ -142,6 +188,15 @@ export const useAppStore = create<IAppState>()((set, get) => {
storeDeviceId(deviceId, userId);
set((state) => ({ ...state, deviceId }));
},
setWalletId: (walletId: string) => {
set((state) => ({ ...state, walletId }));
},
setPassphrase: (passphrase: string) => {
const { userId } = get();
if (!userId) {
throw new Error("First login to demo app server");
}
},
getLatestBackup: async () => {
if (!apiService) {
throw new Error("apiService is not initialized");
Expand All @@ -153,6 +208,40 @@ export const useAppStore = create<IAppState>()((set, get) => {
const latestBackup = await apiService.getLatestBackup(walletId);
set((state) => ({ ...state, latestBackup }));
},
approveJoinWallet: async () => {
if (!fireblocksNCW) {
throw new Error("fireblocksNCW is not initialized");
}
const requestData = await prompt("Insert encoded request data");
if (requestData) {
try {
const decodedData: TRequestDecodedData = JSON.parse(decode(requestData));
const result = await fireblocksNCW.approveJoinWalletRequest(decodedData.requestId);
console.log("approveJoinWallet result:", result);
} catch (e) {
console.error(e);
}
// set((state) => ({ ...state, passphrase }));
} else {
console.log("approveJoinWallet cancelled");
}
},
joinExistingWallet: async () => {
if (!fireblocksNCW) {
throw new Error("fireblocksNCW is not initialized");
}
await fireblocksNCW.requestJoinExistingWallet({
onRequestId(requestId: string) {
set((state) => ({ ...state, addDeviceRequestId: requestId }));
},
});
},
stopJoinExistingWallet: () => {
if (!fireblocksNCW) {
throw new Error("fireblocksNCW is not initialized");
}
fireblocksNCW.stopJoinWallet();
},
getPassphraseInfos: async () => {
if (!apiService) {
throw new Error("apiService is not initialized");
Expand Down Expand Up @@ -251,6 +340,10 @@ export const useAppStore = create<IAppState>()((set, get) => {
case "keys_recovery":
console.log(`Key recover status: ${JSON.stringify((event as IKeyRecoveryEvent).keyDescriptor)}`);
break;

case "join_wallet_descriptor":
console.log(`join wallet event: ${JSON.stringify((event as IJoinWalletEvent).joinWalletDescriptor)}`);
break;
}
},
};
Expand Down Expand Up @@ -300,15 +393,15 @@ export const useAppStore = create<IAppState>()((set, get) => {
const keysStatus = await fireblocksNCW.getKeysStatus();
set((state) => ({ ...state, keysStatus }));
},
createTransaction: async () => {
createTransaction: async (dataToSend?: INewTransactionData) => {
if (!apiService) {
throw new Error("apiService is not initialized");
}
const { deviceId } = get();
if (!deviceId) {
throw new Error("deviceId is not set");
}
const newTxData = await apiService.createTransaction(deviceId);
const newTxData = await apiService.createTransaction(deviceId, dataToSend);
const txs = updateOrAddTx(get().txs, newTxData);
set((state) => ({ ...state, txs }));
},
Expand Down
26 changes: 25 additions & 1 deletion src/IAppState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export interface IPassphraseInfo {

type TAccount = Record<string, IAssetInfo>;
type TSupportedAssets = Record<string, IWalletAsset>;
export type TAppMode = "SIGN_IN" | "JOIN" | null;
export type TPassphrases = Record<string, IPassphraseInfo>;

export interface IBackupInfo {
Expand All @@ -32,9 +33,21 @@ export interface IBackupInfo {
createdAt: number;
}

export interface INewTransactionData {
note: string;
accountId: string;
assetId: string;
amount: string;
destAddress: string;
feeLevel: "LOW" | "MEDIUM" | "HIGH";
estimateFee: boolean;
}

export interface IAppState {
appMode: TAppMode;
fireblocksNCWSdkVersion: string;
automateInitialization: boolean;
joinExistingWalletMode: boolean;
loggedUser: IUser | null;
userId: string | null;
deviceId: string | null;
Expand All @@ -46,24 +59,30 @@ export interface IAppState {
appStoreInitialized: boolean;
loginToDemoAppServerStatus: TAsyncActionStatus;
assignDeviceStatus: TAsyncActionStatus;
joinWalletStatus: TAsyncActionStatus;
fireblocksNCWStatus: TFireblocksNCWStatus;
keysStatus: Record<TMPCAlgorithm, IKeyDescriptor> | null;
passphrase: string | null;
addDeviceRequestId: string | null;
accounts: TAccount[];
passphrases: TPassphrases | null;
supportedAssets: Record<number, TSupportedAssets>;
initAppStore: () => void;
disposeAppStore: () => void;
getGoogleDriveCredentials: () => Promise<string>;
login(provider: "GOOGLE" | "APPLE"): Promise<void>;
setAppMode: (mode: TAppMode) => void;
logout: () => Promise<void>;
clearSDKStorage: () => Promise<void>;
setDeviceId: (deviceId: string) => void;
setWalletId: (walletId: string) => void;
loginToDemoAppServer: () => void;
assignCurrentDevice: () => Promise<void>;
askToJoinWalletExisting: () => Promise<void>;
generateNewDeviceId: () => Promise<void>;
generateMPCKeys: () => Promise<void>;
stopMpcDeviceSetup: () => Promise<void>;
createTransaction: () => Promise<void>;
createTransaction: (dataToSend?: INewTransactionData) => Promise<void>;
cancelTransaction: (txId: string) => Promise<void>;
signTransaction: (txId: string) => Promise<void>;
takeover: () => Promise<IFullKey[]>;
Expand All @@ -75,6 +94,11 @@ export interface IAppState {
change: number,
index: number,
) => string;
setPassphrase: (passphrase: string) => void;
approveJoinWallet: () => Promise<void>;
joinExistingWallet: () => Promise<void>;
stopJoinExistingWallet: () => void;
regeneratePassphrase: () => void;
getPassphraseInfos: () => Promise<void>;
getLatestBackup: () => Promise<void>;
createPassphraseInfo: (passphraseId: string, location: TPassphraseLocation) => Promise<void>;
Expand Down
1 change: 1 addition & 0 deletions src/auth/IAuthManager.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export interface IUser {
displayName: string | null;
email: string | null;
}

export interface IAuthManager {
Expand Down
17 changes: 12 additions & 5 deletions src/components/AppContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import { useAppStore } from "../AppStore";
import { AssignDevice } from "./AssignDevice";
import { FireblocksNCWInitializer } from "./FireblocksNCWInitializer";
import { LoginToDemoAppServer } from "./LoginToDemoAppServer";
import { FireblockNCWExampleActions } from "./FireblockNCWExampleActions";
import { FireblocksNCWExampleActions } from "./FireblocksNCWExampleActions";
import { ModeSelector } from "./ModeSelector";

export const AppContent: React.FC = () => {
const {
Expand All @@ -14,6 +15,7 @@ export const AppContent: React.FC = () => {
fireblocksNCWStatus,
initAppStore,
disposeAppStore,
appMode,
} = useAppStore();

React.useEffect(() => {
Expand All @@ -35,11 +37,16 @@ export const AppContent: React.FC = () => {
<LoginToDemoAppServer />
{loginToDemoAppServerStatus === "success" && (
<>
<AssignDevice />
{assignDeviceStatus === "success" && (
<ModeSelector />
{appMode && (
<>
<FireblocksNCWInitializer />
{fireblocksNCWStatus === "sdk_available" && <FireblockNCWExampleActions />}
<AssignDevice />
{assignDeviceStatus === "success" && (
<>
<FireblocksNCWInitializer />
{fireblocksNCWStatus === "sdk_available" && <FireblocksNCWExampleActions />}
</>
)}
</>
)}
</>
Expand Down
18 changes: 1 addition & 17 deletions src/components/AssetRow.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,12 @@
import React from "react";
import { Copyable } from "./ui/Copyable";
import { IAssetInfo } from "../IAppState";
import { missingIcon } from "../icons/missingIcon";

interface IProps {
assetInfo: IAssetInfo;
}

export const missingIcon = (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
className="w-6 h-6"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M12 9v3.75m9-.75a9 9 0 11-18 0 9 9 0 0118 0zm-9 3.75h.008v.008H12v-.008z"
/>
</svg>
);

export const AssetRow: React.FC<IProps> = ({ assetInfo }) => {
const { asset, address, balance } = assetInfo;

Expand Down
Loading

0 comments on commit b2453e9

Please sign in to comment.