Implement hooks for adding custom network to wallet
This commit is contained in:
parent
cda6ebec30
commit
704a872eab
11
packages/frontend/public/network.json
Normal file
11
packages/frontend/public/network.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"chainId": "laconic-testnet-2",
|
||||||
|
"networkName": "laconicd testnet-2",
|
||||||
|
"namespace": "cosmos",
|
||||||
|
"rpcUrl": "https://laconicd-sapo.laconic.com",
|
||||||
|
"blockExplorerUrl": "",
|
||||||
|
"nativeDenom": "alnt",
|
||||||
|
"addressPrefix": "laconic",
|
||||||
|
"coinType": 118,
|
||||||
|
"gasPrice": 0.001
|
||||||
|
}
|
@ -6,6 +6,8 @@ import {
|
|||||||
VITE_LACONICD_CHAIN_ID,
|
VITE_LACONICD_CHAIN_ID,
|
||||||
VITE_WALLET_IFRAME_URL,
|
VITE_WALLET_IFRAME_URL,
|
||||||
} from 'utils/constants';
|
} from 'utils/constants';
|
||||||
|
import { REQUEST_WALLET_ACCOUNTS, WALLET_ACCOUNTS_DATA } from '../../../constants';
|
||||||
|
import { useAddNetwork } from '../../../hooks/useAddNetwork';
|
||||||
|
|
||||||
const ApproveTransactionModal = ({
|
const ApproveTransactionModal = ({
|
||||||
setAccount,
|
setAccount,
|
||||||
@ -16,11 +18,13 @@ const ApproveTransactionModal = ({
|
|||||||
setIsDataReceived: (isReceived: boolean) => void;
|
setIsDataReceived: (isReceived: boolean) => void;
|
||||||
isVisible: boolean;
|
isVisible: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
|
const { setIframe, isNetworkAvailable } = useAddNetwork();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleMessage = (event: MessageEvent) => {
|
const handleMessage = (event: MessageEvent) => {
|
||||||
if (event.origin !== VITE_WALLET_IFRAME_URL) return;
|
if (event.origin !== VITE_WALLET_IFRAME_URL) return;
|
||||||
|
|
||||||
if (event.data.type === 'WALLET_ACCOUNTS_DATA') {
|
if (event.data.type === WALLET_ACCOUNTS_DATA) {
|
||||||
setIsDataReceived(true);
|
setIsDataReceived(true);
|
||||||
|
|
||||||
if (event.data.data.length === 0) {
|
if (event.data.data.length === 0) {
|
||||||
@ -53,13 +57,17 @@ const ApproveTransactionModal = ({
|
|||||||
|
|
||||||
iframe.contentWindow.postMessage(
|
iframe.contentWindow.postMessage(
|
||||||
{
|
{
|
||||||
type: 'REQUEST_WALLET_ACCOUNTS',
|
type: REQUEST_WALLET_ACCOUNTS,
|
||||||
chainId: VITE_LACONICD_CHAIN_ID,
|
chainId: VITE_LACONICD_CHAIN_ID,
|
||||||
},
|
},
|
||||||
VITE_WALLET_IFRAME_URL,
|
VITE_WALLET_IFRAME_URL,
|
||||||
);
|
);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getDataFromWallet()
|
||||||
|
}, [isNetworkAvailable, getDataFromWallet])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal open={isVisible} disableEscapeKeyDown keepMounted>
|
<Modal open={isVisible} disableEscapeKeyDown keepMounted>
|
||||||
<Box
|
<Box
|
||||||
@ -80,7 +88,7 @@ const ApproveTransactionModal = ({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<iframe
|
<iframe
|
||||||
onLoad={getDataFromWallet}
|
onLoad={(event) => setIframe(event.target as HTMLIFrameElement)}
|
||||||
id="walletIframe"
|
id="walletIframe"
|
||||||
src={`${VITE_WALLET_IFRAME_URL}/wallet-embed`}
|
src={`${VITE_WALLET_IFRAME_URL}/wallet-embed`}
|
||||||
width="100%"
|
width="100%"
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
import { Modal } from '@mui/material';
|
import { Modal } from '@mui/material';
|
||||||
|
|
||||||
import { VITE_WALLET_IFRAME_URL } from 'utils/constants';
|
import { VITE_WALLET_IFRAME_URL } from 'utils/constants';
|
||||||
import useCheckBalance from '../../../hooks/useCheckBalance';
|
import useCheckBalance from '../../../hooks/useCheckBalance';
|
||||||
|
import { useAddNetwork } from '../../../hooks/useAddNetwork';
|
||||||
|
|
||||||
const CHECK_BALANCE_INTERVAL = 5000;
|
const CHECK_BALANCE_INTERVAL = 5000;
|
||||||
const IFRAME_ID = 'checkBalanceIframe';
|
const IFRAME_ID = 'checkBalanceIframe';
|
||||||
@ -22,20 +23,19 @@ const CheckBalanceIframe = ({
|
|||||||
IFRAME_ID,
|
IFRAME_ID,
|
||||||
);
|
);
|
||||||
|
|
||||||
const [isLoaded, setIsLoaded] = useState(false);
|
const { isNetworkAvailable, setIframe } = useAddNetwork();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isLoaded) {
|
if (!isNetworkAvailable || isBalanceSufficient) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
checkBalance();
|
checkBalance();
|
||||||
}, [amount, checkBalance, isLoaded]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
if (!isPollingEnabled) {
|
||||||
if (!isPollingEnabled || !isLoaded || isBalanceSufficient) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
checkBalance();
|
checkBalance();
|
||||||
}, CHECK_BALANCE_INTERVAL);
|
}, CHECK_BALANCE_INTERVAL);
|
||||||
@ -43,7 +43,7 @@ const CheckBalanceIframe = ({
|
|||||||
return () => {
|
return () => {
|
||||||
clearInterval(interval);
|
clearInterval(interval);
|
||||||
};
|
};
|
||||||
}, [isBalanceSufficient, isPollingEnabled, checkBalance, isLoaded]);
|
}, [isBalanceSufficient, isPollingEnabled, checkBalance, isNetworkAvailable]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
onBalanceChange(isBalanceSufficient);
|
onBalanceChange(isBalanceSufficient);
|
||||||
@ -52,7 +52,7 @@ const CheckBalanceIframe = ({
|
|||||||
return (
|
return (
|
||||||
<Modal open={false} disableEscapeKeyDown keepMounted>
|
<Modal open={false} disableEscapeKeyDown keepMounted>
|
||||||
<iframe
|
<iframe
|
||||||
onLoad={() => setIsLoaded(true)}
|
onLoad={(event) => setIframe(event.target as HTMLIFrameElement)}
|
||||||
id={IFRAME_ID}
|
id={IFRAME_ID}
|
||||||
src={VITE_WALLET_IFRAME_URL}
|
src={VITE_WALLET_IFRAME_URL}
|
||||||
width="100%"
|
width="100%"
|
||||||
|
@ -6,6 +6,7 @@ import { useNavigate } from 'react-router-dom';
|
|||||||
import { Box, Modal } from '@mui/material';
|
import { Box, Modal } from '@mui/material';
|
||||||
|
|
||||||
import { BASE_URL, VITE_WALLET_IFRAME_URL } from 'utils/constants';
|
import { BASE_URL, VITE_WALLET_IFRAME_URL } from 'utils/constants';
|
||||||
|
import { REQUEST_CREATE_OR_GET_ACCOUNTS, WALLET_ACCOUNTS_DATA } from '../../../constants';
|
||||||
|
|
||||||
const axiosInstance = axios.create({
|
const axiosInstance = axios.create({
|
||||||
baseURL: BASE_URL,
|
baseURL: BASE_URL,
|
||||||
@ -91,7 +92,7 @@ const AutoSignInIFrameModal = () => {
|
|||||||
const handleAccountsDataResponse = async (event: MessageEvent) => {
|
const handleAccountsDataResponse = async (event: MessageEvent) => {
|
||||||
if (event.origin !== VITE_WALLET_IFRAME_URL) return;
|
if (event.origin !== VITE_WALLET_IFRAME_URL) return;
|
||||||
|
|
||||||
if (event.data.type === 'WALLET_ACCOUNTS_DATA') {
|
if (event.data.type === WALLET_ACCOUNTS_DATA) {
|
||||||
setAccountAddress(event.data.data[0]);
|
setAccountAddress(event.data.data[0]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -115,7 +116,7 @@ const AutoSignInIFrameModal = () => {
|
|||||||
|
|
||||||
iframe.contentWindow.postMessage(
|
iframe.contentWindow.postMessage(
|
||||||
{
|
{
|
||||||
type: 'REQUEST_CREATE_OR_GET_ACCOUNTS',
|
type: REQUEST_CREATE_OR_GET_ACCOUNTS,
|
||||||
chainId: '1',
|
chainId: '1',
|
||||||
},
|
},
|
||||||
VITE_WALLET_IFRAME_URL,
|
VITE_WALLET_IFRAME_URL,
|
||||||
|
@ -3,3 +3,14 @@ export const SHORT_COMMIT_HASH_LENGTH = 8;
|
|||||||
export const SERVER_GQL_PATH = 'graphql';
|
export const SERVER_GQL_PATH = 'graphql';
|
||||||
|
|
||||||
export const SHOPIFY_APP_URL = 'https://store.laconic.com';
|
export const SHOPIFY_APP_URL = 'https://store.laconic.com';
|
||||||
|
|
||||||
|
// iframe request types
|
||||||
|
export const REQUEST_CREATE_OR_GET_ACCOUNTS = 'REQUEST_CREATE_OR_GET_ACCOUNTS';
|
||||||
|
export const REQUEST_ADD_NETWORK = 'REQUEST_ADD_NETWORK';
|
||||||
|
export const REQUEST_WALLET_ACCOUNTS = 'REQUEST_WALLET_ACCOUNTS';
|
||||||
|
|
||||||
|
// iframe response types
|
||||||
|
export const WALLET_ACCOUNTS_DATA = 'WALLET_ACCOUNTS_DATA';
|
||||||
|
export const NETWORK_ADDED_RESPONSE = "NETWORK_ADDED_RESPONSE";
|
||||||
|
export const NETWORK_ALREADY_EXISTS_RESPONSE = "NETWORK_ALREADY_EXISTS_RESPONSE";
|
||||||
|
export const NETWORK_ADD_FAILED_RESPONSE = "NETWORK_ADD_FAILED_RESPONSE";
|
||||||
|
92
packages/frontend/src/hooks/useAddNetwork.ts
Normal file
92
packages/frontend/src/hooks/useAddNetwork.ts
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
import { useState, useEffect } from 'react';
|
||||||
|
import { VITE_LACONICD_CHAIN_ID, VITE_WALLET_IFRAME_URL } from 'utils/constants';
|
||||||
|
import { NETWORK_ADD_FAILED_RESPONSE, NETWORK_ADDED_RESPONSE, NETWORK_ALREADY_EXISTS_RESPONSE, REQUEST_ADD_NETWORK } from '../constants';
|
||||||
|
|
||||||
|
interface NetworkData {
|
||||||
|
chainId: string;
|
||||||
|
namespace: string;
|
||||||
|
networkName: string;
|
||||||
|
rpcUrl: string;
|
||||||
|
coinType: string;
|
||||||
|
addressPrefix?: string;
|
||||||
|
blockExplorerUrl?: string;
|
||||||
|
nativeDenom?: string;
|
||||||
|
gasPrice?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useAddNetwork = () => {
|
||||||
|
const [networkData, setNetworkData] = useState<NetworkData | null>(null);
|
||||||
|
const [iframe, setIframe] = useState<HTMLIFrameElement | null>(null);
|
||||||
|
const [isNetworkAvailable, setIsNetworkAvailable] = useState(false);
|
||||||
|
|
||||||
|
// useEffect to add network in embedded wallet
|
||||||
|
useEffect(() => {
|
||||||
|
if (!networkData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!iframe?.contentWindow) {
|
||||||
|
console.error('iframe not loaded');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
iframe.contentWindow.postMessage(
|
||||||
|
{
|
||||||
|
type: REQUEST_ADD_NETWORK,
|
||||||
|
chainId: VITE_LACONICD_CHAIN_ID,
|
||||||
|
networkData,
|
||||||
|
},
|
||||||
|
VITE_WALLET_IFRAME_URL,
|
||||||
|
);
|
||||||
|
}, [networkData, iframe]);
|
||||||
|
|
||||||
|
// useEffect to listen for network add reponses
|
||||||
|
useEffect(() => {
|
||||||
|
const handleMessage = (event: MessageEvent) => {
|
||||||
|
if (event.origin !== VITE_WALLET_IFRAME_URL) return;
|
||||||
|
|
||||||
|
switch (event.data.type) {
|
||||||
|
case NETWORK_ADDED_RESPONSE:
|
||||||
|
case NETWORK_ALREADY_EXISTS_RESPONSE:
|
||||||
|
// Once network is available, set state
|
||||||
|
setIsNetworkAvailable(true);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NETWORK_ADD_FAILED_RESPONSE:
|
||||||
|
setIsNetworkAvailable(false);
|
||||||
|
console.error("Network could not be added:", event.data.message);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('message', handleMessage);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('message', handleMessage);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const loadNetworkData = async () => {
|
||||||
|
try {
|
||||||
|
const res = await fetch('/network.json');
|
||||||
|
const json = await res.json();
|
||||||
|
|
||||||
|
setNetworkData(json);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Failed to load network data:', err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
loadNetworkData();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return {
|
||||||
|
networkData,
|
||||||
|
isNetworkAvailable,
|
||||||
|
setIframe
|
||||||
|
};
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user