vega-frontend-monorepo/libs/wallet/src/provider.tsx

189 lines
4.9 KiB
TypeScript

import { LocalStorage } from '@vegaprotocol/utils';
import type { ReactNode } from 'react';
import { useCallback, useMemo, useRef, useState } from 'react';
import { WalletClientError } from '@vegaprotocol/wallet-client';
import type { VegaWalletContextShape } from '.';
import type {
PubKey,
Transaction,
VegaConnector,
} from './connectors/vega-connector';
import { VegaWalletContext } from './context';
import { WALLET_KEY, WALLET_RISK_ACCEPTED_KEY } from './storage';
import { ViewConnector } from './connectors';
import { useLocalStorage } from '@vegaprotocol/react-helpers';
type Networks =
| 'MAINNET'
| 'MAINNET_MIRROR'
| 'TESTNET'
| 'VALIDATOR_TESTNET'
| 'STAGNET1'
| 'DEVNET'
| 'CUSTOM';
interface VegaWalletLinks {
explorer: string;
concepts: string;
chromeExtensionUrl: string;
mozillaExtensionUrl: string;
}
export interface VegaWalletConfig {
network: Networks;
vegaUrl: string;
vegaWalletServiceUrl: string;
links: VegaWalletLinks;
}
const ExternalLinks = {
VEGA_WALLET_URL_ABOUT: 'https://vega.xyz/wallet/#overview',
VEGA_WALLET_BROWSER_LIST: '',
};
interface VegaWalletProviderProps {
children: ReactNode;
config: VegaWalletConfig;
}
export const VegaWalletProvider = ({
children,
config,
}: VegaWalletProviderProps) => {
// Current selected pubKey
const [pubKey, setPubKey] = useState<string | null>(null);
const [isReadOnly, setIsReadOnly] = useState<boolean>(false);
// Array of public keys retrieved from the connector
const [pubKeys, setPubKeys] = useState<PubKey[] | null>(null);
// Reference to the current connector instance
const connector = useRef<VegaConnector | null>(null);
const selectPubKey = useCallback((pk: string) => {
setPubKey(pk);
LocalStorage.setItem(WALLET_KEY, pk);
}, []);
const fetchPubKeys = useCallback(async () => {
if (!connector.current) {
throw new Error('No connector');
}
try {
const keys = await connector.current.connect();
if (keys?.length) {
setPubKeys(keys);
setIsReadOnly(connector.current instanceof ViewConnector);
return keys;
} else {
return null;
}
} catch (err) {
if (err instanceof WalletClientError) {
throw err;
}
return null;
}
}, []);
const connect = useCallback(async (c: VegaConnector) => {
connector.current = c;
try {
const keys = await connector.current.connect();
if (keys?.length) {
setPubKeys(keys);
setIsReadOnly(connector.current instanceof ViewConnector);
const lastUsedPubKey = LocalStorage.getItem(WALLET_KEY);
const foundKey = keys.find((key) => key.publicKey === lastUsedPubKey);
if (foundKey) {
setPubKey(foundKey.publicKey);
} else {
setPubKey(keys[0].publicKey);
}
return keys;
} else {
return null;
}
} catch (err) {
if (err instanceof WalletClientError) {
throw err;
}
return null;
}
}, []);
const disconnect = useCallback(async () => {
// always clear state after attempted disconnection.. this
// is because long lived token sessions (used in tests)
// cannot be cleared. Clearing state will force user to reconnect
// again as expected
setPubKeys(null);
setPubKey(null);
setIsReadOnly(false);
LocalStorage.removeItem(WALLET_KEY);
try {
await connector.current?.disconnect();
connector.current = null;
} catch (err) {
console.error(err);
connector.current = null;
}
}, []);
const sendTx = useCallback((pubkey: string, transaction: Transaction) => {
if (!connector.current) {
throw new Error('No connector');
}
return connector.current.sendTx(pubkey, transaction);
}, []);
const [riskAcceptedValue] = useLocalStorage(WALLET_RISK_ACCEPTED_KEY);
const acknowledgeNeeded =
config.network === 'MAINNET' && riskAcceptedValue !== 'true';
const contextValue = useMemo<VegaWalletContextShape>(() => {
return {
vegaUrl: config.vegaUrl,
vegaWalletServiceUrl: config.vegaWalletServiceUrl,
network: config.network,
links: {
explorer: config.links.explorer,
about: ExternalLinks.VEGA_WALLET_URL_ABOUT,
browserList: ExternalLinks.VEGA_WALLET_BROWSER_LIST,
concepts: config.links.concepts,
chromeExtensionUrl: config.links.chromeExtensionUrl,
mozillaExtensionUrl: config.links.mozillaExtensionUrl,
},
isReadOnly,
pubKey,
pubKeys,
selectPubKey,
connect,
disconnect,
sendTx,
fetchPubKeys,
acknowledgeNeeded,
};
}, [
config,
isReadOnly,
pubKey,
pubKeys,
selectPubKey,
connect,
disconnect,
sendTx,
fetchPubKeys,
acknowledgeNeeded,
]);
return (
<VegaWalletContext.Provider value={contextValue}>
{children}
</VegaWalletContext.Provider>
);
};