Skip to content

Instantly share code, notes, and snippets.

@tai-bcw
Last active July 15, 2022 07:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tai-bcw/d9c8707f16865bb6331e90a8882469ff to your computer and use it in GitHub Desktop.
Save tai-bcw/d9c8707f16865bb6331e90a8882469ff to your computer and use it in GitHub Desktop.
import { atom } from "../atoms/atoms";
import {
SENTRY_CATEGORY,
useSentry,
} from "../hooks/useSentry";
import HashconnectAccount from "../types/hashconnect";
import {
HashConnect,
HashConnectTypes,
MessageTypes,
} from "hashconnect";
import React,
{
createContext,
useEffect,
useState,
} from "react";
import {
useRecoilState,
useResetRecoilState,
} from "recoil";
type Networks = "testnet" | "mainnet" | "previewnet";
interface Props {
children: React.ReactNode;
hashConnect: HashConnect;
network?: Networks;
debug?: boolean;
}
export interface HashConnectProviderAPI {
connect: () => void;
installedExtensions: HashConnectTypes.WalletMetadata | null;
isHashpackConnected: boolean;
setIsHashpackConnected: React.Dispatch<React.SetStateAction<boolean>>;
network: Networks;
resetPairing: () => void;
walletData: HashconnectAccount;
hashConnect: HashConnect;
}
const APP_CONFIG: HashConnectTypes.AppMetadata = {
name: 'hashport',
description: 'A public utility that facilitates the movement of digital assets between networks in a quick, secure, and cost-effective way',
icon: 'https://www.hashport.network/wp-content/uploads/2021/06/hashport-glyph-light.svg',
};
export const HashConnectAPIContext = createContext<HashConnectProviderAPI>({} as HashConnectProviderAPI);
export default function HashConnectProvider ({
children,
hashConnect,
network = 'testnet',
debug = false,
}: Props) {
const { logException } = useSentry();
const [ hashconnectAccount, setHashconnectAccount ] = useRecoilState(atom.hashconnectAccount);
const resetHashconnectAccount = useResetRecoilState(atom.hashconnectAccount);
const [ isHashpackConnected, setIsHashpackConnected ] = useState(false);
const [ installedExtensions, setInstalledExtensions ] = useState<HashConnectTypes.WalletMetadata | null>(null);
const isNewConnection = () => {
const {
topic,
pairingString,
privateKey,
pairedAccounts,
pairedWalletData,
} = hashconnectAccount;
return (topic === '' ||
pairingString === '' ||
privateKey === '' ||
pairedAccounts.length === 0 ||
pairedWalletData === null
);
};
const initializeHashConnect = async () => {
try {
if (isNewConnection()) {
const initData = await hashConnect.init(APP_CONFIG);
const privateKey = initData.privKey;
const state = await hashConnect.connect();
const { topic } = state;
const pairingString = hashConnect.generatePairingString(
state,
network,
debug ?? false,
);
setHashconnectAccount((prevState) => ({
...prevState,
...{
privateKey,
topic,
pairingString,
network,
pairedWalletData: APP_CONFIG,
},
}));
} else {
await hashConnect.init(APP_CONFIG, hashconnectAccount.privateKey);
await hashConnect.connect(
hashconnectAccount.topic,
hashconnectAccount.pairedWalletData ?? APP_CONFIG,
);
setIsHashpackConnected(true);
}
//Fires foundExtensionEvent:
hashConnect.findLocalWallets();
} catch (e) {
logException(e, SENTRY_CATEGORY.WALLET_CONNECTION);
}
};
const additionalAccountRequestEventHandler = (data: MessageTypes.AdditionalAccountRequest) => {
if (debug) console.log('=====additionalAccountRequestEvent======', data);
// TODO: Handle requesting additional accounts
};
const acknowledgeMessageEventHandler = (data: MessageTypes.Acknowledge) => {
if (debug) console.log('====acknowledge::Wallet ack====', data);
};
const foundExtensionEventHandler = (data: HashConnectTypes.WalletMetadata) => {
if (debug) console.log('====foundExtensionEvent====', data);
setInstalledExtensions(data);
};
const pairingEventHandler = (data: MessageTypes.ApprovePairing) => {
const accountIds: string[] = [];
data.accountIds.forEach(id => {
if (hashconnectAccount.pairedAccounts.indexOf(id) === -1) {
accountIds.push(id);
}
});
setHashconnectAccount((prevState) => ({
...prevState,
topic: data.topic,
pairedWalletData: data.metadata,
pairedAccounts: accountIds,
}));
setIsHashpackConnected(true);
};
useEffect(() => {
initializeHashConnect();
hashConnect.additionalAccountRequestEvent.on(additionalAccountRequestEventHandler);
hashConnect.acknowledgeMessageEvent.on(acknowledgeMessageEventHandler);
hashConnect.additionalAccountRequestEvent.on(additionalAccountRequestEventHandler);
hashConnect.foundExtensionEvent.on(foundExtensionEventHandler);
hashConnect.pairingEvent.on(pairingEventHandler);
return () => {
hashConnect.additionalAccountRequestEvent.off(additionalAccountRequestEventHandler);
hashConnect.foundExtensionEvent.off(foundExtensionEventHandler);
hashConnect.pairingEvent.off(pairingEventHandler);
};
}, []);
const resetPairing = () => {
if (debug) console.log('====Clearing pairings====');
resetHashconnectAccount();
setIsHashpackConnected(false);
};
const connect = () => {
if (installedExtensions && !isHashpackConnected) {
if (debug) console.log('Pairing String::', hashconnectAccount.pairingString);
hashConnect.connectToLocalWallet(hashconnectAccount.pairingString);
} else {
if (debug && !installedExtensions) console.log('====No extension in browser====');
}
};
return (
<HashConnectAPIContext.Provider
value={{
connect,
walletData: hashconnectAccount,
network,
installedExtensions,
isHashpackConnected,
setIsHashpackConnected,
resetPairing,
hashConnect,
}}>
{children}
</HashConnectAPIContext.Provider>
);
}
export function useHashConnect () {
const value = React.useContext(HashConnectAPIContext);
return value;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment