From ffbe735ec2707064259931783c516a72341b96a0 Mon Sep 17 00:00:00 2001 From: David Colon Date: Wed, 8 Jun 2022 20:51:38 -0400 Subject: [PATCH] adds xdefi provider wrapper - if xdefi wallet is connected exposes a bitcoin and litecoin provider - creates a context provider to pass wrapped wallet provider --- src/context/XDEFIProvider/XDEFIContext.ts | 11 ++ .../XDEFIProvider/XDEFIWalletProvider.tsx | 138 ++++++++++++++++++ src/context/XDEFIProvider/actions.ts | 28 ++++ .../XDEFIProvider/hooks/useXDEFIProvider.ts | 6 + src/context/XDEFIProvider/types.ts | 27 ++++ 5 files changed, 210 insertions(+) create mode 100644 src/context/XDEFIProvider/XDEFIContext.ts create mode 100644 src/context/XDEFIProvider/XDEFIWalletProvider.tsx create mode 100644 src/context/XDEFIProvider/actions.ts create mode 100644 src/context/XDEFIProvider/hooks/useXDEFIProvider.ts create mode 100644 src/context/XDEFIProvider/types.ts diff --git a/src/context/XDEFIProvider/XDEFIContext.ts b/src/context/XDEFIProvider/XDEFIContext.ts new file mode 100644 index 00000000000..96405822c91 --- /dev/null +++ b/src/context/XDEFIProvider/XDEFIContext.ts @@ -0,0 +1,11 @@ +import { createContext } from 'react' + +import { ActionTypes } from './actions' +import { InitialState } from './types' + +export interface IXDEFIProviderContext { + state: InitialState + dispatch: React.Dispatch +} + +export const XDEFIProviderContext = createContext(null) diff --git a/src/context/XDEFIProvider/XDEFIWalletProvider.tsx b/src/context/XDEFIProvider/XDEFIWalletProvider.tsx new file mode 100644 index 00000000000..84cef094496 --- /dev/null +++ b/src/context/XDEFIProvider/XDEFIWalletProvider.tsx @@ -0,0 +1,138 @@ +/* eslint-disable no-console */ +import React, { useCallback, useEffect, useMemo, useReducer } from 'react' + +import { useWallet } from '../../hooks/useWallet/useWallet' +import { KeyManager } from '../WalletProvider/KeyManager' +import { getLocalWalletDeviceId, getLocalWalletType } from '../WalletProvider/local-wallet' +import { ActionTypes, XDEFIProviderActions } from './actions' +import { InitialState, IXfiBitcoinProvider, IXfiLitecoinProvider } from './types' +import { IXDEFIProviderContext, XDEFIProviderContext } from './XDEFIContext' + +const initialState: InitialState = { + ethereumWallet: null, + xfiBitcoinProvider: null, + xfiLitecoinProvider: null, + isWalletLoading: false, +} + +export const reducer = (state: InitialState, action: ActionTypes) => { + switch (action.type) { + case XDEFIProviderActions.XDEFI_CONNECTED: { + return { + ...state, + ...action.payload, + isWalletLoading: false, + } + } + case XDEFIProviderActions.XDEFI_NOT_CONNECTED: { + return { + ...initialState, + ...action.payload, + isWalletLoading: false, + } + } + case XDEFIProviderActions.RESET_STATE: { + return { + ...initialState, + isWalletLoading: false, + } + } + default: + return state + } +} + +const getInitialState = () => { + const localWalletType = getLocalWalletType() + const localWalletDeviceId = getLocalWalletDeviceId() + if (localWalletType && localWalletDeviceId) { + return { + ...initialState, + isWalletLoading: true, + } + } + return initialState +} + +const injectedAccount = async ( + xfiProvider: IXfiBitcoinProvider | IXfiLitecoinProvider, +): Promise => { + return new Promise(resolve => { + xfiProvider.request( + { method: 'request_accounts', params: [] }, + (error: any, accounts: string[]) => { + if (!error && accounts.length) { + resolve(accounts) + } + if (error) { + resolve([]) + } + }, + ) + }) +} + +export const XDEFIWalletProvider = ({ children }: { children: React.ReactNode }): JSX.Element => { + const [state, dispatch] = useReducer(reducer, getInitialState()) + const { + state: { wallet }, + } = useWallet() + const localWalletType = getLocalWalletType() + const localWalletDeviceId = getLocalWalletDeviceId() + + const load = useCallback(() => { + if (localWalletType && localWalletDeviceId) { + switch (localWalletType) { + case KeyManager.XDefi: { + ;(async () => { + const xfi = (window as any).xfi + const xfiBitcoinProvider: IXfiBitcoinProvider = xfi['bitcoin'] + const xfiLitecoinProvider: IXfiLitecoinProvider = xfi['litecoin'] + const xfiBitcoinAccounts = await injectedAccount(xfiBitcoinProvider) + const xfiLItecoinAccounts = await injectedAccount(xfiLitecoinProvider) + + dispatch({ + type: XDEFIProviderActions.XDEFI_CONNECTED, + payload: { + ethereumWallet: wallet!, + xfiBitcoinProvider: { + ...xfiBitcoinProvider, + accounts: xfiBitcoinAccounts, + }, + xfiLitecoinProvider: { + ...xfiLitecoinProvider, + accounts: xfiLItecoinAccounts, + }, + }, + }) + })() + break + } + default: { + dispatch({ + type: XDEFIProviderActions.XDEFI_NOT_CONNECTED, + payload: { + ethereumWallet: wallet!, + }, + }) + } + } + } else { + dispatch({ + type: XDEFIProviderActions.RESET_STATE, + }) + } + }, [localWalletType, localWalletDeviceId, wallet]) + + useEffect(() => load(), [load]) + + const value: IXDEFIProviderContext = useMemo( + () => ({ + state, + dispatch, + }), + [state], + ) + + return {children} +} diff --git a/src/context/XDEFIProvider/actions.ts b/src/context/XDEFIProvider/actions.ts new file mode 100644 index 00000000000..8fc7a48a189 --- /dev/null +++ b/src/context/XDEFIProvider/actions.ts @@ -0,0 +1,28 @@ +import { HDWallet } from '@shapeshiftoss/hdwallet-core' + +import { IXfiBitcoinProvider, IXfiLitecoinProvider } from './types' + +export enum XDEFIProviderActions { + XDEFI_CONNECTED = 'XDEFI_CONNECTED', + XDEFI_NOT_CONNECTED = 'XDEFI_NOT_CONNECTED', + RESET_STATE = 'RESET_STATE', +} + +export type ActionTypes = + | { + type: XDEFIProviderActions.XDEFI_CONNECTED + payload: { + ethereumWallet: HDWallet + xfiBitcoinProvider: IXfiBitcoinProvider + xfiLitecoinProvider: IXfiLitecoinProvider + } + } + | { + type: XDEFIProviderActions.XDEFI_NOT_CONNECTED + payload: { + ethereumWallet: HDWallet + } + } + | { + type: XDEFIProviderActions.RESET_STATE + } diff --git a/src/context/XDEFIProvider/hooks/useXDEFIProvider.ts b/src/context/XDEFIProvider/hooks/useXDEFIProvider.ts new file mode 100644 index 00000000000..a378e6dcd68 --- /dev/null +++ b/src/context/XDEFIProvider/hooks/useXDEFIProvider.ts @@ -0,0 +1,6 @@ +import { useContext } from 'react' + +import { IXDEFIProviderContext, XDEFIProviderContext } from '../XDEFIContext' + +export const useXDEFIProvider = (): IXDEFIProviderContext => + useContext(XDEFIProviderContext as React.Context) diff --git a/src/context/XDEFIProvider/types.ts b/src/context/XDEFIProvider/types.ts new file mode 100644 index 00000000000..fa97ce91f7d --- /dev/null +++ b/src/context/XDEFIProvider/types.ts @@ -0,0 +1,27 @@ +import { HDWallet } from '@shapeshiftoss/hdwallet-core' + +import { AnyFunction } from '../../types/common' + +export interface InitialState { + ethereumWallet: HDWallet | null + xfiBitcoinProvider: IXfiBitcoinProvider | null + xfiLitecoinProvider: IXfiLitecoinProvider | null + isWalletLoading: boolean +} + +export interface IXfiBitcoinProvider { + accounts: string[] + chainId: string + network: string + request: (e: { method: string; params: any[] }, cb: AnyFunction) => Promise + signTransaction: (e: any) => Promise + transfer: (e: any) => Promise +} + +export interface IXfiLitecoinProvider { + accounts: string[] + chainId: string + network: string + request: (e: { method: string; params: any[] }, cb: AnyFunction) => void + transfer: (e: any) => void +}