@pushchain/ui-kit
Version:
Push Chain is a true universal L1 that is 100% EVM compatible. It allows developers to deploy once and make their apps instantly compatible with users from all other L1s (Ethereum, Solana, etc) with zero on-chain code change.
647 lines • 27.5 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.WalletContextProvider = exports.WalletContext = void 0;
const tslib_1 = require("tslib");
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = tslib_1.__importStar(require("react"));
const core_1 = require("@pushchain/core");
const progress_hook_types_1 = require("@pushchain/core/src/lib/progress-hook/progress-hook.types");
const types_1 = require("../types");
const constants_1 = require("../constants");
const WalletProviderRegistry_1 = require("../providers/walletProviders/WalletProviderRegistry");
const PushWalletToast_1 = require("../components/PushWalletToast");
const LoginModal_1 = require("../components/LoginModal");
const WalletContextMap_1 = require("./WalletContextMap");
const useWaapAuth_1 = require("../providers/waap/useWaapAuth");
const waapProvider_1 = require("../providers/waap/waapProvider");
const phantomMobile_1 = require("../providers/walletProviders/solana/phantomMobile");
const walletAuthorization_1 = require("./walletAuthorization");
exports.WalletContext = (0, react_1.createContext)(null);
const WalletContextProvider = ({ children, config, app, themeMode = constants_1.PushUI.CONSTANTS.THEME.DARK, themeOverrides, }) => {
const [universalAccount, setUniversalAccount] = (0, react_1.useState)(null);
const iframeRef = (0, react_1.useRef)(null);
const [isWalletVisible, setWalletVisibility] = (0, react_1.useState)(false); // to display the iframe as connect button is clicked
const [isWalletMinimised, setMinimiseWallet] = (0, react_1.useState)(false); // to display/hide minimized wallet modal
const [isIframeLoading, setIframeLoading] = (0, react_1.useState)(true);
const isIframeLoadingRef = (0, react_1.useRef)(isIframeLoading);
const [isReadOnly, setIsReadOnly] = (0, react_1.useState)(false);
const [connectionStatus, setConnectionStatus] = (0, react_1.useState)(types_1.ConnectionStatus.NOT_CONNECTED);
const [externalWallet, setExternalWallet] = (0, react_1.useState)(null); // to connect with external wallet
const [progress, setProgress] = (0, react_1.useState)(null);
const upgradeResolverRef = (0, react_1.useRef)(null);
const pendingExternalWalletStatusRef = (0, react_1.useRef)(null);
const pushChainClientRef = (0, react_1.useRef)(null);
const setPushChainClient = (client) => {
pushChainClientRef.current = client;
};
const [modalAppData, setModalAppData] = (0, react_1.useState)(app
? {
title: app?.title,
logoURL: app?.logoUrl,
description: app?.description,
}
: undefined);
const [walletAppData, setWalletAppData] = (0, react_1.useState)(app
? {
title: app?.title,
logoURL: app?.logoUrl,
description: app?.description,
}
: undefined);
const [activeTriggerId, setActiveTriggerId] = (0, react_1.useState)();
const { loginWithWaapSocial } = (0, useWaapAuth_1.useWaapAuth)();
const toggleButtonRefs = react_1.default.useRef({});
const updateModalAppData = (newData) => {
setModalAppData((prevData) => ({
...prevData,
...newData,
}));
};
const updateWalletAppData = (newData) => {
setWalletAppData((prevData) => ({
...prevData,
...newData,
}));
};
const handleConnectToPushWallet = () => {
setWalletVisibility(true);
setConnectionStatus(types_1.ConnectionStatus.CONNECTING);
};
// sending wallet config to the Push wallet
const sendWalletConfig = () => {
const walletConfig = {
loginDefaults: config.login,
themeMode,
appMetadata: walletAppData,
themeOverrides: themeOverrides || {},
};
sendMessageToPushWallet({
type: constants_1.APP_TO_WALLET_ACTION.WALLET_CONFIG,
data: {
...walletConfig,
},
});
};
const handleUserLogOutEvent = () => {
if (externalWallet) {
const providerReceived = WalletProviderRegistry_1.walletRegistry.getProvider(externalWallet.providerName);
providerReceived?.disconnect();
}
setConnectionStatus(types_1.ConnectionStatus.NOT_CONNECTED);
setUniversalAccount(null);
setMinimiseWallet(false);
setWalletVisibility(false);
setIframeLoading(true);
setExternalWallet(null);
pendingExternalWalletStatusRef.current = null;
setIsReadOnly(false);
localStorage.removeItem(`walletInfo_${config?.uid || 'default'}`);
document.body.style.overflow = '';
};
// sending events to wallet from dapp
const sendMessageToPushWallet = (message) => {
if (iframeRef?.current?.contentWindow) {
try {
iframeRef.current.contentWindow.postMessage(message, constants_1.WALLET_CONFIG_URL[config.network]);
}
catch (error) {
console.error('Error sending message to push wallet tab:', error);
}
}
};
const sendExternalWalletConnectionStatus = (connectedWallet) => {
if (isIframeLoadingRef.current || !iframeRef?.current?.contentWindow) {
pendingExternalWalletStatusRef.current = connectedWallet;
return;
}
sendMessageToPushWallet({
type: constants_1.APP_TO_WALLET_ACTION.CONNECTION_STATUS,
data: {
status: 'successful',
...connectedWallet,
},
});
};
// response when the wallet sends logged in action
const handleIsLoggedInAction = () => {
handleNewConnectionRequest();
// setExternalWallet(null);
};
// sending a new connection request as soon as wallet gets connected
const handleNewConnectionRequest = () => {
setConnectionStatus(types_1.ConnectionStatus.AUTHENTICATING);
sendMessageToPushWallet({
type: constants_1.APP_TO_WALLET_ACTION.NEW_CONNECTION_REQUEST,
});
};
const handleAppConnectionSuccess = (response) => {
setConnectionStatus(types_1.ConnectionStatus.CONNECTED);
setMinimiseWallet(true);
// setIsReadOnly(false);
if (response.account) {
setUniversalAccount(response.account);
}
localStorage.setItem(`walletInfo_${config?.uid || 'default'}`, JSON.stringify({
account: response.account,
uid: config?.uid || 'default',
}));
};
const handleAppConnectionRejection = () => {
setConnectionStatus(types_1.ConnectionStatus.RETRY);
setUniversalAccount(null);
};
// Connect external wallet
const handleExternalWalletConnection = async (data) => {
try {
const providerReceived = WalletProviderRegistry_1.walletRegistry.getProvider(data.provider);
if (!providerReceived) {
return;
}
const walletInfo = await providerReceived.connect(data.chain);
setConnectionStatus(types_1.ConnectionStatus.CONNECTED);
setMinimiseWallet(true);
const result = core_1.PushChain.utils.account.fromChainAgnostic(walletInfo.caipAddress);
setUniversalAccount(result);
const connectedWallet = {
address: walletInfo.caipAddress,
providerName: data.provider,
chainType: data.chain,
};
localStorage.setItem(`walletInfo_${config?.uid || 'default'}`, JSON.stringify({
wallet: connectedWallet,
uid: config?.uid || 'default',
}));
setExternalWallet(connectedWallet);
sendExternalWalletConnectionStatus(connectedWallet);
}
catch (error) {
console.log('Failed to connect to provider', error);
sendMessageToPushWallet({
type: constants_1.APP_TO_WALLET_ACTION.CONNECTION_STATUS,
data: {
status: 'rejected',
},
});
throw new Error('Failed to connect to provider');
}
};
// handles external wallet signature request
const handleExternalWalletSignRequest = async (data) => {
if (!externalWallet) {
throw new Error('No External wallet connected');
}
try {
const providerReceived = WalletProviderRegistry_1.walletRegistry.getProvider(externalWallet.providerName);
if (!providerReceived) {
throw new Error('Provider not found');
}
const signature = await providerReceived.signMessage(data);
return signature;
}
catch (error) {
console.log('Error in generating signature', error);
throw new Error('Signature request failed');
}
};
const handleExternalWalletSignTransactionRequest = async (data) => {
if (!externalWallet) {
throw new Error('No External wallet connected');
}
try {
const providerReceived = WalletProviderRegistry_1.walletRegistry.getProvider(externalWallet.providerName);
if (!providerReceived) {
throw new Error('Provider not found');
}
const signature = await providerReceived.signAndSendTransaction(data);
return signature;
}
catch (error) {
console.log('Error in generating signature', error);
throw new Error('Signature request failed');
}
};
const handleExternalWalletSignTypedDataRequest = async (data) => {
if (!externalWallet) {
throw new Error('No External wallet connected');
}
try {
const providerReceived = WalletProviderRegistry_1.walletRegistry.getProvider(externalWallet.providerName);
if (!providerReceived) {
throw new Error('Provider not found');
}
const signature = await providerReceived.signTypedData(data);
return signature;
}
catch (error) {
console.log('Error in generating signature', error);
throw new Error('Signature request failed');
}
};
const handleSocialConnection = async () => {
const result = await loginWithWaapSocial();
if (!result)
return;
console.log(result);
const w = await core_1.PushChain.utils.account.convertExecutorToOriginAccount(result.address);
if (!w.account)
return;
// setUniversalAccount(w.account);
// localStorage.setItem(
// `walletInfo_${config?.uid || 'default'}`,
// JSON.stringify({
// account: w.account,
// uid: config?.uid || 'default'
// })
// );
sendMessageToPushWallet({
type: constants_1.APP_TO_WALLET_ACTION.SOCIAL_CONNECTION_STATUS,
data: {
account: w.account,
},
});
};
const handleSocialSignAndSendMessage = async (message) => {
try {
const signature = await (0, waapProvider_1.waapSignMessage)(message);
sendMessageToPushWallet({
type: constants_1.APP_TO_WALLET_ACTION.SIGN_MESSAGE,
data: { signature },
});
}
catch (error) {
sendMessageToPushWallet({
type: constants_1.APP_TO_WALLET_ACTION.ERROR,
data: {
error: error,
},
});
}
};
const handleSocialSignAndSendTransaction = async (txn) => {
try {
const signature = await (0, waapProvider_1.waapSignAndSendTransaction)(txn);
sendMessageToPushWallet({
type: constants_1.WALLET_TO_APP_ACTION.SIGN_TRANSACTION,
data: { signature },
});
}
catch (error) {
sendMessageToPushWallet({
type: constants_1.WALLET_TO_APP_ACTION.ERROR,
data: {
error: error,
},
});
}
};
const handleSocialSignTypedData = async (typedData) => {
try {
const signature = await (0, waapProvider_1.waapSignTypedData)(typedData);
sendMessageToPushWallet({
type: constants_1.WALLET_TO_APP_ACTION.SIGN_TYPED_DATA,
data: { signature },
});
}
catch (error) {
sendMessageToPushWallet({
type: constants_1.WALLET_TO_APP_ACTION.ERROR,
data: {
error: error,
},
});
}
};
const handleSignMessage = async (data) => {
let signature;
if (externalWallet) {
signature = await handleExternalWalletSignRequest(data);
}
else {
signature = await (0, waapProvider_1.waapSignMessage)(data);
}
return signature;
};
const handleSignAndSendTransaction = async (data) => {
let signature;
if (externalWallet) {
signature = await handleExternalWalletSignTransactionRequest(data);
}
else {
signature = await (0, waapProvider_1.waapSignAndSendTransaction)(data);
}
return signature;
};
const handleSignTypedData = async (data) => {
let signature;
if (externalWallet) {
signature = await handleExternalWalletSignTypedDataRequest(data);
}
else {
signature = await (0, waapProvider_1.waapSignTypedData)(data);
}
return signature;
};
const handleSignAuthorization = async (params) => {
return (0, walletAuthorization_1.signAuthorizationForWalletConnection)(externalWallet, params, WalletProviderRegistry_1.walletRegistry, waapProvider_1.waapSignAuthorization);
};
const getAuthWindowConfig = () => {
// Calculate the screen width and height
const screenWidth = window.screen.width;
const screenHeight = window.screen.height;
const width = 500;
const height = 600;
// Calculate the position to center the window
const left = (screenWidth - width) / 2;
const top = (screenHeight - height) / 2;
// Open a new window with the calculated position
const windowFeatures = `width=${width},height=${height},left=${left},top=${top},resizable,scrollbars`;
return windowFeatures;
};
const requestPushWalletConnection = () => {
setMinimiseWallet(false);
sendMessageToPushWallet({
type: constants_1.APP_TO_WALLET_ACTION.RECONNECT_WALLET,
});
// Wait for a response from the Push Wallet iframe
return new Promise((resolve, reject) => {
const handleMessage = (event) => {
if (event.data.type === constants_1.WALLET_TO_APP_ACTION.APP_CONNECTION_SUCCESS) {
window.removeEventListener('message', handleMessage);
if (event.data.error) {
reject(new Error(event.data.error));
}
else {
resolve(event.data);
}
}
if (event.data.type === constants_1.WALLET_TO_APP_ACTION.APP_CONNECTION_CANCELLED) {
window.removeEventListener('message', handleMessage);
reject(new Error('Push Wallet connection failed'));
setMinimiseWallet(true);
}
};
window.addEventListener('message', handleMessage);
setTimeout(() => {
window.removeEventListener('message', handleMessage);
reject(new Error('Push Wallet connection timed out'));
setMinimiseWallet(true);
}, 100000);
});
};
const checkAndShowUpgradeIfNeeded = async (pushChainClient) => {
setPushChainClient(pushChainClient);
if (!pushChainClient) {
return false;
}
try {
await pushChainClient.accountStatusReady;
if (!pushChainClient.isReadMode &&
pushChainClient.accountStatus.uea.loaded &&
pushChainClient.accountStatus.uea.requiresUpgrade) {
setMinimiseWallet(false);
sendMessageToPushWallet({
type: constants_1.APP_TO_WALLET_ACTION.SHOW_UPGRADE_DRAWER,
data: {
currentVersion: pushChainClient.accountStatus.uea.version,
newVersion: pushChainClient.accountStatus.uea.minRequiredVersion,
},
});
// Wait for upgrade button click from iframe
return new Promise((resolve, reject) => {
upgradeResolverRef.current = {
success: () => {
upgradeResolverRef.current = null;
resolve(true);
},
error: (error) => {
upgradeResolverRef.current = null;
resolve(false);
},
};
});
}
return true;
}
catch (error) {
console.error('Error checking upgrade status:', error);
return false;
}
};
const handleUpgradeAccountSuccess = () => {
setMinimiseWallet(true);
// Resolve any pending upgrade promise
if (upgradeResolverRef.current?.success) {
upgradeResolverRef.current.success();
upgradeResolverRef.current = null;
}
};
const handleUpgradeAccountError = (error) => {
setMinimiseWallet(true);
// Reject any pending upgrade promise
if (upgradeResolverRef.current?.error) {
upgradeResolverRef.current.error(error instanceof Error ? error : new Error('Upgrade failed'));
upgradeResolverRef.current = null;
}
};
const handleUpgradeAccount = async () => {
const pushChainClient = pushChainClientRef.current;
if (!pushChainClient) {
throw new Error('PushChain client not initialized');
}
try {
await pushChainClient.upgradeAccount({
progressHook: (progress) => {
setProgress(progress);
if (progress.id === progress_hook_types_1.PROGRESS_HOOK.UEA_MIG_9901) {
setTimeout(() => setProgress(null), 10000);
}
},
});
// Send success response to iframe
sendMessageToPushWallet({
type: constants_1.APP_TO_WALLET_ACTION.UPGRADE_ACCOUNT_RESPONSE,
data: { success: true },
});
handleUpgradeAccountSuccess();
}
catch (error) {
console.error('Error upgrading account:', error);
// Send error response to iframe
sendMessageToPushWallet({
type: constants_1.APP_TO_WALLET_ACTION.UPGRADE_ACCOUNT_RESPONSE,
data: {
success: false,
error: error instanceof Error ? error.message : 'Upgrade failed',
},
});
// Reject the upgrade promise
if (upgradeResolverRef.current?.error) {
upgradeResolverRef.current.error(error instanceof Error ? error : new Error('Upgrade failed'));
}
}
};
(0, react_1.useEffect)(() => {
isIframeLoadingRef.current = isIframeLoading;
}, [isIframeLoading]);
(0, react_1.useEffect)(() => {
if ((0, phantomMobile_1.hasPhantomMobileConnectRequest)())
return;
const walletInfo = localStorage.getItem(`walletInfo_${config?.uid || 'default'}`);
const walletData = walletInfo ? JSON.parse(walletInfo) : null;
if (!walletData)
return;
if (walletData.uid !== config?.uid)
return;
if (walletData?.wallet && walletData.wallet?.providerName) {
setUniversalAccount(core_1.PushChain.utils.account.fromChainAgnostic(walletData.wallet.address));
setExternalWallet(walletData.wallet);
}
else {
setUniversalAccount(walletData.account);
}
setIsReadOnly(true);
setMinimiseWallet(true);
setWalletVisibility(true);
setConnectionStatus(types_1.ConnectionStatus.CONNECTED);
}, []);
(0, react_1.useEffect)(() => {
const requestedChain = (0, phantomMobile_1.consumePhantomMobileConnectRequest)();
if (!requestedChain)
return;
setWalletVisibility(true);
setConnectionStatus(types_1.ConnectionStatus.CONNECTING);
setIsReadOnly(false);
handleExternalWalletConnection({
chain: requestedChain,
provider: phantomMobile_1.PHANTOM_PROVIDER_NAME,
}).catch((error) => {
console.log('Failed to resume Phantom mobile connection', error);
setConnectionStatus(types_1.ConnectionStatus.RETRY);
});
}, []);
(0, react_1.useEffect)(() => {
if (isIframeLoading || !pendingExternalWalletStatusRef.current)
return;
const connectedWallet = pendingExternalWalletStatusRef.current;
pendingExternalWalletStatusRef.current = null;
sendMessageToPushWallet({
type: constants_1.APP_TO_WALLET_ACTION.CONNECTION_STATUS,
data: {
status: 'successful',
...connectedWallet,
},
});
}, [isIframeLoading]);
(0, react_1.useEffect)(() => {
if (isIframeLoading)
return;
if (!isReadOnly)
return;
if (externalWallet) {
sendMessageToPushWallet({
type: constants_1.APP_TO_WALLET_ACTION.READ_ONLY_CONNECTION_STATUS,
data: {
status: 'successful',
...externalWallet,
},
});
}
else if (universalAccount) {
sendMessageToPushWallet({
type: constants_1.APP_TO_WALLET_ACTION.READ_ONLY_CONNECTION_STATUS,
data: {
status: 'successful',
...universalAccount,
},
});
}
}, [isIframeLoading, externalWallet]);
(0, react_1.useEffect)(() => {
const messageHandler = (event) => {
if (iframeRef.current?.contentWindow !== event.source)
return;
switch (event.data.type) {
case constants_1.WALLET_TO_APP_ACTION.CONNECT_EXTERNAL_WALLET:
handleExternalWalletConnection(event.data.data);
break;
case constants_1.WALLET_TO_APP_ACTION.CONNECT_SOCIAL:
handleSocialConnection();
break;
case constants_1.WALLET_TO_APP_ACTION.IS_LOGGED_IN:
handleIsLoggedInAction();
break;
case constants_1.WALLET_TO_APP_ACTION.APP_CONNECTION_SUCCESS:
handleAppConnectionSuccess(event.data.data);
break;
case constants_1.WALLET_TO_APP_ACTION.APP_CONNECTION_REJECTED:
handleAppConnectionRejection();
break;
case constants_1.APP_TO_WALLET_ACTION.SIGN_MESSAGE:
handleSocialSignAndSendMessage(event.data.data);
break;
case constants_1.APP_TO_WALLET_ACTION.SIGN_TRANSACTION:
handleSocialSignAndSendTransaction(event.data.data);
break;
case constants_1.APP_TO_WALLET_ACTION.SIGN_TYPED_DATA:
handleSocialSignTypedData(event.data.data);
break;
case constants_1.WALLET_TO_APP_ACTION.IS_LOGGED_OUT:
handleUserLogOutEvent();
break;
case constants_1.WALLET_TO_APP_ACTION.CLOSE_IFRAME:
if (universalAccount)
setMinimiseWallet(true);
else
handleUserLogOutEvent();
break;
case constants_1.WALLET_TO_APP_ACTION.UPGRADE_ACCOUNT_REQUEST:
handleUpgradeAccount();
break;
case constants_1.WALLET_TO_APP_ACTION.UPGRADE_ACCOUNT_SUCCESS:
handleUpgradeAccountSuccess();
break;
case constants_1.WALLET_TO_APP_ACTION.UPGRADE_ACCOUNT_ERROR:
handleUpgradeAccountError();
break;
default:
console.warn('Unknown message type:', event.data.type);
}
};
window.addEventListener('message', messageHandler);
return () => window.removeEventListener('message', messageHandler);
}, [universalAccount]);
const WalletContext = (0, WalletContextMap_1.getWalletContext)(config?.uid || 'default');
return ((0, jsx_runtime_1.jsxs)(WalletContext.Provider, { value: {
app,
config,
connectionStatus,
universalAccount,
isWalletMinimised,
modalAppData,
themeMode,
themeOverrides: {},
updateModalAppData,
walletAppData,
updateWalletAppData,
setMinimiseWallet,
handleConnectToPushWallet,
handleUserLogOutEvent,
handleSignMessage,
handleSignAndSendTransaction,
handleSignTypedData,
handleSignAuthorization,
progress,
setProgress,
isReadOnly,
setIsReadOnly,
handleExternalWalletConnection,
requestPushWalletConnection,
checkAndShowUpgradeIfNeeded,
activeTriggerId,
setActiveTriggerId,
toggleButtonRefs,
connectionType: externalWallet ? 'external' : 'social',
}, children: [(0, jsx_runtime_1.jsx)(LoginModal_1.LoginModal, { iframeRef: iframeRef, themeMode: themeMode, modalAppData: modalAppData, isWalletVisible: isWalletVisible, isIframeLoading: isIframeLoading, setIframeLoading: setIframeLoading, sendWalletConfig: sendWalletConfig, config: config, universalAccount: universalAccount, isWalletMinimised: isWalletMinimised, setMinimiseWallet: setMinimiseWallet, handleUserLogOutEvent: handleUserLogOutEvent, sendMessageToPushWallet: sendMessageToPushWallet, isReadOnly: isReadOnly, toggleButtonRefs: toggleButtonRefs, activeTriggerId: activeTriggerId }), progress && ((0, jsx_runtime_1.jsx)(PushWalletToast_1.PushWalletToast, { progress: progress, setProgress: setProgress, initialChain: universalAccount?.chain, toastPosition: config?.toast?.position, hidden: config?.toast?.hidden })), children] }));
};
exports.WalletContextProvider = WalletContextProvider;
//# sourceMappingURL=WalletContext.js.map