Integrate wallet IFrame for payments #42
@ -15,3 +15,4 @@ VITE_PASSKEY_WALLET_RPID=
|
|||||||
VITE_TURNKEY_API_BASE_URL=
|
VITE_TURNKEY_API_BASE_URL=
|
||||||
|
|
||||||
VITE_LACONICD_CHAIN_ID=
|
VITE_LACONICD_CHAIN_ID=
|
||||||
|
VITE_IFRAME_ORIGIN_URL=
|
||||||
|
@ -0,0 +1,49 @@
|
|||||||
|
import { Select, Option } from '@snowballtools/material-tailwind-react-fork';
|
||||||
|
|
||||||
|
const AccountsDropdown = ({
|
||||||
|
accounts,
|
||||||
|
onAccountChange,
|
||||||
|
}: {
|
||||||
|
accounts: string[];
|
||||||
|
onAccountChange: (selectedAccount: string) => void;
|
||||||
|
}) => {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="p-6 bg-slate-100 dark:bg-overlay3 rounded-lg mb-6 shadow-md">
|
||||||
|
{!accounts.length ? (
|
||||||
|
<div className="text-center">
|
||||||
|
<p className="text-gray-700 dark:text-gray-300 mb-4">
|
||||||
|
No accounts found. Please visit{' '}
|
||||||
|
<a
|
||||||
|
href="https://store.laconic.com"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="text-blue-600 underline dark:text-blue-400"
|
||||||
|
>
|
||||||
|
store.laconic.com
|
||||||
|
</a>{' '}
|
||||||
|
to create a wallet.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div>
|
||||||
|
<Select
|
||||||
|
label="Select Account"
|
||||||
|
defaultValue={accounts[0]}
|
||||||
|
onChange={(value) => value && onAccountChange(value)}
|
||||||
|
className="dark:bg-overlay2 dark:text-foreground"
|
||||||
|
aria-label="Wallet Account Selector"
|
||||||
|
>
|
||||||
|
{accounts.map((account, index) => (
|
||||||
|
<Option key={index} value={account}>
|
||||||
|
{account}
|
||||||
|
</Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AccountsDropdown;
|
@ -20,10 +20,11 @@ import { Button } from '../../shared/Button';
|
|||||||
import { Input } from 'components/shared/Input';
|
import { Input } from 'components/shared/Input';
|
||||||
import { useToast } from 'components/shared/Toast';
|
import { useToast } from 'components/shared/Toast';
|
||||||
import { useGQLClient } from '../../../context/GQLClientContext';
|
import { useGQLClient } from '../../../context/GQLClientContext';
|
||||||
import IFrame from './IFrame';
|
import IFrameModal from './IFrameModal';
|
||||||
import EnvironmentVariablesForm from 'pages/org-slug/projects/id/settings/EnvironmentVariablesForm';
|
import EnvironmentVariablesForm from 'pages/org-slug/projects/id/settings/EnvironmentVariablesForm';
|
||||||
import { EnvironmentVariablesFormValues } from 'types/types';
|
import { EnvironmentVariablesFormValues } from 'types/types';
|
||||||
import { VITE_LACONICD_CHAIN_ID } from 'utils/constants';
|
import { VITE_LACONICD_CHAIN_ID, VITE_IFRAME_ORIGIN_URL } from 'utils/constants';
|
||||||
|
import AccountsDropdown from './AccountsDropdown';
|
||||||
|
|
||||||
type ConfigureDeploymentFormValues = {
|
type ConfigureDeploymentFormValues = {
|
||||||
option: string;
|
option: string;
|
||||||
@ -36,6 +37,7 @@ type ConfigureFormValues = ConfigureDeploymentFormValues &
|
|||||||
EnvironmentVariablesFormValues;
|
EnvironmentVariablesFormValues;
|
||||||
|
|
||||||
const DEFAULT_MAX_PRICE = '10000';
|
const DEFAULT_MAX_PRICE = '10000';
|
||||||
|
const TX_APPROVAL_TIMEOUT = 60000;
|
||||||
|
|
||||||
const Configure = () => {
|
const Configure = () => {
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
@ -175,7 +177,6 @@ const Configure = () => {
|
|||||||
const handleFormSubmit = useCallback(
|
const handleFormSubmit = useCallback(
|
||||||
async (createFormData: FieldValues) => {
|
async (createFormData: FieldValues) => {
|
||||||
try {
|
try {
|
||||||
setIsFrameVisible(true);
|
|
||||||
const deployerLrn = createFormData.lrn;
|
const deployerLrn = createFormData.lrn;
|
||||||
const deployer = deployers.find(
|
const deployer = deployers.find(
|
||||||
(deployer) => deployer.deployerLrn === deployerLrn,
|
(deployer) => deployer.deployerLrn === deployerLrn,
|
||||||
@ -213,6 +214,33 @@ const Configure = () => {
|
|||||||
senderAddress,
|
senderAddress,
|
||||||
amountToBePaid,
|
amountToBePaid,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (!txHash) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate transaction hash
|
||||||
|
const isTxHashValid = await verifyTx(
|
||||||
|
senderAddress,
|
||||||
|
txHash,
|
||||||
|
amount,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isTxHashValid) {
|
||||||
|
toast({
|
||||||
|
id: 'payment-successful',
|
||||||
|
title: 'Payment successful',
|
||||||
|
variant: 'success',
|
||||||
|
onDismiss: dismiss,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
toast({
|
||||||
|
id: 'invalid-tx-hash',
|
||||||
|
title: 'Transaction validation failed',
|
||||||
|
variant: 'error',
|
||||||
|
onDismiss: dismiss,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const environmentVariables = createFormData.variables.map(
|
const environmentVariables = createFormData.variables.map(
|
||||||
@ -300,48 +328,16 @@ const Configure = () => {
|
|||||||
setIsPaymentDone(false);
|
setIsPaymentDone(false);
|
||||||
setIsPaymentLoading(true);
|
setIsPaymentLoading(true);
|
||||||
|
|
||||||
toast({
|
|
||||||
id: 'sending-payment-request',
|
|
||||||
title: 'Check and approve payment request',
|
|
||||||
variant: 'loading',
|
|
||||||
onDismiss: dismiss,
|
|
||||||
});
|
|
||||||
|
|
||||||
await requestTx(senderAddress, snowballAddress, amount);
|
await requestTx(senderAddress, snowballAddress, amount);
|
||||||
|
|
||||||
const txHash = await new Promise<string>((resolve, reject) => {
|
const txHash = await new Promise<string>((resolve, reject) => {
|
||||||
const handleTxStatus = async (event: MessageEvent) => {
|
const handleTxStatus = async (event: MessageEvent) => {
|
||||||
if (event.origin !== 'http://localhost:3001') return;
|
if (event.origin !== VITE_IFRAME_ORIGIN_URL) return;
|
||||||
|
|
||||||
if (event.data.type === 'TRANSACTION_SUCCESS') {
|
if (event.data.type === 'TRANSACTION_SUCCESS') {
|
||||||
const txResponse = event.data.data;
|
const txResponse = event.data.data;
|
||||||
|
resolve(txResponse);
|
||||||
setIsFrameVisible(false);
|
setIsFrameVisible(false);
|
||||||
|
|
||||||
// Validate transaction hash
|
|
||||||
const isTxHashValid = await verifyTx(
|
|
||||||
senderAddress,
|
|
||||||
txResponse,
|
|
||||||
amount,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isTxHashValid) {
|
|
||||||
resolve(txResponse);
|
|
||||||
toast({
|
|
||||||
id: 'payment-successful',
|
|
||||||
title: 'Payment successful',
|
|
||||||
variant: 'success',
|
|
||||||
onDismiss: dismiss,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
reject(new Error('Invalid transaction hash'));
|
|
||||||
toast({
|
|
||||||
id: 'invalid-tx-hash',
|
|
||||||
title: 'Transaction validation failed',
|
|
||||||
variant: 'error',
|
|
||||||
onDismiss: dismiss,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else if (event.data.type === 'ERROR') {
|
} else if (event.data.type === 'ERROR') {
|
||||||
console.error('Error from wallet:', event.data.message);
|
console.error('Error from wallet:', event.data.message);
|
||||||
reject(new Error('Transaction failed'));
|
reject(new Error('Transaction failed'));
|
||||||
@ -362,7 +358,13 @@ const Configure = () => {
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
reject(new Error('Transaction timeout'));
|
reject(new Error('Transaction timeout'));
|
||||||
window.removeEventListener('message', handleTxStatus);
|
window.removeEventListener('message', handleTxStatus);
|
||||||
}, 60000);
|
toast({
|
||||||
|
id: 'transaction-timeout',
|
||||||
|
title: 'The transaction request timed out. Please try again',
|
||||||
|
variant: 'error',
|
||||||
|
onDismiss: dismiss,
|
||||||
|
});
|
||||||
|
}, TX_APPROVAL_TIMEOUT);
|
||||||
});
|
});
|
||||||
return txHash;
|
return txHash;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -391,8 +393,10 @@ const Configure = () => {
|
|||||||
toAddress: recipient,
|
toAddress: recipient,
|
||||||
amount,
|
amount,
|
||||||
},
|
},
|
||||||
'http://localhost:3001'
|
VITE_IFRAME_ORIGIN_URL
|
||||||
);
|
);
|
||||||
|
|
||||||
|
setIsFrameVisible(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -561,10 +565,9 @@ const Configure = () => {
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<IFrame
|
<AccountsDropdown accounts={accounts} onAccountChange={onAccountChange} />
|
||||||
accounts={accounts}
|
<IFrameModal
|
||||||
setAccounts={setAccounts}
|
setAccounts={setAccounts}
|
||||||
onAccountChange={onAccountChange}
|
|
||||||
isVisible={isFrameVisible}
|
isVisible={isFrameVisible}
|
||||||
toggleModal={toggleModal}
|
toggleModal={toggleModal}
|
||||||
/>
|
/>
|
||||||
|
@ -1,124 +0,0 @@
|
|||||||
import { useCallback, useEffect } from 'react';
|
|
||||||
|
|
||||||
import { Select, Option } from '@snowballtools/material-tailwind-react-fork';
|
|
||||||
import { Box, Modal } from '@mui/material';
|
|
||||||
|
|
||||||
import { VITE_LACONICD_CHAIN_ID } from 'utils/constants';
|
|
||||||
|
|
||||||
const IFrame = ({
|
|
||||||
accounts,
|
|
||||||
setAccounts,
|
|
||||||
onAccountChange,
|
|
||||||
isVisible,
|
|
||||||
toggleModal,
|
|
||||||
}: {
|
|
||||||
accounts: string[];
|
|
||||||
setAccounts: (accounts: string[]) => void;
|
|
||||||
onAccountChange: (selectedAccount: string) => void;
|
|
||||||
isVisible: boolean;
|
|
||||||
toggleModal: () => void;
|
|
||||||
}) => {
|
|
||||||
useEffect(() => {
|
|
||||||
const handleMessage = (event: MessageEvent) => {
|
|
||||||
// TODO: Use env for origin URL
|
|
||||||
if (event.origin !== 'http://localhost:3001') return;
|
|
||||||
|
|
||||||
if (event.data.type === 'WALLET_ACCOUNTS_DATA') {
|
|
||||||
setAccounts(event.data.data);
|
|
||||||
} else if (event.data.type === 'ERROR') {
|
|
||||||
console.error('Error from wallet:', event.data.message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener('message', handleMessage);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener('message', handleMessage);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const getDataFromWallet = useCallback(() => {
|
|
||||||
const iframe = document.getElementById('walletIframe') as HTMLIFrameElement;
|
|
||||||
|
|
||||||
if (!iframe.contentWindow) {
|
|
||||||
console.error('Iframe not found or not loaded');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
iframe.contentWindow.postMessage(
|
|
||||||
{
|
|
||||||
type: 'REQUEST_WALLET_ACCOUNTS',
|
|
||||||
chainId: VITE_LACONICD_CHAIN_ID,
|
|
||||||
},
|
|
||||||
'http://localhost:3001'
|
|
||||||
);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="p-6 bg-slate-100 dark:bg-overlay3 rounded-lg mb-6 shadow-md">
|
|
||||||
{!accounts.length ? (
|
|
||||||
<div className="text-center">
|
|
||||||
<p className="text-gray-700 dark:text-gray-300 mb-4">
|
|
||||||
No accounts found. Please visit{' '}
|
|
||||||
<a
|
|
||||||
href="https://store.laconic.com"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="text-blue-600 underline dark:text-blue-400"
|
|
||||||
>
|
|
||||||
store.laconic.com
|
|
||||||
</a>{' '}
|
|
||||||
to create a wallet.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div>
|
|
||||||
<Select
|
|
||||||
label="Select Account"
|
|
||||||
defaultValue={accounts[0]}
|
|
||||||
onChange={(value) => value && onAccountChange(value)}
|
|
||||||
className="dark:bg-overlay2 dark:text-foreground"
|
|
||||||
aria-label="Wallet Account Selector"
|
|
||||||
>
|
|
||||||
{accounts.map((account, index) => (
|
|
||||||
<Option key={index} value={account}>
|
|
||||||
{account}
|
|
||||||
</Option>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<Modal open={isVisible} onClose={toggleModal} keepMounted>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
position: 'absolute',
|
|
||||||
top: '50%',
|
|
||||||
left: '50%',
|
|
||||||
transform: 'translate(-50%, -50%)',
|
|
||||||
width: '90%',
|
|
||||||
maxWidth: '1200px',
|
|
||||||
height: '600px',
|
|
||||||
maxHeight: '80vh',
|
|
||||||
overflow: 'auto',
|
|
||||||
boxShadow: 24,
|
|
||||||
borderRadius: '8px',
|
|
||||||
outline: 'none',
|
|
||||||
bgcolor: 'background.paper',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<iframe
|
|
||||||
onLoad={getDataFromWallet}
|
|
||||||
id="walletIframe"
|
|
||||||
src="http://localhost:3001/WalletEmbed"
|
|
||||||
width="100%"
|
|
||||||
height="100%"
|
|
||||||
sandbox="allow-scripts allow-same-origin"
|
|
||||||
className="border rounded-md shadow-sm"
|
|
||||||
></iframe>
|
|
||||||
</Box>
|
|
||||||
</Modal>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default IFrame;
|
|
@ -0,0 +1,85 @@
|
|||||||
|
import { useCallback, useEffect } from 'react';
|
||||||
|
|
||||||
|
import { Box, Modal } from '@mui/material';
|
||||||
|
|
||||||
|
import { VITE_LACONICD_CHAIN_ID, VITE_IFRAME_ORIGIN_URL } from 'utils/constants';
|
||||||
|
|
||||||
|
const IFrameModal = ({
|
||||||
|
setAccounts,
|
||||||
|
isVisible,
|
||||||
|
toggleModal,
|
||||||
|
}: {
|
||||||
|
setAccounts: (accounts: string[]) => void;
|
||||||
|
isVisible: boolean;
|
||||||
|
toggleModal: () => void;
|
||||||
|
}) => {
|
||||||
|
useEffect(() => {
|
||||||
|
const handleMessage = (event: MessageEvent) => {
|
||||||
|
// TODO: Use env for origin URL
|
||||||
|
if (event.origin !== VITE_IFRAME_ORIGIN_URL) return;
|
||||||
|
|
||||||
|
if (event.data.type === 'WALLET_ACCOUNTS_DATA') {
|
||||||
|
setAccounts(event.data.data);
|
||||||
|
} else if (event.data.type === 'ERROR') {
|
||||||
|
console.error('Error from wallet:', event.data.message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('message', handleMessage);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('message', handleMessage);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const getDataFromWallet = useCallback(() => {
|
||||||
|
const iframe = document.getElementById('walletIframe') as HTMLIFrameElement;
|
||||||
|
|
||||||
|
if (!iframe.contentWindow) {
|
||||||
|
console.error('Iframe not found or not loaded');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
iframe.contentWindow.postMessage(
|
||||||
|
{
|
||||||
|
type: 'REQUEST_WALLET_ACCOUNTS',
|
||||||
|
chainId: VITE_LACONICD_CHAIN_ID,
|
||||||
|
},
|
||||||
|
VITE_IFRAME_ORIGIN_URL
|
||||||
|
);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal open={isVisible} onClose={toggleModal} keepMounted>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: '50%',
|
||||||
|
left: '50%',
|
||||||
|
transform: 'translate(-50%, -50%)',
|
||||||
|
width: '90%',
|
||||||
|
maxWidth: '1200px',
|
||||||
|
height: '600px',
|
||||||
|
maxHeight: '80vh',
|
||||||
|
overflow: 'auto',
|
||||||
|
boxShadow: 24,
|
||||||
|
borderRadius: '8px',
|
||||||
|
outline: 'none',
|
||||||
|
bgcolor: 'background.paper',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<iframe
|
||||||
|
onLoad={getDataFromWallet}
|
||||||
|
id="walletIframe"
|
||||||
|
src={`${VITE_IFRAME_ORIGIN_URL}/WalletEmbed`}
|
||||||
|
width="100%"
|
||||||
|
height="100%"
|
||||||
|
sandbox="allow-scripts allow-same-origin"
|
||||||
|
className="border rounded-md shadow-sm"
|
||||||
|
></iframe>
|
||||||
|
</Box>
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
export default IFrameModal;
|
@ -1,210 +0,0 @@
|
|||||||
import {
|
|
||||||
createContext,
|
|
||||||
useCallback,
|
|
||||||
useContext,
|
|
||||||
useEffect,
|
|
||||||
useRef,
|
|
||||||
useState,
|
|
||||||
} from 'react';
|
|
||||||
|
|
||||||
import SignClient from '@walletconnect/sign-client';
|
|
||||||
import { getSdkError } from '@walletconnect/utils';
|
|
||||||
import { SessionTypes } from '@walletconnect/types';
|
|
||||||
|
|
||||||
import { walletConnectModal } from '../utils/web3modal';
|
|
||||||
import {
|
|
||||||
VITE_LACONICD_CHAIN_ID,
|
|
||||||
VITE_WALLET_CONNECT_ID,
|
|
||||||
} from 'utils/constants';
|
|
||||||
|
|
||||||
interface ClientInterface {
|
|
||||||
signClient: SignClient | undefined;
|
|
||||||
session: SessionTypes.Struct | undefined;
|
|
||||||
loadingSession: boolean;
|
|
||||||
onConnect: () => Promise<void>;
|
|
||||||
onDisconnect: () => Promise<void>;
|
|
||||||
onSessionDelete: () => void;
|
|
||||||
accounts: { address: string }[];
|
|
||||||
}
|
|
||||||
|
|
||||||
const ClientContext = createContext({} as ClientInterface);
|
|
||||||
|
|
||||||
export const useWalletConnectClient = () => {
|
|
||||||
return useContext(ClientContext);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const WalletConnectClientProvider = ({
|
|
||||||
children,
|
|
||||||
}: {
|
|
||||||
children: JSX.Element;
|
|
||||||
}) => {
|
|
||||||
const [signClient, setSignClient] = useState<SignClient>();
|
|
||||||
const [session, setSession] = useState<SessionTypes.Struct>();
|
|
||||||
const [loadingSession, setLoadingSession] = useState(true);
|
|
||||||
const [accounts, setAccounts] = useState<{ address: string }[]>([]);
|
|
||||||
|
|
||||||
const isSignClientInitializing = useRef<boolean>(false);
|
|
||||||
|
|
||||||
const onSessionConnect = useCallback(async (session: SessionTypes.Struct) => {
|
|
||||||
setSession(session);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const subscribeToEvents = useCallback(
|
|
||||||
async (client: SignClient) => {
|
|
||||||
client.on('session_update', ({ topic, params }) => {
|
|
||||||
const { namespaces } = params;
|
|
||||||
const currentSession = client.session.get(topic);
|
|
||||||
const updatedSession = { ...currentSession, namespaces };
|
|
||||||
setSession(updatedSession);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[setSession],
|
|
||||||
);
|
|
||||||
|
|
||||||
const onConnect = async () => {
|
|
||||||
const proposalNamespace = {
|
|
||||||
cosmos: {
|
|
||||||
methods: ['cosmos_sendTokens'],
|
|
||||||
chains: [`cosmos:${VITE_LACONICD_CHAIN_ID}`],
|
|
||||||
events: [],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { uri, approval } = await signClient!.connect({
|
|
||||||
requiredNamespaces: proposalNamespace,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (uri) {
|
|
||||||
walletConnectModal.openModal({ uri });
|
|
||||||
const session = await approval();
|
|
||||||
onSessionConnect(session);
|
|
||||||
walletConnectModal.closeModal();
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onDisconnect = useCallback(async () => {
|
|
||||||
if (typeof signClient === 'undefined') {
|
|
||||||
throw new Error('WalletConnect is not initialized');
|
|
||||||
}
|
|
||||||
if (typeof session === 'undefined') {
|
|
||||||
throw new Error('Session is not connected');
|
|
||||||
}
|
|
||||||
|
|
||||||
await signClient.disconnect({
|
|
||||||
topic: session.topic,
|
|
||||||
reason: getSdkError('USER_DISCONNECTED'),
|
|
||||||
});
|
|
||||||
|
|
||||||
onSessionDelete();
|
|
||||||
}, [signClient, session]);
|
|
||||||
|
|
||||||
const onSessionDelete = () => {
|
|
||||||
setAccounts([]);
|
|
||||||
setSession(undefined);
|
|
||||||
};
|
|
||||||
|
|
||||||
const checkPersistedState = useCallback(
|
|
||||||
async (signClient: SignClient) => {
|
|
||||||
if (typeof signClient === 'undefined') {
|
|
||||||
throw new Error('WalletConnect is not initialized');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof session !== 'undefined') return;
|
|
||||||
if (signClient.session.length) {
|
|
||||||
const lastKeyIndex = signClient.session.keys.length - 1;
|
|
||||||
const previousSsession = signClient.session.get(
|
|
||||||
signClient.session.keys[lastKeyIndex],
|
|
||||||
);
|
|
||||||
|
|
||||||
await onSessionConnect(previousSsession);
|
|
||||||
return previousSsession;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[session, onSessionConnect],
|
|
||||||
);
|
|
||||||
|
|
||||||
const createClient = useCallback(async () => {
|
|
||||||
isSignClientInitializing.current = true;
|
|
||||||
try {
|
|
||||||
const signClient = await SignClient.init({
|
|
||||||
projectId: VITE_WALLET_CONNECT_ID,
|
|
||||||
metadata: {
|
|
||||||
name: 'Deploy App',
|
|
||||||
description: '',
|
|
||||||
url: window.location.href,
|
|
||||||
icons: ['https://avatars.githubusercontent.com/u/92608123'],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
setSignClient(signClient);
|
|
||||||
await checkPersistedState(signClient);
|
|
||||||
await subscribeToEvents(signClient);
|
|
||||||
setLoadingSession(false);
|
|
||||||
} catch (e) {
|
|
||||||
console.error('error in createClient', e);
|
|
||||||
}
|
|
||||||
isSignClientInitializing.current = false;
|
|
||||||
}, [setSignClient, checkPersistedState, subscribeToEvents]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!signClient && !isSignClientInitializing.current) {
|
|
||||||
createClient();
|
|
||||||
}
|
|
||||||
}, [signClient, createClient]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const populateAccounts = async () => {
|
|
||||||
if (!session) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!session.namespaces['cosmos']) {
|
|
||||||
console.log('Accounts for cosmos namespace not found');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const cosmosAddresses = session.namespaces['cosmos'].accounts;
|
|
||||||
|
|
||||||
const cosmosAccounts = cosmosAddresses.map((address) => ({
|
|
||||||
address,
|
|
||||||
}));
|
|
||||||
|
|
||||||
const allAccounts = cosmosAccounts;
|
|
||||||
|
|
||||||
setAccounts(allAccounts);
|
|
||||||
};
|
|
||||||
|
|
||||||
populateAccounts();
|
|
||||||
}, [session]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!signClient) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
signClient.on('session_delete', onSessionDelete);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
signClient.off('session_delete', onSessionDelete);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ClientContext.Provider
|
|
||||||
value={{
|
|
||||||
signClient,
|
|
||||||
onConnect,
|
|
||||||
onDisconnect,
|
|
||||||
onSessionDelete,
|
|
||||||
loadingSession,
|
|
||||||
session,
|
|
||||||
accounts,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</ClientContext.Provider>
|
|
||||||
);
|
|
||||||
};
|
|
@ -4,10 +4,9 @@ import assert from 'assert';
|
|||||||
import { GQLClient } from 'gql-client';
|
import { GQLClient } from 'gql-client';
|
||||||
|
|
||||||
import { ThemeProvider } from '@snowballtools/material-tailwind-react-fork';
|
import { ThemeProvider } from '@snowballtools/material-tailwind-react-fork';
|
||||||
|
|
||||||
import './index.css';
|
|
||||||
import '@fontsource/inter';
|
import '@fontsource/inter';
|
||||||
import '@fontsource-variable/jetbrains-mono';
|
import '@fontsource-variable/jetbrains-mono';
|
||||||
|
|
||||||
import App from './App';
|
import App from './App';
|
||||||
import reportWebVitals from './reportWebVitals';
|
import reportWebVitals from './reportWebVitals';
|
||||||
import { GQLClientProvider } from './context/GQLClientContext';
|
import { GQLClientProvider } from './context/GQLClientContext';
|
||||||
@ -16,7 +15,7 @@ import { Toaster } from 'components/shared/Toast';
|
|||||||
import { LogErrorBoundary } from 'utils/log-error';
|
import { LogErrorBoundary } from 'utils/log-error';
|
||||||
import { BASE_URL } from 'utils/constants';
|
import { BASE_URL } from 'utils/constants';
|
||||||
import Web3ModalProvider from './context/Web3Provider';
|
import Web3ModalProvider from './context/Web3Provider';
|
||||||
import { WalletConnectClientProvider } from 'context/WalletConnectContext';
|
import './index.css';
|
||||||
|
|
||||||
console.log(`v-0.0.9`);
|
console.log(`v-0.0.9`);
|
||||||
|
|
||||||
@ -32,16 +31,14 @@ const gqlClient = new GQLClient({ gqlEndpoint });
|
|||||||
root.render(
|
root.render(
|
||||||
<LogErrorBoundary>
|
<LogErrorBoundary>
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<WalletConnectClientProvider>
|
<ThemeProvider>
|
||||||
<ThemeProvider>
|
<Web3ModalProvider>
|
||||||
<Web3ModalProvider>
|
<GQLClientProvider client={gqlClient}>
|
||||||
<GQLClientProvider client={gqlClient}>
|
<App />
|
||||||
<App />
|
<Toaster />
|
||||||
<Toaster />
|
</GQLClientProvider>
|
||||||
</GQLClientProvider>
|
</Web3ModalProvider>
|
||||||
</Web3ModalProvider>
|
</ThemeProvider>
|
||||||
</ThemeProvider>
|
|
||||||
</WalletConnectClientProvider>
|
|
||||||
</React.StrictMode>
|
</React.StrictMode>
|
||||||
</LogErrorBoundary>,
|
</LogErrorBoundary>,
|
||||||
);
|
);
|
||||||
|
@ -12,3 +12,4 @@ export const VITE_WALLET_CONNECT_ID = import.meta.env.VITE_WALLET_CONNECT_ID;
|
|||||||
export const VITE_BUGSNAG_API_KEY = import.meta.env.VITE_BUGSNAG_API_KEY;
|
export const VITE_BUGSNAG_API_KEY = import.meta.env.VITE_BUGSNAG_API_KEY;
|
||||||
export const VITE_LIT_RELAY_API_KEY = import.meta.env.VITE_LIT_RELAY_API_KEY;
|
export const VITE_LIT_RELAY_API_KEY = import.meta.env.VITE_LIT_RELAY_API_KEY;
|
||||||
export const VITE_LACONICD_CHAIN_ID = import.meta.env.VITE_LACONICD_CHAIN_ID;
|
export const VITE_LACONICD_CHAIN_ID = import.meta.env.VITE_LACONICD_CHAIN_ID;
|
||||||
|
export const VITE_IFRAME_ORIGIN_URL = import.meta.env.VITE_IFRAME_ORIGIN_URL;
|
||||||
|
Loading…
Reference in New Issue
Block a user