From 362711da2b8253944e0140ba34b7f03f65a6abd4 Mon Sep 17 00:00:00 2001 From: Tolu Lawson <55362043+tolulawson@users.noreply.github.com> Date: Tue, 10 Sep 2024 18:55:38 -0600 Subject: [PATCH 1/6] Refactor popup handling --- scripts/background.js | 12 +- scripts/inject-script.js | 510 ++++++++++++++++++++++++--------------- 2 files changed, 324 insertions(+), 198 deletions(-) diff --git a/scripts/background.js b/scripts/background.js index 0e1a158..179b4ac 100644 --- a/scripts/background.js +++ b/scripts/background.js @@ -78,13 +78,13 @@ async function createClientPopup({ messageType, }) { // Remove existing client popup windows - const contexts = await chrome.runtime.getContexts({ - contextTypes: ['TAB'], - }); + // const contexts = await chrome.runtime.getContexts({ + // contextTypes: ['TAB'], + // }); - contexts.forEach((context) => { - chrome.tabs.remove(context.tabId); - }); + // contexts.forEach((context) => { + // chrome.tabs.remove(context.tabId); + // }); const params = new URLSearchParams(); params.append('originTabId', JSON.stringify(sender.tab.id)); diff --git a/scripts/inject-script.js b/scripts/inject-script.js index 0ec3b06..636e2d4 100644 --- a/scripts/inject-script.js +++ b/scripts/inject-script.js @@ -2,13 +2,11 @@ import { MESSAGE_TYPES } from './helpers/constants'; const createResponseHandler = () => - ({ resolve, reject, onSuccess, onError, messageType, setRequestPending }) => { + ({ resolve, reject, onSuccess, onError, messageType }) => { function listener({ data: { type, data, error }, origin }) { // only accept messages from the same origin and message type of this context if (origin !== window.location.origin || type !== messageType) return; - setRequestPending?.(false); - if (error) { onError?.(new Error(error)); reject(new Error(error)); @@ -30,10 +28,99 @@ const createResponseHandler = class MyDogeWallet { constructor() { this.isMyDoge = true; + this.requestQueue = []; this.isRequestPending = false; console.info('MyDoge API initialized'); } + createPopupRequestHandler({ requestType, responseType, isDataValid }) { + return ({ data, onSuccess, onError }) => { + return new Promise((resolve, reject) => { + if (data && !isDataValid) { + onError?.(new Error('Invalid data')); + reject(new Error('Invalid data')); + return; + } + this.requestQueue.push({ + onSuccess, + onError, + requestType, + responseType, + resolve, + reject, + data, + }); + if (!this.isRequestPending) { + this.processNextRequest(); + } + }); + }; + } + + createPopupResponseHandler() { + return ({ resolve, reject, onSuccess, onError, responseType }) => { + const listener = ({ data: { type, data, error }, origin }) => { + // only accept messages from the same origin and message type of this context + if (origin !== window.location.origin || type !== responseType) return; + + if (error) { + onError?.(new Error(error)); + reject(new Error(error)); + } else if (data) { + onSuccess?.(data); + resolve(data); + } else { + onError?.(new Error('Unable to connect to MyDoge')); + reject(new Error('Unable to connect to MyDoge')); + } + // process next request after popup has closed + setTimeout(() => { + this.requestQueue.shift(); + this.processNextRequest(); + window.removeEventListener('message', listener); + }, 1600); + }; + window.addEventListener('message', listener); + }; + } + + handleRequest({ requestType, data }) { + window.postMessage({ type: requestType, data }, window.location.origin); + } + + handlePopupResponse({ resolve, reject, onSuccess, onError, responseType }) { + const popupResponseHandler = this.createPopupResponseHandler(); + popupResponseHandler({ resolve, reject, onSuccess, onError, responseType }); + } + + processNextRequest() { + if (this.requestQueue.length === 0) { + this.isRequestPending = false; + return; + } + this.isRequestPending = true; + + const { + data, + resolve, + reject, + onSuccess, + onError, + requestType, + responseType, + } = this.requestQueue[0]; + + this.handleRequest({ requestType, data }); + + this.handlePopupResponse({ + resolve, + reject, + onSuccess, + onError, + responseType, + }); + } + /** * Initiates a connection request with the wallet. * @function @@ -51,20 +138,24 @@ class MyDogeWallet { * .catch(error => console.error(error)); */ connect(onSuccess, onError) { - return new Promise((resolve, reject) => { - window.postMessage( - { type: MESSAGE_TYPES.CLIENT_REQUEST_CONNECTION }, - window.location.origin - ); - - createResponseHandler()({ - resolve, - reject, - onSuccess, - onError, - messageType: MESSAGE_TYPES.CLIENT_REQUEST_CONNECTION_RESPONSE, - }); - }); + return this.createPopupRequestHandler({ + requestType: MESSAGE_TYPES.CLIENT_REQUEST_CONNECTION, + responseType: MESSAGE_TYPES.CLIENT_REQUEST_CONNECTION_RESPONSE, + })({ onSuccess, onError }); + // return new Promise((resolve, reject) => { + // window.postMessage( + // { type: MESSAGE_TYPES.CLIENT_REQUEST_CONNECTION }, + // window.location.origin + // ); + + // createResponseHandler()({ + // resolve, + // reject, + // onSuccess, + // onError, + // messageType: MESSAGE_TYPES.CLIENT_REQUEST_CONNECTION_RESPONSE, + // }); + // }); } /** @@ -84,6 +175,10 @@ class MyDogeWallet { * .catch(error => console.error(error)); */ getBalance(onSuccess, onError) { + // return this.createRegularRequestHandler({ + // requestType: MESSAGE_TYPES.CLIENT_GET_BALANCE, + // responseType: MESSAGE_TYPES.CLIENT_GET_BALANCE_RESPONSE, + // })({ onSuccess, onError }); return new Promise((resolve, reject) => { window.postMessage( { type: MESSAGE_TYPES.CLIENT_GET_BALANCE }, @@ -203,34 +298,39 @@ class MyDogeWallet { * .catch(error => console.error(error)); */ requestTransaction(data, onSuccess, onError) { - return new Promise((resolve, reject) => { - if (!data?.recipientAddress || !data?.dogeAmount) { - onError?.(new Error('Invalid data')); - reject(new Error('Invalid data')); - return; - } - if (this.isRequestPending) { - onError?.(new Error('There is a pending request')); - reject(new Error('There is a pending request')); - return; - } - this.isRequestPending = true; - window.postMessage( - { type: MESSAGE_TYPES.CLIENT_REQUEST_TRANSACTION, data }, - window.location.origin - ); - - createResponseHandler()({ - resolve, - reject, - onSuccess, - onError, - messageType: MESSAGE_TYPES.CLIENT_REQUEST_TRANSACTION_RESPONSE, - setRequestPending: (isRequestPending) => { - this.isRequestPending = isRequestPending; - }, - }); - }); + return this.createPopupRequestHandler({ + requestType: MESSAGE_TYPES.CLIENT_REQUEST_TRANSACTION, + responseType: MESSAGE_TYPES.CLIENT_REQUEST_TRANSACTION_RESPONSE, + isDataValid: data?.recipientAddress && data?.dogeAmount, + })({ data, onSuccess, onError }); + // return new Promise((resolve, reject) => { + // if (!data?.recipientAddress || !data?.dogeAmount) { + // onError?.(new Error('Invalid data')); + // reject(new Error('Invalid data')); + // return; + // } + // if (this.isRequestPending) { + // onError?.(new Error('There is a pending request')); + // reject(new Error('There is a pending request')); + // return; + // } + // this.isRequestPending = true; + // window.postMessage( + // { type: MESSAGE_TYPES.CLIENT_REQUEST_TRANSACTION, data }, + // window.location.origin + // ); + + // createResponseHandler()({ + // resolve, + // reject, + // onSuccess, + // onError, + // messageType: MESSAGE_TYPES.CLIENT_REQUEST_TRANSACTION_RESPONSE, + // setRequestPending: (isRequestPending) => { + // this.isRequestPending = isRequestPending; + // }, + // }); + // }); } /** @@ -253,34 +353,39 @@ class MyDogeWallet { * .catch(error => console.error(error)); */ requestInscriptionTransaction(data, onSuccess, onError) { - return new Promise((resolve, reject) => { - if (!data?.recipientAddress || !data?.location) { - onError?.(new Error('Invalid data')); - reject(new Error('Invalid data')); - return; - } - if (this.isRequestPending) { - onError?.(new Error('There is a pending request')); - reject(new Error('There is a pending request')); - return; - } - this.isRequestPending = true; - window.postMessage( - { type: MESSAGE_TYPES.CLIENT_REQUEST_DOGINAL_TRANSACTION, data }, - window.location.origin - ); - - createResponseHandler()({ - resolve, - reject, - onSuccess, - onError, - messageType: MESSAGE_TYPES.CLIENT_REQUEST_DOGINAL_TRANSACTION_RESPONSE, - setRequestPending: (isRequestPending) => { - this.isRequestPending = isRequestPending; - }, - }); - }); + return this.createPopupRequestHandler({ + requestType: MESSAGE_TYPES.CLIENT_REQUEST_DOGINAL_TRANSACTION, + responseType: MESSAGE_TYPES.CLIENT_REQUEST_DOGINAL_TRANSACTION_RESPONSE, + isDataValid: data?.recipientAddress && data?.location, + })({ data, onSuccess, onError }); + // return new Promise((resolve, reject) => { + // if (!data?.recipientAddress || !data?.location) { + // onError?.(new Error('Invalid data')); + // reject(new Error('Invalid data')); + // return; + // } + // if (this.isRequestPending) { + // onError?.(new Error('There is a pending request')); + // reject(new Error('There is a pending request')); + // return; + // } + // this.isRequestPending = true; + // window.postMessage( + // { type: MESSAGE_TYPES.CLIENT_REQUEST_DOGINAL_TRANSACTION, data }, + // window.location.origin + // ); + + // createResponseHandler()({ + // resolve, + // reject, + // onSuccess, + // onError, + // messageType: MESSAGE_TYPES.CLIENT_REQUEST_DOGINAL_TRANSACTION_RESPONSE, + // setRequestPending: (isRequestPending) => { + // this.isRequestPending = isRequestPending; + // }, + // }); + // }); } /** @@ -304,38 +409,44 @@ class MyDogeWallet { * .catch(error => console.error(error)); */ requestAvailableDRC20Transaction(data, onSuccess, onError) { - return new Promise((resolve, reject) => { - if (!data?.ticker || !data?.amount) { - onError?.(new Error('Invalid data')); - reject(new Error('Invalid data')); - return; - } - if (this.isRequestPending) { - onError?.(new Error('There is a pending request')); - reject(new Error('There is a pending request')); - return; - } - this.isRequestPending = true; - window.postMessage( - { - type: MESSAGE_TYPES.CLIENT_REQUEST_AVAILABLE_DRC20_TRANSACTION, - data, - }, - window.location.origin - ); - - createResponseHandler()({ - resolve, - reject, - onSuccess, - onError, - messageType: - MESSAGE_TYPES.CLIENT_REQUEST_AVAILABLE_DRC20_TRANSACTION_RESPONSE, - setRequestPending: (isRequestPending) => { - this.isRequestPending = isRequestPending; - }, - }); - }); + return this.createPopupRequestHandler({ + requestType: MESSAGE_TYPES.CLIENT_REQUEST_AVAILABLE_DRC20_TRANSACTION, + responseType: + MESSAGE_TYPES.CLIENT_REQUEST_AVAILABLE_DRC20_TRANSACTION_RESPONSE, + isDataValid: data?.ticker && data?.amount, + })({ data, onSuccess, onError }); + // return new Promise((resolve, reject) => { + // if (!data?.ticker || !data?.amount) { + // onError?.(new Error('Invalid data')); + // reject(new Error('Invalid data')); + // return; + // } + // if (this.isRequestPending) { + // onError?.(new Error('There is a pending request')); + // reject(new Error('There is a pending request')); + // return; + // } + // this.isRequestPending = true; + // window.postMessage( + // { + // type: MESSAGE_TYPES.CLIENT_REQUEST_AVAILABLE_DRC20_TRANSACTION, + // data, + // }, + // window.location.origin + // ); + + // createResponseHandler()({ + // resolve, + // reject, + // onSuccess, + // onError, + // messageType: + // MESSAGE_TYPES.CLIENT_REQUEST_AVAILABLE_DRC20_TRANSACTION_RESPONSE, + // setRequestPending: (isRequestPending) => { + // this.isRequestPending = isRequestPending; + // }, + // }); + // }); } /** @@ -358,37 +469,42 @@ class MyDogeWallet { * .catch(error => console.error(error)); */ requestPsbt(data, onSuccess, onError) { - return new Promise((resolve, reject) => { - if (!data?.rawTx || !data?.indexes?.length) { - onError?.(new Error('Invalid data')); - reject(new Error('Invalid data')); - return; - } - if (this.isRequestPending) { - onError?.(new Error('There is a pending request')); - reject(new Error('There is a pending request')); - return; - } - this.isRequestPending = true; - window.postMessage( - { - type: MESSAGE_TYPES.CLIENT_REQUEST_PSBT, - data, - }, - window.location.origin - ); - - createResponseHandler()({ - resolve, - reject, - onSuccess, - onError, - messageType: MESSAGE_TYPES.CLIENT_REQUEST_PSBT_RESPONSE, - setRequestPending: (isRequestPending) => { - this.isRequestPending = isRequestPending; - }, - }); - }); + return this.createPopupRequestHandler({ + requestType: MESSAGE_TYPES.CLIENT_REQUEST_PSBT, + responseType: MESSAGE_TYPES.CLIENT_REQUEST_PSBT_RESPONSE, + isDataValid: data?.rawTx && data?.indexes?.length, + })({ data, onSuccess, onError }); + // return new Promise((resolve, reject) => { + // if (!data?.rawTx || !data?.indexes?.length) { + // onError?.(new Error('Invalid data')); + // reject(new Error('Invalid data')); + // return; + // } + // if (this.isRequestPending) { + // onError?.(new Error('There is a pending request')); + // reject(new Error('There is a pending request')); + // return; + // } + // this.isRequestPending = true; + // window.postMessage( + // { + // type: MESSAGE_TYPES.CLIENT_REQUEST_PSBT, + // data, + // }, + // window.location.origin + // ); + + // createResponseHandler()({ + // resolve, + // reject, + // onSuccess, + // onError, + // messageType: MESSAGE_TYPES.CLIENT_REQUEST_PSBT_RESPONSE, + // setRequestPending: (isRequestPending) => { + // this.isRequestPending = isRequestPending; + // }, + // }); + // }); } /** @@ -410,34 +526,39 @@ class MyDogeWallet { * .catch(error => console.error(error)); */ requestSignedMessage(data, onSuccess, onError) { - return new Promise((resolve, reject) => { - if (!data?.message) { - onError?.(new Error('Invalid data')); - reject(new Error('Invalid data')); - return; - } - if (this.isRequestPending) { - onError?.(new Error('There is a pending request')); - reject(new Error('There is a pending request')); - return; - } - this.isRequestPending = true; - window.postMessage( - { type: MESSAGE_TYPES.CLIENT_REQUEST_SIGNED_MESSAGE, data }, - window.location.origin - ); - - createResponseHandler()({ - resolve, - reject, - onSuccess, - onError, - messageType: MESSAGE_TYPES.CLIENT_REQUEST_SIGNED_MESSAGE_RESPONSE, - setRequestPending: (isRequestPending) => { - this.isRequestPending = isRequestPending; - }, - }); - }); + return this.createPopupRequestHandler({ + requestType: MESSAGE_TYPES.CLIENT_REQUEST_SIGNED_MESSAGE, + responseType: MESSAGE_TYPES.CLIENT_REQUEST_SIGNED_MESSAGE_RESPONSE, + isDataValid: !!data?.message, + })({ data, onSuccess, onError }); + // return new Promise((resolve, reject) => { + // if (!data?.message) { + // onError?.(new Error('Invalid data')); + // reject(new Error('Invalid data')); + // return; + // } + // if (this.isRequestPending) { + // onError?.(new Error('There is a pending request')); + // reject(new Error('There is a pending request')); + // return; + // } + // this.isRequestPending = true; + // window.postMessage( + // { type: MESSAGE_TYPES.CLIENT_REQUEST_SIGNED_MESSAGE, data }, + // window.location.origin + // ); + + // createResponseHandler()({ + // resolve, + // reject, + // onSuccess, + // onError, + // messageType: MESSAGE_TYPES.CLIENT_REQUEST_SIGNED_MESSAGE_RESPONSE, + // setRequestPending: (isRequestPending) => { + // this.isRequestPending = isRequestPending; + // }, + // }); + // }); } /** @@ -459,34 +580,39 @@ class MyDogeWallet { * .catch(error => console.error(error)); */ requestDecryptedMessage(data, onSuccess, onError) { - return new Promise((resolve, reject) => { - if (!data?.message) { - onError?.(new Error('Invalid data')); - reject(new Error('Invalid data')); - return; - } - if (this.isRequestPending) { - onError?.(new Error('There is a pending request')); - reject(new Error('There is a pending request')); - return; - } - this.isRequestPending = true; - window.postMessage( - { type: MESSAGE_TYPES.CLIENT_REQUEST_DECRYPTED_MESSAGE, data }, - window.location.origin - ); - - createResponseHandler()({ - resolve, - reject, - onSuccess, - onError, - messageType: MESSAGE_TYPES.CLIENT_REQUEST_DECRYPTED_MESSAGE_RESPONSE, - setRequestPending: (isRequestPending) => { - this.isRequestPending = isRequestPending; - }, - }); - }); + return this.createPopupRequestHandler({ + requestType: MESSAGE_TYPES.CLIENT_REQUEST_DECRYPTED_MESSAGE, + responseType: MESSAGE_TYPES.CLIENT_REQUEST_DECRYPTED_MESSAGE_RESPONSE, + isDataValid: !!data?.message, + })({ data, onSuccess, onError }); + // return new Promise((resolve, reject) => { + // if (!data?.message) { + // onError?.(new Error('Invalid data')); + // reject(new Error('Invalid data')); + // return; + // } + // if (this.isRequestPending) { + // onError?.(new Error('There is a pending request')); + // reject(new Error('There is a pending request')); + // return; + // } + // this.isRequestPending = true; + // window.postMessage( + // { type: MESSAGE_TYPES.CLIENT_REQUEST_DECRYPTED_MESSAGE, data }, + // window.location.origin + // ); + + // createResponseHandler()({ + // resolve, + // reject, + // onSuccess, + // onError, + // messageType: MESSAGE_TYPES.CLIENT_REQUEST_DECRYPTED_MESSAGE_RESPONSE, + // setRequestPending: (isRequestPending) => { + // this.isRequestPending = isRequestPending; + // }, + // }); + // }); } /** From 34bdad19ac496fe753b6ac2b7810d6e86c451ab7 Mon Sep 17 00:00:00 2001 From: Tolu Lawson <55362043+tolulawson@users.noreply.github.com> Date: Wed, 11 Sep 2024 13:37:39 -0600 Subject: [PATCH 2/6] Fix wrong popup response type --- .../ClientAvailableDRC20Transaction.js | 21 ++++----- views/ClientRequest/ClientConnect.js | 11 +++-- views/ClientRequest/ClientDecryptedMessage.js | 20 ++++++--- .../ClientRequest/ClientDoginalTransaction.js | 43 ++++++++++++------- views/ClientRequest/ClientPSBT.js | 12 +++--- views/ClientRequest/ClientSignedMessage.js | 20 ++++++--- views/ClientRequest/ClientTransaction.js | 26 ++++++++--- views/ClientRequest/index.js | 42 ++++++++++++++---- 8 files changed, 136 insertions(+), 59 deletions(-) diff --git a/views/ClientRequest/ClientAvailableDRC20Transaction.js b/views/ClientRequest/ClientAvailableDRC20Transaction.js index dcf5650..37bcdf4 100644 --- a/views/ClientRequest/ClientAvailableDRC20Transaction.js +++ b/views/ClientRequest/ClientAvailableDRC20Transaction.js @@ -26,6 +26,7 @@ export function ClientAvailableDRC20Transaction({ connectedClient, connectedAddressIndex, handleError, + responseMessageType, }) { const handleWindowClose = useCallback(() => { dispatch({ type: DISPATCH_TYPES.CLEAR_CLIENT_REQUEST }); @@ -48,7 +49,7 @@ export function ClientAvailableDRC20Transaction({ const onRejectTransaction = useCallback(() => { sendMessage( { - message: MESSAGE_TYPES.CLIENT_REQUEST_TRANSACTION_RESPONSE, + message: responseMessageType, data: { error: 'User refused transaction', originTabId, origin }, }, () => { @@ -68,7 +69,7 @@ export function ClientAvailableDRC20Transaction({ }, [] ); - }, [handleWindowClose, origin, originTabId]); + }, [handleWindowClose, origin, originTabId, responseMessageType]); useEffect(() => { if (!connectedClient?.address || typeof connectedAddressIndex !== 'number') @@ -83,8 +84,7 @@ export function ClientAvailableDRC20Transaction({ setPageLoading(false); handleError({ error: 'Insufficient balance', - messageType: - MESSAGE_TYPES.CLIENT_REQUEST_AVAILABLE_DRC20_TRANSACTION_RESPONSE, + messageType: responseMessageType, }); return; } @@ -106,8 +106,7 @@ export function ClientAvailableDRC20Transaction({ } else { handleError({ error: 'Unable to create available drc-20 transaction', - messageType: - MESSAGE_TYPES.CLIENT_REQUEST_AVAILABLE_DRC20_TRANSACTION_RESPONSE, + messageType: responseMessageType, }); } } @@ -119,6 +118,7 @@ export function ClientAvailableDRC20Transaction({ connectedClient?.address, handleError, params, + responseMessageType, ticker, ]); @@ -174,6 +174,7 @@ export function ClientAvailableDRC20Transaction({ params={params} handleWindowClose={handleWindowClose} txs={transaction?.txs} + responseMessageType={responseMessageType} /> ); @@ -184,6 +185,7 @@ const ConfirmationModal = ({ onClose, params, handleWindowClose, + responseMessageType, txs, }) => { const cancelRef = useRef(); @@ -202,8 +204,7 @@ const ConfirmationModal = ({ if (txId) { sendMessage( { - message: - MESSAGE_TYPES.CLIENT_REQUEST_AVAILABLE_DRC20_TRANSACTION_RESPONSE, + message: responseMessageType, data: { tokenAmount, ticker, txId, originTabId, origin }, }, () => { @@ -224,8 +225,7 @@ const ConfirmationModal = ({ ); } else { sendMessage({ - message: - MESSAGE_TYPES.CLIENT_REQUEST_AVAILABLE_DRC20_TRANSACTION_RESPONSE, + message: responseMessageType, data: { error: 'Failed to inscribe token transfer', originTabId, @@ -256,6 +256,7 @@ const ConfirmationModal = ({ origin, originTabId, params, + responseMessageType, ticker, tokenAmount, txs, diff --git a/views/ClientRequest/ClientConnect.js b/views/ClientRequest/ClientConnect.js index b9035cf..35f67e7 100644 --- a/views/ClientRequest/ClientConnect.js +++ b/views/ClientRequest/ClientConnect.js @@ -25,7 +25,12 @@ import { sendMessage } from '../../scripts/helpers/message'; const REFRESH_INTERVAL = 10000; -export function ClientConnect({ params, wallet, dispatch }) { +export function ClientConnect({ + params, + wallet, + dispatch, + responseMessageType, +}) { const { origin, originTabId } = params ?? {}; const handleWindowClose = useCallback(() => { @@ -35,7 +40,7 @@ export function ClientConnect({ params, wallet, dispatch }) { const onRejectConnection = useCallback(() => { sendMessage( { - message: MESSAGE_TYPES.CLIENT_REQUEST_CONNECTION_RESPONSE, + message: responseMessageType, data: { approved: false, originTabId, origin }, }, () => { @@ -55,7 +60,7 @@ export function ClientConnect({ params, wallet, dispatch }) { }, [] ); - }, [handleWindowClose, origin, originTabId]); + }, [handleWindowClose, origin, originTabId, responseMessageType]); const [addressBalances, setAddressBalances] = useState({}); const [selectedAddressIndex, setSelectedAddressIndex] = useState(0); diff --git a/views/ClientRequest/ClientDecryptedMessage.js b/views/ClientRequest/ClientDecryptedMessage.js index 0789fcf..7ca67e1 100644 --- a/views/ClientRequest/ClientDecryptedMessage.js +++ b/views/ClientRequest/ClientDecryptedMessage.js @@ -26,6 +26,7 @@ export function ClientDecryptedMessage({ dispatch, connectedClient, connectedAddressIndex: addressIndex, + responseMessageType, }) { const { originTabId, origin, message } = params; @@ -41,7 +42,7 @@ export function ClientDecryptedMessage({ const onRejectTransaction = useCallback(() => { sendMessage( { - message: MESSAGE_TYPES.CLIENT_REQUEST_DECRYPTED_MESSAGE_RESPONSE, + message: responseMessageType, data: { error: 'User refused decrypted message', originTabId, origin }, }, () => { @@ -61,7 +62,7 @@ export function ClientDecryptedMessage({ }, [] ); - }, [handleWindowClose, origin, originTabId]); + }, [handleWindowClose, origin, originTabId, responseMessageType]); return ( <> @@ -111,6 +112,7 @@ export function ClientDecryptedMessage({ message={message} addressIndex={addressIndex} handleWindowClose={handleWindowClose} + responseMessageType={responseMessageType} /> ); @@ -124,6 +126,7 @@ const ConfirmationModal = ({ addressIndex, originTabId, handleWindowClose, + responseMessageType, }) => { const cancelRef = useRef(); const [loading, setLoading] = useState(false); @@ -139,7 +142,7 @@ const ConfirmationModal = ({ if (decryptedMessage) { sendMessage( { - message: MESSAGE_TYPES.CLIENT_REQUEST_DECRYPTED_MESSAGE_RESPONSE, + message: responseMessageType, data: { decryptedMessage, originTabId, origin }, }, () => { @@ -160,7 +163,7 @@ const ConfirmationModal = ({ } else { sendMessage( { - message: MESSAGE_TYPES.CLIENT_REQUEST_DECRYPTED_MESSAGE_RESPONSE, + message: responseMessageType, data: { error: 'Failed to decrypt message', originTabId, @@ -188,7 +191,14 @@ const ConfirmationModal = ({ } } ); - }, [addressIndex, handleWindowClose, origin, originTabId, message]); + }, [ + addressIndex, + handleWindowClose, + origin, + originTabId, + message, + responseMessageType, + ]); return ( <> diff --git a/views/ClientRequest/ClientDoginalTransaction.js b/views/ClientRequest/ClientDoginalTransaction.js index e082ca9..4c9425d 100644 --- a/views/ClientRequest/ClientDoginalTransaction.js +++ b/views/ClientRequest/ClientDoginalTransaction.js @@ -32,6 +32,7 @@ export function ClientDoginalTransaction({ connectedClient, connectedAddressIndex, handleError, + responseMessageType, }) { const { originTabId, origin, recipientAddress, location } = params; @@ -53,8 +54,7 @@ export function ClientDoginalTransaction({ if (!validateAddress(recipientAddress)) { handleError({ error: 'Invalid address', - messageType: - MESSAGE_TYPES.CLIENT_REQUEST_DOGINAL_TRANSACTION_RESPONSE, + messageType: responseMessageType, }); return; } @@ -67,8 +67,7 @@ export function ClientDoginalTransaction({ if (txid?.length !== 64 || Number.isNaN(vout)) { handleError({ error: 'Invalid output', - messageType: - MESSAGE_TYPES.CLIENT_REQUEST_DOGINAL_TRANSACTION_RESPONSE, + messageType: responseMessageType, }); return; } @@ -83,8 +82,7 @@ export function ClientDoginalTransaction({ if (!doginal || !doginal.inscriptions?.find((i) => i.offset === offset)) { handleError({ error: 'Doginal not found', - messageType: - MESSAGE_TYPES.CLIENT_REQUEST_DOGINAL_TRANSACTION_RESPONSE, + messageType: responseMessageType, }); setPageLoading(false); return; @@ -110,15 +108,20 @@ export function ClientDoginalTransaction({ } else { handleError({ error: 'Unable to create doginal transaction', - messageType: - MESSAGE_TYPES.CLIENT_REQUEST_DOGINAL_TRANSACTION_RESPONSE, + messageType: responseMessageType, }); throw new Error('Unable to create doginal transaction'); } } ); })(); - }, [connectedClient?.address, handleError, location, recipientAddress]); + }, [ + connectedClient?.address, + handleError, + location, + recipientAddress, + responseMessageType, + ]); const [confirmationModalOpen, setConfirmationModalOpen] = useState(false); const onCloseModal = useCallback(() => { @@ -128,7 +131,7 @@ export function ClientDoginalTransaction({ const onRejectTransaction = useCallback(() => { sendMessage( { - message: MESSAGE_TYPES.CLIENT_REQUEST_TRANSACTION_RESPONSE, + message: responseMessageType, data: { error: 'User refused transaction', originTabId, origin }, }, () => { @@ -148,7 +151,7 @@ export function ClientDoginalTransaction({ }, [] ); - }, [handleWindowClose, origin, originTabId]); + }, [handleWindowClose, origin, originTabId, responseMessageType]); if (!transaction) return ( @@ -214,6 +217,7 @@ export function ClientDoginalTransaction({ recipientAddress={recipientAddress} dogeAmount={transaction.amount} selectedNFT={selectedNFT} + responseMessageType={responseMessageType} /> ); @@ -230,6 +234,7 @@ const ConfirmationModal = ({ recipientAddress, dogeAmount, selectedNFT, + responseMessageType, }) => { const cancelRef = useRef(); const [loading, setLoading] = useState(false); @@ -252,8 +257,7 @@ const ConfirmationModal = ({ if (txId) { sendMessage( { - message: - MESSAGE_TYPES.CLIENT_REQUEST_DOGINAL_TRANSACTION_RESPONSE, + message: responseMessageType, data: { txId, originTabId, origin }, }, () => { @@ -274,7 +278,7 @@ const ConfirmationModal = ({ } else { sendMessage( { - message: MESSAGE_TYPES.CLIENT_REQUEST_TRANSACTION_RESPONSE, + message: responseMessageType, data: { error: 'Failed to send transaction', originTabId, @@ -302,7 +306,16 @@ const ConfirmationModal = ({ } } ); - }, [addressIndex, handleWindowClose, onClose, origin, originTabId, rawTx]); + }, [ + addressIndex, + handleWindowClose, + onClose, + origin, + originTabId, + rawTx, + responseMessageType, + selectedNFT.location, + ]); return ( <> diff --git a/views/ClientRequest/ClientPSBT.js b/views/ClientRequest/ClientPSBT.js index 47c04d1..faacce6 100644 --- a/views/ClientRequest/ClientPSBT.js +++ b/views/ClientRequest/ClientPSBT.js @@ -31,6 +31,7 @@ export function ClientPSBT({ dispatch, connectedClient, connectedAddressIndex: selectedAddressIndex, + responseMessageType, }) { const { originTabId, origin, rawTx, indexes: indexesParam } = params; @@ -120,7 +121,7 @@ export function ClientPSBT({ setLoading(false); sendMessage( { - message: MESSAGE_TYPES.CLIENT_REQUEST_PSBT_RESPONSE, + message: responseMessageType, data: { error: description, originTabId, origin }, }, () => { @@ -141,13 +142,13 @@ export function ClientPSBT({ [] ); }, - [handleWindowClose, origin, originTabId] + [handleWindowClose, origin, originTabId, responseMessageType] ); const onRejectTransaction = useCallback(() => { sendMessage( { - message: MESSAGE_TYPES.CLIENT_REQUEST_PSBT_RESPONSE, + message: responseMessageType, data: { error: 'User refused transaction', originTabId, origin }, }, () => { @@ -167,7 +168,7 @@ export function ClientPSBT({ }, [] ); - }, [handleWindowClose, origin, originTabId]); + }, [handleWindowClose, origin, originTabId, responseMessageType]); const [loading, setLoading] = useState(false); @@ -190,7 +191,7 @@ export function ClientPSBT({ if (txId) { sendMessage( { - message: MESSAGE_TYPES.CLIENT_REQUEST_PSBT_RESPONSE, + message: responseMessageType, data: { txId, originTabId, origin }, }, () => { @@ -232,6 +233,7 @@ export function ClientPSBT({ originTabId, rawTx, selectedAddressIndex, + responseMessageType, ]); const [inputsModalOpen, setInputsModalOpen] = useState(false); diff --git a/views/ClientRequest/ClientSignedMessage.js b/views/ClientRequest/ClientSignedMessage.js index 3b652e1..01fbe1c 100644 --- a/views/ClientRequest/ClientSignedMessage.js +++ b/views/ClientRequest/ClientSignedMessage.js @@ -26,6 +26,7 @@ export function ClientSignedMessage({ dispatch, connectedClient, connectedAddressIndex: addressIndex, + responseMessageType, }) { const { originTabId, origin, message } = params; @@ -41,7 +42,7 @@ export function ClientSignedMessage({ const onRejectTransaction = useCallback(() => { sendMessage( { - message: MESSAGE_TYPES.CLIENT_REQUEST_SIGNED_MESSAGE_RESPONSE, + message: responseMessageType, data: { error: 'User refused signed message', originTabId, origin }, }, () => { @@ -61,7 +62,7 @@ export function ClientSignedMessage({ }, [] ); - }, [handleWindowClose, origin, originTabId]); + }, [handleWindowClose, origin, originTabId, responseMessageType]); return ( <> @@ -112,6 +113,7 @@ export function ClientSignedMessage({ message={message} addressIndex={addressIndex} handleWindowClose={handleWindowClose} + responseMessageType={responseMessageType} /> ); @@ -125,6 +127,7 @@ const ConfirmationModal = ({ addressIndex, originTabId, handleWindowClose, + responseMessageType, }) => { const cancelRef = useRef(); const [loading, setLoading] = useState(false); @@ -140,7 +143,7 @@ const ConfirmationModal = ({ if (signedMessage) { sendMessage( { - message: MESSAGE_TYPES.CLIENT_REQUEST_SIGNED_MESSAGE_RESPONSE, + message: responseMessageType, data: { signedMessage, originTabId, origin }, }, () => { @@ -161,7 +164,7 @@ const ConfirmationModal = ({ } else { sendMessage( { - message: MESSAGE_TYPES.CLIENT_REQUEST_SIGNED_MESSAGE_RESPONSE, + message: responseMessageType, data: { error: 'Failed to sign message', originTabId, @@ -189,7 +192,14 @@ const ConfirmationModal = ({ } } ); - }, [addressIndex, handleWindowClose, origin, originTabId, message]); + }, [ + addressIndex, + handleWindowClose, + origin, + originTabId, + message, + responseMessageType, + ]); return ( <> diff --git a/views/ClientRequest/ClientTransaction.js b/views/ClientRequest/ClientTransaction.js index 8a0e6b4..204af8e 100644 --- a/views/ClientRequest/ClientTransaction.js +++ b/views/ClientRequest/ClientTransaction.js @@ -28,6 +28,7 @@ export function ClientTransaction({ connectedAddressIndex, handleError, handleWindowClose, + responseMessageType, }) { const { originTabId, origin, recipientAddress, dogeAmount } = params; @@ -56,7 +57,7 @@ export function ClientTransaction({ }); if (error) { handleError({ - messageType: MESSAGE_TYPES.CLIENT_REQUEST_TRANSACTION_RESPONSE, + messageType: responseMessageType, error, }); } @@ -69,7 +70,7 @@ export function ClientTransaction({ setTransaction({ rawTx, fee, amount }); } else { handleError({ - messageType: MESSAGE_TYPES.CLIENT_REQUEST_TRANSACTION_RESPONSE, + messageType: responseMessageType, error: 'Unable to create transaction', }); } @@ -83,6 +84,7 @@ export function ClientTransaction({ origin, originTabId, recipientAddress, + responseMessageType, ]); const [confirmationModalOpen, setConfirmationModalOpen] = useState(false); @@ -93,7 +95,7 @@ export function ClientTransaction({ const onRejectTransaction = useCallback(() => { sendMessage( { - message: MESSAGE_TYPES.CLIENT_REQUEST_TRANSACTION_RESPONSE, + message: responseMessageType, data: { error: 'User refused transaction', originTabId, origin }, }, () => { @@ -113,7 +115,7 @@ export function ClientTransaction({ }, [] ); - }, [handleWindowClose, origin, originTabId]); + }, [handleWindowClose, origin, originTabId, responseMessageType]); if (!transaction) return ( @@ -174,6 +176,7 @@ export function ClientTransaction({ handleWindowClose={handleWindowClose} recipientAddress={recipientAddress} dogeAmount={dogeAmount} + responseMessageType={responseMessageType} /> ); @@ -189,6 +192,7 @@ const ConfirmationModal = ({ handleWindowClose, recipientAddress, dogeAmount, + responseMessageType, }) => { const cancelRef = useRef(); const [loading, setLoading] = useState(false); @@ -206,7 +210,7 @@ const ConfirmationModal = ({ if (txId) { sendMessage( { - message: MESSAGE_TYPES.CLIENT_REQUEST_TRANSACTION_RESPONSE, + message: responseMessageType, data: { txId, originTabId, origin }, }, () => { @@ -227,7 +231,7 @@ const ConfirmationModal = ({ } else { sendMessage( { - message: MESSAGE_TYPES.CLIENT_REQUEST_TRANSACTION_RESPONSE, + message: responseMessageType, data: { error: 'Failed to send transaction', originTabId, @@ -255,7 +259,15 @@ const ConfirmationModal = ({ } } ); - }, [addressIndex, handleWindowClose, onClose, origin, originTabId, rawTx]); + }, [ + addressIndex, + handleWindowClose, + onClose, + origin, + originTabId, + rawTx, + responseMessageType, + ]); return ( <> diff --git a/views/ClientRequest/index.js b/views/ClientRequest/index.js index cdf9e25..bc9066b 100644 --- a/views/ClientRequest/index.js +++ b/views/ClientRequest/index.js @@ -16,14 +16,34 @@ import { ClientSignedMessage } from './ClientSignedMessage'; import { ClientTransaction } from './ClientTransaction'; const CLIENT_REQUEST_ROUTES = { - [MESSAGE_TYPES.CLIENT_REQUEST_CONNECTION]: ClientConnect, - [MESSAGE_TYPES.CLIENT_REQUEST_TRANSACTION]: ClientTransaction, - [MESSAGE_TYPES.CLIENT_REQUEST_DOGINAL_TRANSACTION]: ClientDoginalTransaction, - [MESSAGE_TYPES.CLIENT_REQUEST_AVAILABLE_DRC20_TRANSACTION]: - ClientAvailableDRC20Transaction, - [MESSAGE_TYPES.CLIENT_REQUEST_PSBT]: ClientPSBT, - [MESSAGE_TYPES.CLIENT_REQUEST_SIGNED_MESSAGE]: ClientSignedMessage, - [MESSAGE_TYPES.CLIENT_REQUEST_DECRYPTED_MESSAGE]: ClientDecryptedMessage, + [MESSAGE_TYPES.CLIENT_REQUEST_CONNECTION]: { + component: ClientConnect, + response: MESSAGE_TYPES.CLIENT_REQUEST_CONNECTION_RESPONSE, + }, + [MESSAGE_TYPES.CLIENT_REQUEST_TRANSACTION]: { + component: ClientTransaction, + response: MESSAGE_TYPES.CLIENT_REQUEST_TRANSACTION_RESPONSE, + }, + [MESSAGE_TYPES.CLIENT_REQUEST_DOGINAL_TRANSACTION]: { + component: ClientDoginalTransaction, + response: MESSAGE_TYPES.CLIENT_REQUEST_DOGINAL_TRANSACTION_RESPONSE, + }, + [MESSAGE_TYPES.CLIENT_REQUEST_AVAILABLE_DRC20_TRANSACTION]: { + component: ClientAvailableDRC20Transaction, + response: MESSAGE_TYPES.CLIENT_REQUEST_AVAILABLE_DRC20_TRANSACTION_RESPONSE, + }, + [MESSAGE_TYPES.CLIENT_REQUEST_PSBT]: { + component: ClientPSBT, + response: MESSAGE_TYPES.CLIENT_REQUEST_PSBT_RESPONSE, + }, + [MESSAGE_TYPES.CLIENT_REQUEST_SIGNED_MESSAGE]: { + component: ClientSignedMessage, + response: MESSAGE_TYPES.CLIENT_REQUEST_SIGNED_MESSAGE_RESPONSE, + }, + [MESSAGE_TYPES.CLIENT_REQUEST_DECRYPTED_MESSAGE]: { + component: ClientDecryptedMessage, + response: MESSAGE_TYPES.CLIENT_REQUEST_DECRYPTED_MESSAGE_RESPONSE, + }, }; export function ClientRequest() { @@ -32,9 +52,12 @@ export function ClientRequest() { const { params } = clientRequest; const RenderScreen = clientRequest - ? CLIENT_REQUEST_ROUTES[clientRequest?.requestType] + ? CLIENT_REQUEST_ROUTES[clientRequest?.requestType]?.component : null; + const responseMessageType = + CLIENT_REQUEST_ROUTES[clientRequest?.requestType]?.response; + const handleWindowClose = useCallback(() => { dispatch({ type: DISPATCH_TYPES.CLEAR_CLIENT_REQUEST }); }, [dispatch]); @@ -82,6 +105,7 @@ export function ClientRequest() { connectedAddressIndex={params.connectedAddressIndex} handleError={handleError} handleWindowClose={handleWindowClose} + responseMessageType={responseMessageType} /> ); From 115e4d24d849233f8b003c94a6f0f670833965f0 Mon Sep 17 00:00:00 2001 From: Tolu Lawson <55362043+tolulawson@users.noreply.github.com> Date: Wed, 11 Sep 2024 20:12:14 -0600 Subject: [PATCH 3/6] Refactor client popups --- .eslintrc.json | 3 +- Context.js | 2 +- scripts/inject-script.js | 61 +++-- .../ClientAvailableDRC20Transaction.js | 203 ++++++++-------- views/ClientRequest/ClientConnect.js | 167 ++++++------- views/ClientRequest/ClientDecryptedMessage.js | 203 ++++++++-------- .../ClientRequest/ClientDoginalTransaction.js | 225 +++++++++--------- views/ClientRequest/ClientPSBT.js | 175 +++++++------- views/ClientRequest/ClientSignedMessage.js | 201 ++++++++-------- views/ClientRequest/ClientTransaction.js | 206 ++++++++-------- views/ClientRequest/index.js | 109 ++++++--- 11 files changed, 834 insertions(+), 721 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 72a5a1a..f1acef6 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -5,8 +5,9 @@ "es2021": true, "react-native/react-native": true }, + // "parser": "@typescript-eslint/parser", "parserOptions": { - "ecmaVersion": 2020, + "ecmaVersion": 2022, "ecmaFeatures": { "jsx": true }, diff --git a/Context.js b/Context.js index 621627a..362893b 100644 --- a/Context.js +++ b/Context.js @@ -82,7 +82,7 @@ export const AppContextProvider = ({ children }) => { case DISPATCH_TYPES.SET_CLIENT_REQUEST: return { ...state, clientRequest: payload.clientRequest }; case DISPATCH_TYPES.CLEAR_CLIENT_REQUEST: - setTimeout(() => window?.close(), 1500); + window?.close(); return { ...state }; case DISPATCH_TYPES.SET_CONTEXT_LOADED: return { ...state, ready: payload.ready }; diff --git a/scripts/inject-script.js b/scripts/inject-script.js index 636e2d4..a151dcc 100644 --- a/scripts/inject-script.js +++ b/scripts/inject-script.js @@ -14,8 +14,6 @@ const createResponseHandler = onSuccess?.(data); resolve(data); } else { - onError?.(new Error('Unable to connect to MyDoge')); - reject(new Error('Unable to connect to MyDoge')); } window.removeEventListener('message', listener); } @@ -26,14 +24,14 @@ const createResponseHandler = * Class representing the MyDoge API to interact with the Dogecoin wallet. */ class MyDogeWallet { + #requestQueue = []; + #isRequestPending = false; constructor() { this.isMyDoge = true; - this.requestQueue = []; - this.isRequestPending = false; console.info('MyDoge API initialized'); } - createPopupRequestHandler({ requestType, responseType, isDataValid }) { + #createPopupRequestHandler({ requestType, responseType, isDataValid }) { return ({ data, onSuccess, onError }) => { return new Promise((resolve, reject) => { if (data && !isDataValid) { @@ -41,7 +39,7 @@ class MyDogeWallet { reject(new Error('Invalid data')); return; } - this.requestQueue.push({ + this.#requestQueue.push({ onSuccess, onError, requestType, @@ -50,14 +48,14 @@ class MyDogeWallet { reject, data, }); - if (!this.isRequestPending) { - this.processNextRequest(); + if (!this.#isRequestPending) { + this.#processNextRequest(); } }); }; } - createPopupResponseHandler() { + #createPopupResponseHandler() { return ({ resolve, reject, onSuccess, onError, responseType }) => { const listener = ({ data: { type, data, error }, origin }) => { // only accept messages from the same origin and message type of this context @@ -69,36 +67,33 @@ class MyDogeWallet { } else if (data) { onSuccess?.(data); resolve(data); - } else { - onError?.(new Error('Unable to connect to MyDoge')); - reject(new Error('Unable to connect to MyDoge')); } // process next request after popup has closed setTimeout(() => { - this.requestQueue.shift(); - this.processNextRequest(); + this.#requestQueue.shift(); + this.#processNextRequest(); window.removeEventListener('message', listener); - }, 1600); + }, 500); }; window.addEventListener('message', listener); }; } - handleRequest({ requestType, data }) { + #handleRequest({ requestType, data }) { window.postMessage({ type: requestType, data }, window.location.origin); } - handlePopupResponse({ resolve, reject, onSuccess, onError, responseType }) { - const popupResponseHandler = this.createPopupResponseHandler(); + #handlePopupResponse({ resolve, reject, onSuccess, onError, responseType }) { + const popupResponseHandler = this.#createPopupResponseHandler(); popupResponseHandler({ resolve, reject, onSuccess, onError, responseType }); } - processNextRequest() { - if (this.requestQueue.length === 0) { - this.isRequestPending = false; + #processNextRequest() { + if (this.#requestQueue.length === 0) { + this.#isRequestPending = false; return; } - this.isRequestPending = true; + this.#isRequestPending = true; const { data, @@ -108,11 +103,11 @@ class MyDogeWallet { onError, requestType, responseType, - } = this.requestQueue[0]; + } = this.#requestQueue[0]; - this.handleRequest({ requestType, data }); + this.#handleRequest({ requestType, data }); - this.handlePopupResponse({ + this.#handlePopupResponse({ resolve, reject, onSuccess, @@ -138,7 +133,7 @@ class MyDogeWallet { * .catch(error => console.error(error)); */ connect(onSuccess, onError) { - return this.createPopupRequestHandler({ + return this.#createPopupRequestHandler({ requestType: MESSAGE_TYPES.CLIENT_REQUEST_CONNECTION, responseType: MESSAGE_TYPES.CLIENT_REQUEST_CONNECTION_RESPONSE, })({ onSuccess, onError }); @@ -185,7 +180,7 @@ class MyDogeWallet { window.location.origin ); - createResponseHandler()({ + this.#createPopupResponseHandler()({ resolve, reject, onSuccess, @@ -298,7 +293,7 @@ class MyDogeWallet { * .catch(error => console.error(error)); */ requestTransaction(data, onSuccess, onError) { - return this.createPopupRequestHandler({ + return this.#createPopupRequestHandler({ requestType: MESSAGE_TYPES.CLIENT_REQUEST_TRANSACTION, responseType: MESSAGE_TYPES.CLIENT_REQUEST_TRANSACTION_RESPONSE, isDataValid: data?.recipientAddress && data?.dogeAmount, @@ -353,7 +348,7 @@ class MyDogeWallet { * .catch(error => console.error(error)); */ requestInscriptionTransaction(data, onSuccess, onError) { - return this.createPopupRequestHandler({ + return this.#createPopupRequestHandler({ requestType: MESSAGE_TYPES.CLIENT_REQUEST_DOGINAL_TRANSACTION, responseType: MESSAGE_TYPES.CLIENT_REQUEST_DOGINAL_TRANSACTION_RESPONSE, isDataValid: data?.recipientAddress && data?.location, @@ -409,7 +404,7 @@ class MyDogeWallet { * .catch(error => console.error(error)); */ requestAvailableDRC20Transaction(data, onSuccess, onError) { - return this.createPopupRequestHandler({ + return this.#createPopupRequestHandler({ requestType: MESSAGE_TYPES.CLIENT_REQUEST_AVAILABLE_DRC20_TRANSACTION, responseType: MESSAGE_TYPES.CLIENT_REQUEST_AVAILABLE_DRC20_TRANSACTION_RESPONSE, @@ -469,7 +464,7 @@ class MyDogeWallet { * .catch(error => console.error(error)); */ requestPsbt(data, onSuccess, onError) { - return this.createPopupRequestHandler({ + return this.#createPopupRequestHandler({ requestType: MESSAGE_TYPES.CLIENT_REQUEST_PSBT, responseType: MESSAGE_TYPES.CLIENT_REQUEST_PSBT_RESPONSE, isDataValid: data?.rawTx && data?.indexes?.length, @@ -526,7 +521,7 @@ class MyDogeWallet { * .catch(error => console.error(error)); */ requestSignedMessage(data, onSuccess, onError) { - return this.createPopupRequestHandler({ + return this.#createPopupRequestHandler({ requestType: MESSAGE_TYPES.CLIENT_REQUEST_SIGNED_MESSAGE, responseType: MESSAGE_TYPES.CLIENT_REQUEST_SIGNED_MESSAGE_RESPONSE, isDataValid: !!data?.message, @@ -580,7 +575,7 @@ class MyDogeWallet { * .catch(error => console.error(error)); */ requestDecryptedMessage(data, onSuccess, onError) { - return this.createPopupRequestHandler({ + return this.#createPopupRequestHandler({ requestType: MESSAGE_TYPES.CLIENT_REQUEST_DECRYPTED_MESSAGE, responseType: MESSAGE_TYPES.CLIENT_REQUEST_DECRYPTED_MESSAGE_RESPONSE, isDataValid: !!data?.message, diff --git a/views/ClientRequest/ClientAvailableDRC20Transaction.js b/views/ClientRequest/ClientAvailableDRC20Transaction.js index 37bcdf4..26df7c8 100644 --- a/views/ClientRequest/ClientAvailableDRC20Transaction.js +++ b/views/ClientRequest/ClientAvailableDRC20Transaction.js @@ -4,7 +4,7 @@ import { Button, HStack, Text, - Toast, + // Toast, VStack, } from 'native-base'; import { useCallback, useEffect, useRef, useState } from 'react'; @@ -13,26 +13,27 @@ import { FaLink } from 'react-icons/fa'; import { BigButton } from '../../components/Button'; import { ClientPopupLoading } from '../../components/ClientPopupLoading'; import { OriginBadge } from '../../components/OriginBadge'; -import { ToastRender } from '../../components/ToastRender'; +// import { ToastRender } from '../../components/ToastRender'; import { WalletAddress } from '../../components/WalletAddress'; -import { DISPATCH_TYPES } from '../../Context'; +// import { DISPATCH_TYPES } from '../../Context'; import { MESSAGE_TYPES } from '../../scripts/helpers/constants'; import { getDRC20Balances } from '../../scripts/helpers/doginals'; import { sendMessage } from '../../scripts/helpers/message'; export function ClientAvailableDRC20Transaction({ params, - dispatch, + // dispatch, connectedClient, connectedAddressIndex, - handleError, - responseMessageType, + handleResponse, + // handleError, + // responseMessageType, }) { - const handleWindowClose = useCallback(() => { - dispatch({ type: DISPATCH_TYPES.CLEAR_CLIENT_REQUEST }); - }, [dispatch]); + // const handleWindowClose = useCallback(() => { + // dispatch({ type: DISPATCH_TYPES.CLEAR_CLIENT_REQUEST }); + // }, [dispatch]); - const { origin, originTabId, ticker, amount } = params ?? {}; + const { origin, ticker, amount } = params ?? {}; const [confirmationModalOpen, setConfirmationModalOpen] = useState(false); const onCloseModal = useCallback(() => { @@ -47,29 +48,34 @@ export function ClientAvailableDRC20Transaction({ const [transaction, setTransaction] = useState(); const onRejectTransaction = useCallback(() => { - sendMessage( - { - message: responseMessageType, - data: { error: 'User refused transaction', originTabId, origin }, - }, - () => { - Toast.show({ - duration: 3000, - render: () => { - return ( - - ); - }, - }); - handleWindowClose(); - }, - [] - ); - }, [handleWindowClose, origin, originTabId, responseMessageType]); + handleResponse({ + toastMessage: `MyDoge failed to authorize the transaction to ${origin}`, + toastTitle: 'Transaction Rejected', + error: 'User refused transaction', + }); + // sendMessage( + // { + // message: responseMessageType, + // data: { error: 'User refused transaction', originTabId, origin }, + // }, + // () => { + // Toast.show({ + // duration: 3000, + // render: () => { + // return ( + // + // ); + // }, + // }); + // handleWindowClose(); + // }, + // [] + // ); + }, [handleResponse, origin]); useEffect(() => { if (!connectedClient?.address || typeof connectedAddressIndex !== 'number') @@ -82,9 +88,10 @@ export function ClientAvailableDRC20Transaction({ if (ab < amt) { setPageLoading(false); - handleError({ + handleResponse({ + toastMessage: 'Insufficient balance', + toastTitle: 'Error', error: 'Insufficient balance', - messageType: responseMessageType, }); return; } @@ -104,9 +111,10 @@ export function ClientAvailableDRC20Transaction({ if (txs?.length && fee) { setTransaction({ txs, fee }); } else { - handleError({ + handleResponse({ error: 'Unable to create available drc-20 transaction', - messageType: responseMessageType, + toastTitle: 'Error', + toastMessage: 'Unable to create transaction', }); } } @@ -116,9 +124,8 @@ export function ClientAvailableDRC20Transaction({ amount, connectedAddressIndex, connectedClient?.address, - handleError, + handleResponse, params, - responseMessageType, ticker, ]); @@ -172,9 +179,10 @@ export function ClientAvailableDRC20Transaction({ showModal={confirmationModalOpen} onClose={onCloseModal} params={params} - handleWindowClose={handleWindowClose} + // handleWindowClose={handleWindowClose} txs={transaction?.txs} - responseMessageType={responseMessageType} + // responseMessageType={responseMessageType} + handleResponse={handleResponse} /> ); @@ -184,13 +192,14 @@ const ConfirmationModal = ({ showModal, onClose, params, - handleWindowClose, - responseMessageType, + // handleWindowClose, + // responseMessageType, txs, + handleResponse, }) => { const cancelRef = useRef(); const [loading, setLoading] = useState(false); - const { origin, originTabId, ticker, amount: tokenAmount } = params; + const { origin, ticker, amount: tokenAmount } = params; const onSubmit = useCallback(async () => { setLoading(true); @@ -202,65 +211,63 @@ const ConfirmationModal = ({ (txId) => { setLoading(false); if (txId) { - sendMessage( - { - message: responseMessageType, - data: { tokenAmount, ticker, txId, originTabId, origin }, - }, - () => { - onClose(); - Toast.show({ - duration: 3000, - render: () => { - return ( - - ); - }, - }); - handleWindowClose(); - } - ); - } else { - sendMessage({ - message: responseMessageType, - data: { - error: 'Failed to inscribe token transfer', - originTabId, - origin, - }, + handleResponse({ + toastMessage: 'Transaction Sent', + toastTitle: 'Success', + data: { tokenAmount, ticker, txId }, }); - Toast.show({ - title: 'Error', - description: 'Transaction Failed', - duration: 3000, - render: () => { - return ( - - ); - }, + // sendMessage( + // { + // message: responseMessageType, + // data: { tokenAmount, ticker, txId, originTabId, origin }, + // }, + // () => { + // onClose(); + // Toast.show({ + // duration: 3000, + // render: () => { + // return ( + // + // ); + // }, + // }); + // handleWindowClose(); + // } + // ); + } else { + handleResponse({ + toastMessage: 'Failed to inscribe token transfer', + toastTitle: 'Error', + error: 'Failed to inscribe token transfer', }); - handleWindowClose(); + // data: { + // error: 'Failed to inscribe token transfer', + // originTabId, + // origin, + // }, + // }); + // Toast.show({ + // title: 'Error', + // description: 'Transaction Failed', + // duration: 3000, + // render: () => { + // return ( + // + // ); + // }, + // }); + // handleWindowClose(); } } ); - }, [ - handleWindowClose, - onClose, - origin, - originTabId, - params, - responseMessageType, - ticker, - tokenAmount, - txs, - ]); + }, [handleResponse, params, ticker, tokenAmount, txs]); return ( <> diff --git a/views/ClientRequest/ClientConnect.js b/views/ClientRequest/ClientConnect.js index 35f67e7..47e147c 100644 --- a/views/ClientRequest/ClientConnect.js +++ b/views/ClientRequest/ClientConnect.js @@ -7,7 +7,7 @@ import { HStack, Pressable, Text, - Toast, + // Toast, VStack, } from 'native-base'; import { useCallback, useRef, useState } from 'react'; @@ -17,50 +17,51 @@ import sb from 'satoshi-bitcoin'; import { BigButton } from '../../components/Button'; import { OriginBadge } from '../../components/OriginBadge'; -import { ToastRender } from '../../components/ToastRender'; -import { DISPATCH_TYPES } from '../../Context'; +// import { ToastRender } from '../../components/ToastRender'; +// import { DISPATCH_TYPES } from '../../Context'; import { useInterval } from '../../hooks/useInterval'; import { MESSAGE_TYPES } from '../../scripts/helpers/constants'; import { sendMessage } from '../../scripts/helpers/message'; const REFRESH_INTERVAL = 10000; -export function ClientConnect({ - params, - wallet, - dispatch, - responseMessageType, -}) { +export function ClientConnect({ params, wallet, handleResponse }) { const { origin, originTabId } = params ?? {}; - const handleWindowClose = useCallback(() => { - dispatch({ type: DISPATCH_TYPES.CLEAR_CLIENT_REQUEST }); - }, [dispatch]); + // const handleWindowClose = useCallback(() => { + // dispatch({ type: DISPATCH_TYPES.CLEAR_CLIENT_REQUEST }); + // }, [dispatch]); const onRejectConnection = useCallback(() => { - sendMessage( - { - message: responseMessageType, - data: { approved: false, originTabId, origin }, - }, - () => { - Toast.show({ - duration: 3000, - render: () => { - return ( - - ); - }, - }); - handleWindowClose(); - }, - [] - ); - }, [handleWindowClose, origin, originTabId, responseMessageType]); + handleResponse({ + toastMessage: `MyDoge failed to connected to ${origin}`, + toastTitle: 'Connection Failed', + error: 'Unable to connect to MyDoge', + // data: { approved: false, originTabId, origin }, + }); + // sendMessage( + // { + // message: responseMessageType, + // data: { approved: false, originTabId, origin }, + // }, + // () => { + // Toast.show({ + // duration: 3000, + // render: () => { + // return ( + // + // ); + // }, + // }); + // handleWindowClose(); + // }, + // [] + // ); + }, [handleResponse, origin, originTabId]); const [addressBalances, setAddressBalances] = useState({}); const [selectedAddressIndex, setSelectedAddressIndex] = useState(0); @@ -182,12 +183,13 @@ export function ClientConnect({ ); @@ -198,54 +200,57 @@ const ConfirmationModal = ({ onClose, selectedAddress, selectedAddressIndex, - origin, - originTabId, + // origin, + // originTabId, balance, - handleWindowClose, + handleResponse, + // handleWindowClose, }) => { const cancelRef = useRef(); const onConnect = useCallback(() => { - sendMessage( - { - message: MESSAGE_TYPES.CLIENT_REQUEST_CONNECTION_RESPONSE, - data: { - approved: true, - address: selectedAddress, - selectedAddressIndex, - balance, - originTabId, - origin, - }, - }, - (response) => { - if (response) { - onClose?.(); - Toast.show({ - duration: 3000, - render: () => { - return ( - - ); - }, - }); - handleWindowClose(); - } + handleResponse({ + toastMessage: `MyDoge has connected to ${origin}`, + toastTitle: 'Connection Success', + data: { + approved: true, + address: selectedAddress, + selectedAddressIndex, + balance, }, - [] - ); - }, [ - selectedAddress, - selectedAddressIndex, - balance, - originTabId, - origin, - onClose, - handleWindowClose, - ]); + }); + // sendMessage( + // { + // message: MESSAGE_TYPES.CLIENT_REQUEST_CONNECTION_RESPONSE, + // data: { + // approved: true, + // address: selectedAddress, + // selectedAddressIndex, + // balance, + // originTabId, + // origin, + // }, + // }, + // (response) => { + // if (response) { + // onClose?.(); + // Toast.show({ + // duration: 3000, + // render: () => { + // return ( + // + // ); + // }, + // }); + // handleWindowClose(); + // } + // }, + // [] + // ); + }, [handleResponse, selectedAddress, selectedAddressIndex, balance]); return ( { - dispatch({ type: DISPATCH_TYPES.CLEAR_CLIENT_REQUEST }); - }, [dispatch]); + // const handleWindowClose = useCallback(() => { + // dispatch({ type: DISPATCH_TYPES.CLEAR_CLIENT_REQUEST }); + // }, [dispatch]); const [confirmationModalOpen, setConfirmationModalOpen] = useState(false); const onCloseModal = useCallback(() => { @@ -40,29 +41,34 @@ export function ClientDecryptedMessage({ }, []); const onRejectTransaction = useCallback(() => { - sendMessage( - { - message: responseMessageType, - data: { error: 'User refused decrypted message', originTabId, origin }, - }, - () => { - Toast.show({ - duration: 3000, - render: () => { - return ( - - ); - }, - }); - handleWindowClose(); - }, - [] - ); - }, [handleWindowClose, origin, originTabId, responseMessageType]); + handleResponse({ + toastMessage: 'Message Rejected', + toastTitle: 'Error', + error: 'User refused decrypted message', + }); + // sendMessage( + // { + // message: responseMessageType, + // data: { error: 'User refused decrypted message', originTabId, origin }, + // }, + // () => { + // Toast.show({ + // duration: 3000, + // render: () => { + // return ( + // + // ); + // }, + // }); + // handleWindowClose(); + // }, + // [] + // ); + }, [handleResponse, origin, originTabId]); return ( <> @@ -111,8 +117,9 @@ export function ClientDecryptedMessage({ originTabId={originTabId} message={message} addressIndex={addressIndex} - handleWindowClose={handleWindowClose} - responseMessageType={responseMessageType} + handleResponse={handleResponse} + // handleWindowClose={handleWindowClose} + // responseMessageType={responseMessageType} /> ); @@ -124,13 +131,19 @@ const ConfirmationModal = ({ origin, message, addressIndex, - originTabId, - handleWindowClose, - responseMessageType, + // originTabId, + // handleWindowClose, + // responseMessageType, + handleResponse, }) => { const cancelRef = useRef(); const [loading, setLoading] = useState(false); + const handleClose = useCallback(() => { + setLoading(false); + onClose(); + }, [onClose]); + const onSubmit = useCallback(async () => { setLoading(true); sendMessage( @@ -140,65 +153,69 @@ const ConfirmationModal = ({ }, (decryptedMessage) => { if (decryptedMessage) { - sendMessage( - { - message: responseMessageType, - data: { decryptedMessage, originTabId, origin }, - }, - () => { - Toast.show({ - duration: 3000, - render: () => { - return ( - - ); - }, - }); - handleWindowClose(); - } - ); + handleResponse({ + toastMessage: 'Message Decrypted Successfully', + toastTitle: 'Success', + data: { decryptedMessage }, + }); + // sendMessage( + // { + // message: responseMessageType, + // data: { decryptedMessage, originTabId, origin }, + // }, + // () => { + // Toast.show({ + // duration: 3000, + // render: () => { + // return ( + // + // ); + // }, + // }); + // handleWindowClose(); + // } + // ); } else { - sendMessage( - { - message: responseMessageType, - data: { - error: 'Failed to decrypt message', - originTabId, - origin, - }, - }, - () => { - Toast.show({ - title: 'Error', - description: 'Message Decrypting Failed', - duration: 3000, - render: () => { - return ( - - ); - }, - }); - handleWindowClose(); - } - ); + handleResponse({ + toastMessage: 'Message Decrypting Failed', + toastTitle: 'Error', + error: 'Failed to decrypt message', + }); + // sendMessage( + // { + // message: responseMessageType, + // data: { + // error: 'Failed to decrypt message', + // originTabId, + // origin, + // }, + // }, + // () => { + // Toast.show({ + // title: 'Error', + // description: 'Message Decrypting Failed', + // duration: 3000, + // render: () => { + // return ( + // + // ); + // }, + // }); + // handleWindowClose(); + // } + // ); } } ); - }, [ - addressIndex, - handleWindowClose, - origin, - originTabId, - message, - responseMessageType, - ]); + }, [message, addressIndex, handleResponse]); + return ( <> @@ -210,7 +227,7 @@ const ConfirmationModal = ({ @@ -229,7 +246,7 @@ const ConfirmationModal = ({
On this page

MyDogeWallet

Class representing the MyDoge API to interact with the Dogecoin wallet.

Constructor

new MyDogeWallet()

Methods

(async) connect(onSuccessopt, onErroropt) → {Promise.<{approved: boolean, address: string, publicKey: string, balance: number}>}

Initiates a connection request with the wallet.
Parameters:
NameTypeAttributesDescription
onSuccessfunction<optional>
Optional callback function to execute upon successful connection. Receives an object containing the wallet address and balance.
onErrorfunction<optional>
Optional callback function to execute upon connection error.
Returns:
Promise object representing the outcome of the connection attempt, resolving to an object with the connected address information.
Type: 
Promise.<{approved: boolean, address: string, publicKey: string, balance: number}>
Example
connect(
+    
On this page

MyDogeWallet

Class representing the MyDoge API to interact with the Dogecoin wallet.

Constructor

new MyDogeWallet()

Methods

(async) connect(onSuccessopt, onErroropt) → {Promise.<{approved: boolean, address: string, publicKey: string, balance: number}>}

Initiates a connection request with the wallet.
Parameters:
NameTypeAttributesDescription
onSuccessfunction<optional>
Optional callback function to execute upon successful connection. Receives an object containing the wallet address and balance.
onErrorfunction<optional>
Optional callback function to execute upon connection error.
Returns:
Promise object representing the outcome of the connection attempt, resolving to an object with the connected address information.
Type: 
Promise.<{approved: boolean, address: string, publicKey: string, balance: number}>
Example
connect(
   (result) => console.log(`Connected to wallet: ${result.address}`),
   (error) => console.error(`Connection failed: ${error}`)
 ).then(result => console.log(result.address))
-  .catch(error => console.error(error));

(async) disconnect(onSuccessopt, onErroropt) → {Promise.<void>}

Disconnects the current session with the wallet.
Parameters:
NameTypeAttributesDescription
onSuccessfunction<optional>
Optional callback function to execute upon successful disconnection.
onErrorfunction<optional>
Optional callback function to execute upon error in disconnecting.
Returns:
Promise object representing the disconnection outcome.
Type: 
Promise.<void>
Example
disconnect(
+  .catch(error => console.error(error));

(async) disconnect(onSuccessopt, onErroropt) → {Promise.<void>}

Disconnects the current session with the wallet.
Parameters:
NameTypeAttributesDescription
onSuccessfunction<optional>
Optional callback function to execute upon successful disconnection.
onErrorfunction<optional>
Optional callback function to execute upon error in disconnecting.
Returns:
Promise object representing the disconnection outcome.
Type: 
Promise.<void>
Example
disconnect(
   () => console.log(`Disconnected from wallet`),
   (error) => console.error(`Disconnection failed: ${error}`)
 ).then(() => console.log('Disconnected from wallet'))
-  .catch(error => console.error(error));

(async) getBalance(onSuccessopt, onErroropt) → {Promise.<{address: string, balance: number}>}

Retrieves the balance from the connected wallet.
Parameters:
NameTypeAttributesDescription
onSuccessfunction<optional>
Optional callback function to execute upon successful retrieval of balance. Receives an object containing the wallet address and balance.
onErrorfunction<optional>
Optional callback function to execute upon error in retrieving balance.
Returns:
Promise object representing the outcome of the balance retrieval, resolving to an object with the wallet address and balance.
Type: 
Promise.<{address: string, balance: number}>
Example
getBalance(
+  .catch(error => console.error(error));

(async) getBalance(onSuccessopt, onErroropt) → {Promise.<{address: string, balance: number}>}

Retrieves the balance from the connected wallet.
Parameters:
NameTypeAttributesDescription
onSuccessfunction<optional>
Optional callback function to execute upon successful retrieval of balance. Receives an object containing the wallet address and balance.
onErrorfunction<optional>
Optional callback function to execute upon error in retrieving balance.
Returns:
Promise object representing the outcome of the balance retrieval, resolving to an object with the wallet address and balance.
Type: 
Promise.<{address: string, balance: number}>
Example
getBalance(
   (result) => console.log(`Connected to wallet: ${result.balance}`),
   (error) => console.error(`Connection failed: ${error}`)
 ).then(result => console.log(result.balance))
-  .catch(error => console.error(error));

(async) getConnectionStatus(onSuccessopt, onErroropt) → {Promise.<{connected: boolean, address: string, selectedWalletAddress: string}>}

Retrieves the connection status with the wallet.
Parameters:
NameTypeAttributesDescription
onSuccessfunction<optional>
Optional callback function to execute upon successfully retrieving the status. Receives an object containing the wallet address, selected wallet address, and connection status.
onErrorfunction<optional>
Optional callback function to execute upon error in retrieving the connection status.
Returns:
Promise object representing the outcome of the connection status retrieval, resolving to an object with the wallet address, selected wallet address, and connection status.
Type: 
Promise.<{connected: boolean, address: string, selectedWalletAddress: string}>
Example
getConnectionStatus(
+  .catch(error => console.error(error));

(async) getConnectionStatus(onSuccessopt, onErroropt) → {Promise.<{connected: boolean, address: string, selectedWalletAddress: string}>}

Retrieves the connection status with the wallet.
Parameters:
NameTypeAttributesDescription
onSuccessfunction<optional>
Optional callback function to execute upon successfully retrieving the status. Receives an object containing the wallet address, selected wallet address, and connection status.
onErrorfunction<optional>
Optional callback function to execute upon error in retrieving the connection status.
Returns:
Promise object representing the outcome of the connection status retrieval, resolving to an object with the wallet address, selected wallet address, and connection status.
Type: 
Promise.<{connected: boolean, address: string, selectedWalletAddress: string}>
Example
getConnectionStatus(
   (result) => console.log(`Connected to wallet: ${result.connected}`),
   (error) => console.error(`Connection status retrieval failed: ${error}`)
 ).then(result => console.log(result.connected))
-  .catch(error => console.error(error));

(async) getDRC20Balance(data, onSuccessopt, onErroropt) → {Promise.<{availableBalance: number, transferableBalance: number, ticker: string, address: string}>}

Retrieves the DRC20 token balance based on provided data.
Parameters:
NameTypeAttributesDescription
dataObjectData required to fetch the DRC20 balance, must contain 'ticker'.
Properties
NameTypeDescription
tickerstringThe ticker symbol for the DRC20 token.
onSuccessfunction<optional>
Optional callback function to execute upon successful retrieval. Receives an object containing the available balance, transferable balance, ticker symbol, and wallet address.
onErrorfunction<optional>
Optional callback function to execute upon error in retrieving balance.
Returns:
Promise object representing the outcome of the balance retrieval, resolving to an object with the wallet address, available balance, and transferable balance.
Type: 
Promise.<{availableBalance: number, transferableBalance: number, ticker: string, address: string}>
Example
getDRC20Balance(
+  .catch(error => console.error(error));

(async) getDRC20Balance(data, onSuccessopt, onErroropt) → {Promise.<{availableBalance: number, transferableBalance: number, ticker: string, address: string}>}

Retrieves the DRC20 token balance based on provided data.
Parameters:
NameTypeAttributesDescription
dataObjectData required to fetch the DRC20 balance, must contain 'ticker'.
Properties
NameTypeDescription
tickerstringThe ticker symbol for the DRC20 token.
onSuccessfunction<optional>
Optional callback function to execute upon successful retrieval. Receives an object containing the available balance, transferable balance, ticker symbol, and wallet address.
onErrorfunction<optional>
Optional callback function to execute upon error in retrieving balance.
Returns:
Promise object representing the outcome of the balance retrieval, resolving to an object with the wallet address, available balance, and transferable balance.
Type: 
Promise.<{availableBalance: number, transferableBalance: number, ticker: string, address: string}>
Example
getDRC20Balance(
   (result) => console.log(`Available balance: ${result.availableBalance}, transferable balance: ${result.transferableBalance}`),
   (error) => console.error(`Balance retrieval failed: ${error}`)
 ).then(result => console.log(result.availableBalance))
-  .catch(error => console.error(error));

(async) getTransactionStatus(onSuccessopt, onErroropt) → {Promise.<{connected: boolean, address: string, connectedWalletAddress: string}>}

Retrieves the status of a specific transaction based on provided data.
Parameters:
NameTypeAttributesDescription
onSuccessfunction<optional>
Optional callback function to execute upon successfully retrieving the status. Receives an object containing the wallet address, selected wallet address, and connection status.
onErrorfunction<optional>
Optional callback function to execute upon error in retrieving the connection status.
Returns:
Promise object representing the outcome of the connection status retrieval, resolving to an object with the wallet address, selected wallet address, and connection status.
Type: 
Promise.<{connected: boolean, address: string, connectedWalletAddress: string}>
Example
getConnectionStatus(
+  .catch(error => console.error(error));

(async) getTransactionStatus(onSuccessopt, onErroropt) → {Promise.<{connected: boolean, address: string, connectedWalletAddress: string}>}

Retrieves the status of a specific transaction based on provided data.
Parameters:
NameTypeAttributesDescription
onSuccessfunction<optional>
Optional callback function to execute upon successfully retrieving the status. Receives an object containing the wallet address, selected wallet address, and connection status.
onErrorfunction<optional>
Optional callback function to execute upon error in retrieving the connection status.
Returns:
Promise object representing the outcome of the connection status retrieval, resolving to an object with the wallet address, selected wallet address, and connection status.
Type: 
Promise.<{connected: boolean, address: string, connectedWalletAddress: string}>
Example
getConnectionStatus(
   (result) => console.log(`Connected to wallet: ${result.connected}`),
   (error) => console.error(`Connection status retrieval failed: ${error}`)
 ).then(result => console.log(result.connected))
-  .catch(error => console.error(error));

(async) getTransferableDRC20(data, onSuccessopt, onErroropt) → {Promise.<{inscriptions: Array.<{txid: string, vout: number, ticker: string, contentType: string, content: string, output: string, amount: number}>, ticker: string, address: string}>}

Retrieves transferable DRC20 inscriptions based on provided data.
Parameters:
NameTypeAttributesDescription
dataObjectData required for the query, must contain 'ticker'.
Properties
NameTypeDescription
tickerstringThe ticker symbol for the DRC20 token.
onSuccessfunction<optional>
Optional callback function to execute upon successful retrieval. Receives an object containing the transferable inscriptions, ticker symbol, and wallet address.
onErrorfunction<optional>
Optional callback function to execute upon error in fetching the transferable balance.
Returns:
Promise object representing the outcome of the balance retrieval, resolving to an object with the wallet address, transferable inscriptions, and ticker symbol.}
Type: 
Promise.<{inscriptions: Array.<{txid: string, vout: number, ticker: string, contentType: string, content: string, output: string, amount: number}>, ticker: string, address: string}>
Example
getTransferableDRC20(
+  .catch(error => console.error(error));

(async) getTransferableDRC20(data, onSuccessopt, onErroropt) → {Promise.<{inscriptions: Array.<{txid: string, vout: number, ticker: string, contentType: string, content: string, output: string, amount: number}>, ticker: string, address: string}>}

Retrieves transferable DRC20 inscriptions based on provided data.
Parameters:
NameTypeAttributesDescription
dataObjectData required for the query, must contain 'ticker'.
Properties
NameTypeDescription
tickerstringThe ticker symbol for the DRC20 token.
onSuccessfunction<optional>
Optional callback function to execute upon successful retrieval. Receives an object containing the transferable inscriptions, ticker symbol, and wallet address.
onErrorfunction<optional>
Optional callback function to execute upon error in fetching the transferable balance.
Returns:
Promise object representing the outcome of the balance retrieval, resolving to an object with the wallet address, transferable inscriptions, and ticker symbol.}
Type: 
Promise.<{inscriptions: Array.<{txid: string, vout: number, ticker: string, contentType: string, content: string, output: string, amount: number}>, ticker: string, address: string}>
Example
getTransferableDRC20(
   (result) => console.log(`Transferable inscriptions: ${result.inscriptions}`),
   (error) => console.error(`Balance retrieval failed: ${error}`)
 ).then(result => console.log(result.inscriptions))
-  .catch(error => console.error(error));

(async) requestAvailableDRC20Transaction(data, onSuccessopt, onErroropt) → {Promise.<{txId: string, ticker: string, amount: number}>}

Requests a transaction for available DRC20 tokens based on specified data.
Parameters:
NameTypeAttributesDescription
dataObjectData required for the transaction, must contain 'ticker' and 'amount'.
Properties
NameTypeDescription
tickerstringThe ticker symbol for the DRC20 token.
amountstringThe amount of DRC20 tokens to make available.
onSuccessfunction<optional>
Optional callback function to execute upon successful transaction request. Receives an object containing the transaction ID, ticker symbol, and amount.
onErrorfunction<optional>
Optional callback function to execute upon error in processing the transaction request.
Returns:
Promise object representing the outcome of the transaction request, resolving to an object with the transaction ID, ticker symbol, and amount.
Type: 
Promise.<{txId: string, ticker: string, amount: number}>
Example
requestInscriptionTransaction(
+  .catch(error => console.error(error));

(async) requestAvailableDRC20Transaction(data, onSuccessopt, onErroropt) → {Promise.<{txId: string, ticker: string, amount: number}>}

Requests a transaction for available DRC20 tokens based on specified data.
Parameters:
NameTypeAttributesDescription
dataObjectData required for the transaction, must contain 'ticker' and 'amount'.
Properties
NameTypeDescription
tickerstringThe ticker symbol for the DRC20 token.
amountstringThe amount of DRC20 tokens to make available.
onSuccessfunction<optional>
Optional callback function to execute upon successful transaction request. Receives an object containing the transaction ID, ticker symbol, and amount.
onErrorfunction<optional>
Optional callback function to execute upon error in processing the transaction request.
Returns:
Promise object representing the outcome of the transaction request, resolving to an object with the transaction ID, ticker symbol, and amount.
Type: 
Promise.<{txId: string, ticker: string, amount: number}>
Example
requestInscriptionTransaction(
   (result) => console.log(`Transaction ID: ${result.txId} `),
   (error) => console.error(`Transaction request failed: ${error}`)
 ).then(result => console.log(result.txId))
-  .catch(error => console.error(error));

(async) requestDecryptedMessage(data, onSuccessopt, onErroropt) → {Promise.<{decryptedMessage: string}>}

Requests the decrypting of an arbitrary message encrypted by the connected address public key.
Parameters:
NameTypeAttributesDescription
dataObjectData required for the decryption, must contain 'message'.
Properties
NameTypeDescription
messagestringThe message to be decrypted.
onSuccessfunction<optional>
Optional callback function to execute upon successful message signing. Receives an object containing the decrypted message.
onErrorfunction<optional>
Callback function to execute upon error in decrypting the message.
Returns:
Promise object representing the outcome of the request, resolving to an object with the decrypted message.
Type: 
Promise.<{decryptedMessage: string}>
Example
requestDecryptedMessage(
+  .catch(error => console.error(error));

(async) requestDecryptedMessage(data, onSuccessopt, onErroropt) → {Promise.<{decryptedMessage: string}>}

Requests the decrypting of an arbitrary message encrypted by the connected address public key.
Parameters:
NameTypeAttributesDescription
dataObjectData required for the decryption, must contain 'message'.
Properties
NameTypeDescription
messagestringThe message to be decrypted.
onSuccessfunction<optional>
Optional callback function to execute upon successful message signing. Receives an object containing the decrypted message.
onErrorfunction<optional>
Callback function to execute upon error in decrypting the message.
Returns:
Promise object representing the outcome of the request, resolving to an object with the decrypted message.
Type: 
Promise.<{decryptedMessage: string}>
Example
requestDecryptedMessage(
   (result) => console.log(`Decrypted message: ${result.decryptedMessage}`),
   (error) => console.error(`Message decryption failed: ${error}`)
 ).then(result => console.log(result.decryptedMessage))
-  .catch(error => console.error(error));

(async) requestInscriptionTransaction(data, onSuccessopt, onErroropt) → {Promise.<{txId: string}>}

Requests an inscription transaction for Doginal/DRC-20 based on the specified data.
Parameters:
NameTypeAttributesDescription
dataObjectData required for the transaction, must contain 'recipientAddress' and 'output'.
Properties
NameTypeDescription
recipientAddressstringThe recipient address.
locationstringThe location of the inscription in the format txid:vout:offset.
onSuccessfunction<optional>
Optional callback function to execute upon successful transaction request. Receives an object containing the transaction ID.
onErrorfunction<optional>
Optional function to execute upon error in processing the transaction request.
Returns:
Promise object representing the outcome of the transaction request, resolving to an object with the transaction ID.
Type: 
Promise.<{txId: string}>
Example
requestInscriptionTransaction(
+  .catch(error => console.error(error));

(async) requestInscriptionTransaction(data, onSuccessopt, onErroropt) → {Promise.<{txId: string}>}

Requests an inscription transaction for Doginal/DRC-20 based on the specified data.
Parameters:
NameTypeAttributesDescription
dataObjectData required for the transaction, must contain 'recipientAddress' and 'output'.
Properties
NameTypeDescription
recipientAddressstringThe recipient address.
locationstringThe location of the inscription in the format txid:vout:offset.
onSuccessfunction<optional>
Optional callback function to execute upon successful transaction request. Receives an object containing the transaction ID.
onErrorfunction<optional>
Optional function to execute upon error in processing the transaction request.
Returns:
Promise object representing the outcome of the transaction request, resolving to an object with the transaction ID.
Type: 
Promise.<{txId: string}>
Example
requestInscriptionTransaction(
   (result) => console.log(`Transaction ID: ${result.txId}`),
   (error) => console.error(`Transaction request failed: ${error}`)
 ).then(result => console.log(result.txId))
@@ -44,11 +44,11 @@
   (result) => console.log(`Transaction ID: ${result.txId}`),
   (error) => console.error(`Transaction request failed: ${error}`)
 ).then(result => console.log(result.txId))
-  .catch(error => console.error(error));

(async) requestSignedMessage(data, onSuccessopt, onErroropt) → {Promise.<{signedMessage: string}>}

Requests the signing of an arbitrary message based on provided data.
Parameters:
NameTypeAttributesDescription
dataObjectData required for the message signing, must contain 'message'.
Properties
NameTypeDescription
messagestringThe message to be signed.
onSuccessfunction<optional>
Optional callback function to execute upon successful message signing. Receives an object containing the signed message.
onErrorfunction<optional>
Callback function to execute upon error in signing the message.
Returns:
Promise object representing the outcome of the request, resolving to an object with the base64 signed message.
Type: 
Promise.<{signedMessage: string}>
Example
requestSignedMessage(
+  .catch(error => console.error(error));

(async) requestSignedMessage(data, onSuccessopt, onErroropt) → {Promise.<{signedMessage: string}>}

Requests the signing of an arbitrary message based on provided data.
Parameters:
NameTypeAttributesDescription
dataObjectData required for the message signing, must contain 'message'.
Properties
NameTypeDescription
messagestringThe message to be signed.
onSuccessfunction<optional>
Optional callback function to execute upon successful message signing. Receives an object containing the signed message.
onErrorfunction<optional>
Callback function to execute upon error in signing the message.
Returns:
Promise object representing the outcome of the request, resolving to an object with the base64 signed message.
Type: 
Promise.<{signedMessage: string}>
Example
requestSignedMessage(
   (result) => console.log(`Signed message: ${result.signedMessage}`),
   (error) => console.error(`Message signing failed: ${error}`)
 ).then(result => console.log(result.signedMessage))
-  .catch(error => console.error(error));

(async) requestTransaction(data, onSuccessopt, onErroropt) → {Promise.<{txId: string}>}

Requests a Dogecoin transaction based on the specified data.
Parameters:
NameTypeAttributesDescription
dataObjectData needed for the transaction, must contain 'recipientAddress' and 'dogeAmount'.
Properties
NameTypeDescription
recipientAddressstringThe recipient address.
dogeAmountnumberThe amount of Dogecoin to send.
onSuccessfunction<optional>
Optional callback function to execute upon successful transaction request. Receives an object containing the transaction ID.
onErrorfunction<optional>
Optional callback function to execute upon error in processing the transaction request.
Returns:
Promise object representing the outcome of the transaction request, resolving to an object with the transaction ID.
Type: 
Promise.<{txId: string}>
Example
requestTransaction(
+  .catch(error => console.error(error));

(async) requestTransaction(data, onSuccessopt, onErroropt) → {Promise.<{txId: string}>}

Requests a Dogecoin transaction based on the specified data.
Parameters:
NameTypeAttributesDescription
dataObjectData needed for the transaction, must contain 'recipientAddress' and 'dogeAmount'.
Properties
NameTypeDescription
recipientAddressstringThe recipient address.
dogeAmountnumberThe amount of Dogecoin to send.
onSuccessfunction<optional>
Optional callback function to execute upon successful transaction request. Receives an object containing the transaction ID.
onErrorfunction<optional>
Optional callback function to execute upon error in processing the transaction request.
Returns:
Promise object representing the outcome of the transaction request, resolving to an object with the transaction ID.
Type: 
Promise.<{txId: string}>
Example
requestTransaction(
   (result) => console.log(`Transaction ID: ${result.txId}`),
   (error) => console.error(`Transaction request failed: ${error}`)
 ).then(result => console.log(result.txId))
diff --git a/docs/inject-script.js.html b/docs/inject-script.js.html
index ec173bb..4046533 100644
--- a/docs/inject-script.js.html
+++ b/docs/inject-script.js.html
@@ -4,13 +4,11 @@
 
 const createResponseHandler =
   () =>
-  ({ resolve, reject, onSuccess, onError, messageType, setRequestPending }) => {
+  ({ resolve, reject, onSuccess, onError, messageType }) => {
     function listener({ data: { type, data, error }, origin }) {
       // only accept messages from the same origin and message type of this context
       if (origin !== window.location.origin || type !== messageType) return;
 
-      setRequestPending?.(false);
-
       if (error) {
         onError?.(new Error(error));
         reject(new Error(error));
@@ -18,8 +16,6 @@
         onSuccess?.(data);
         resolve(data);
       } else {
-        onError?.(new Error('Unable to connect to MyDoge'));
-        reject(new Error('Unable to connect to MyDoge'));
       }
       window.removeEventListener('message', listener);
     }
@@ -30,12 +26,98 @@
  * Class representing the MyDoge API to interact with the Dogecoin wallet.
  */
 class MyDogeWallet {
+  #requestQueue = [];
+  #isRequestPending = false;
   constructor() {
     this.isMyDoge = true;
-    this.isRequestPending = false;
     console.info('MyDoge API initialized');
   }
 
+  #createPopupRequestHandler({ requestType, responseType, isDataValid }) {
+    return ({ data, onSuccess, onError }) => {
+      return new Promise((resolve, reject) => {
+        if (data && !isDataValid) {
+          onError?.(new Error('Invalid data'));
+          reject(new Error('Invalid data'));
+          return;
+        }
+        this.#requestQueue.push({
+          onSuccess,
+          onError,
+          requestType,
+          responseType,
+          resolve,
+          reject,
+          data,
+        });
+        if (!this.#isRequestPending) {
+          this.#processNextRequest();
+        }
+      });
+    };
+  }
+
+  #createPopupResponseHandler() {
+    return ({ resolve, reject, onSuccess, onError, responseType }) => {
+      const listener = ({ data: { type, data, error }, origin }) => {
+        // only accept messages from the same origin and message type of this context
+        if (origin !== window.location.origin || type !== responseType) return;
+
+        if (error) {
+          onError?.(new Error(error));
+          reject(new Error(error));
+        } else if (data) {
+          onSuccess?.(data);
+          resolve(data);
+        }
+        // process next request after popup has closed
+        setTimeout(() => {
+          this.#requestQueue.shift();
+          this.#processNextRequest();
+          window.removeEventListener('message', listener);
+        }, 500);
+      };
+      window.addEventListener('message', listener);
+    };
+  }
+
+  #handleRequest({ requestType, data }) {
+    window.postMessage({ type: requestType, data }, window.location.origin);
+  }
+
+  #handlePopupResponse({ resolve, reject, onSuccess, onError, responseType }) {
+    const popupResponseHandler = this.#createPopupResponseHandler();
+    popupResponseHandler({ resolve, reject, onSuccess, onError, responseType });
+  }
+
+  #processNextRequest() {
+    if (this.#requestQueue.length === 0) {
+      this.#isRequestPending = false;
+      return;
+    }
+    this.#isRequestPending = true;
+
+    const {
+      data,
+      resolve,
+      reject,
+      onSuccess,
+      onError,
+      requestType,
+      responseType,
+    } = this.#requestQueue[0];
+
+    this.#handleRequest({ requestType, data });
+
+    this.#handlePopupResponse({
+      resolve,
+      reject,
+      onSuccess,
+      onError,
+      responseType,
+    });
+  }
+
   /**
    * Initiates a connection request with the wallet.
    * @function
@@ -53,20 +135,10 @@
    *   .catch(error => console.error(error));
    */
   connect(onSuccess, onError) {
-    return new Promise((resolve, reject) => {
-      window.postMessage(
-        { type: MESSAGE_TYPES.CLIENT_REQUEST_CONNECTION },
-        window.location.origin
-      );
-
-      createResponseHandler()({
-        resolve,
-        reject,
-        onSuccess,
-        onError,
-        messageType: MESSAGE_TYPES.CLIENT_REQUEST_CONNECTION_RESPONSE,
-      });
-    });
+    return this.#createPopupRequestHandler({
+      requestType: MESSAGE_TYPES.CLIENT_REQUEST_CONNECTION,
+      responseType: MESSAGE_TYPES.CLIENT_REQUEST_CONNECTION_RESPONSE,
+    })({ onSuccess, onError });
   }
 
   /**
@@ -92,7 +164,7 @@
         window.location.origin
       );
 
-      createResponseHandler()({
+      this.#createPopupResponseHandler()({
         resolve,
         reject,
         onSuccess,
@@ -205,34 +277,11 @@
    *   .catch(error => console.error(error));
    */
   requestTransaction(data, onSuccess, onError) {
-    return new Promise((resolve, reject) => {
-      if (!data?.recipientAddress || !data?.dogeAmount) {
-        onError?.(new Error('Invalid data'));
-        reject(new Error('Invalid data'));
-        return;
-      }
-      if (this.isRequestPending) {
-        onError?.(new Error('There is a pending request'));
-        reject(new Error('There is a pending request'));
-        return;
-      }
-      this.isRequestPending = true;
-      window.postMessage(
-        { type: MESSAGE_TYPES.CLIENT_REQUEST_TRANSACTION, data },
-        window.location.origin
-      );
-
-      createResponseHandler()({
-        resolve,
-        reject,
-        onSuccess,
-        onError,
-        messageType: MESSAGE_TYPES.CLIENT_REQUEST_TRANSACTION_RESPONSE,
-        setRequestPending: (isRequestPending) => {
-          this.isRequestPending = isRequestPending;
-        },
-      });
-    });
+    return this.#createPopupRequestHandler({
+      requestType: MESSAGE_TYPES.CLIENT_REQUEST_TRANSACTION,
+      responseType: MESSAGE_TYPES.CLIENT_REQUEST_TRANSACTION_RESPONSE,
+      isDataValid: data?.recipientAddress && data?.dogeAmount,
+    })({ data, onSuccess, onError });
   }
 
   /**
@@ -255,34 +304,11 @@
    *   .catch(error => console.error(error));
    */
   requestInscriptionTransaction(data, onSuccess, onError) {
-    return new Promise((resolve, reject) => {
-      if (!data?.recipientAddress || !data?.location) {
-        onError?.(new Error('Invalid data'));
-        reject(new Error('Invalid data'));
-        return;
-      }
-      if (this.isRequestPending) {
-        onError?.(new Error('There is a pending request'));
-        reject(new Error('There is a pending request'));
-        return;
-      }
-      this.isRequestPending = true;
-      window.postMessage(
-        { type: MESSAGE_TYPES.CLIENT_REQUEST_DOGINAL_TRANSACTION, data },
-        window.location.origin
-      );
-
-      createResponseHandler()({
-        resolve,
-        reject,
-        onSuccess,
-        onError,
-        messageType: MESSAGE_TYPES.CLIENT_REQUEST_DOGINAL_TRANSACTION_RESPONSE,
-        setRequestPending: (isRequestPending) => {
-          this.isRequestPending = isRequestPending;
-        },
-      });
-    });
+    return this.#createPopupRequestHandler({
+      requestType: MESSAGE_TYPES.CLIENT_REQUEST_DOGINAL_TRANSACTION,
+      responseType: MESSAGE_TYPES.CLIENT_REQUEST_DOGINAL_TRANSACTION_RESPONSE,
+      isDataValid: data?.recipientAddress && data?.location,
+    })({ data, onSuccess, onError });
   }
 
   /**
@@ -306,38 +332,12 @@
    *   .catch(error => console.error(error));
    */
   requestAvailableDRC20Transaction(data, onSuccess, onError) {
-    return new Promise((resolve, reject) => {
-      if (!data?.ticker || !data?.amount) {
-        onError?.(new Error('Invalid data'));
-        reject(new Error('Invalid data'));
-        return;
-      }
-      if (this.isRequestPending) {
-        onError?.(new Error('There is a pending request'));
-        reject(new Error('There is a pending request'));
-        return;
-      }
-      this.isRequestPending = true;
-      window.postMessage(
-        {
-          type: MESSAGE_TYPES.CLIENT_REQUEST_AVAILABLE_DRC20_TRANSACTION,
-          data,
-        },
-        window.location.origin
-      );
-
-      createResponseHandler()({
-        resolve,
-        reject,
-        onSuccess,
-        onError,
-        messageType:
-          MESSAGE_TYPES.CLIENT_REQUEST_AVAILABLE_DRC20_TRANSACTION_RESPONSE,
-        setRequestPending: (isRequestPending) => {
-          this.isRequestPending = isRequestPending;
-        },
-      });
-    });
+    return this.#createPopupRequestHandler({
+      requestType: MESSAGE_TYPES.CLIENT_REQUEST_AVAILABLE_DRC20_TRANSACTION,
+      responseType:
+        MESSAGE_TYPES.CLIENT_REQUEST_AVAILABLE_DRC20_TRANSACTION_RESPONSE,
+      isDataValid: data?.ticker && data?.amount,
+    })({ data, onSuccess, onError });
   }
 
   /**
@@ -361,37 +361,11 @@
    *   .catch(error => console.error(error));
    */
   requestPsbt(data, onSuccess, onError) {
-    return new Promise((resolve, reject) => {
-      if (!data?.rawTx || !data?.indexes?.length) {
-        onError?.(new Error('Invalid data'));
-        reject(new Error('Invalid data'));
-        return;
-      }
-      if (this.isRequestPending) {
-        onError?.(new Error('There is a pending request'));
-        reject(new Error('There is a pending request'));
-        return;
-      }
-      this.isRequestPending = true;
-      window.postMessage(
-        {
-          type: MESSAGE_TYPES.CLIENT_REQUEST_PSBT,
-          data,
-        },
-        window.location.origin
-      );
-
-      createResponseHandler()({
-        resolve,
-        reject,
-        onSuccess,
-        onError,
-        messageType: MESSAGE_TYPES.CLIENT_REQUEST_PSBT_RESPONSE,
-        setRequestPending: (isRequestPending) => {
-          this.isRequestPending = isRequestPending;
-        },
-      });
-    });
+    return this.#createPopupRequestHandler({
+      requestType: MESSAGE_TYPES.CLIENT_REQUEST_PSBT,
+      responseType: MESSAGE_TYPES.CLIENT_REQUEST_PSBT_RESPONSE,
+      isDataValid: data?.rawTx && data?.indexes?.length,
+    })({ data, onSuccess, onError });
   }
 
   /**
@@ -413,34 +387,11 @@
    *   .catch(error => console.error(error));
    */
   requestSignedMessage(data, onSuccess, onError) {
-    return new Promise((resolve, reject) => {
-      if (!data?.message) {
-        onError?.(new Error('Invalid data'));
-        reject(new Error('Invalid data'));
-        return;
-      }
-      if (this.isRequestPending) {
-        onError?.(new Error('There is a pending request'));
-        reject(new Error('There is a pending request'));
-        return;
-      }
-      this.isRequestPending = true;
-      window.postMessage(
-        { type: MESSAGE_TYPES.CLIENT_REQUEST_SIGNED_MESSAGE, data },
-        window.location.origin
-      );
-
-      createResponseHandler()({
-        resolve,
-        reject,
-        onSuccess,
-        onError,
-        messageType: MESSAGE_TYPES.CLIENT_REQUEST_SIGNED_MESSAGE_RESPONSE,
-        setRequestPending: (isRequestPending) => {
-          this.isRequestPending = isRequestPending;
-        },
-      });
-    });
+    return this.#createPopupRequestHandler({
+      requestType: MESSAGE_TYPES.CLIENT_REQUEST_SIGNED_MESSAGE,
+      responseType: MESSAGE_TYPES.CLIENT_REQUEST_SIGNED_MESSAGE_RESPONSE,
+      isDataValid: !!data?.message,
+    })({ data, onSuccess, onError });
   }
 
   /**
@@ -462,34 +413,11 @@
    *   .catch(error => console.error(error));
    */
   requestDecryptedMessage(data, onSuccess, onError) {
-    return new Promise((resolve, reject) => {
-      if (!data?.message) {
-        onError?.(new Error('Invalid data'));
-        reject(new Error('Invalid data'));
-        return;
-      }
-      if (this.isRequestPending) {
-        onError?.(new Error('There is a pending request'));
-        reject(new Error('There is a pending request'));
-        return;
-      }
-      this.isRequestPending = true;
-      window.postMessage(
-        { type: MESSAGE_TYPES.CLIENT_REQUEST_DECRYPTED_MESSAGE, data },
-        window.location.origin
-      );
-
-      createResponseHandler()({
-        resolve,
-        reject,
-        onSuccess,
-        onError,
-        messageType: MESSAGE_TYPES.CLIENT_REQUEST_DECRYPTED_MESSAGE_RESPONSE,
-        setRequestPending: (isRequestPending) => {
-          this.isRequestPending = isRequestPending;
-        },
-      });
-    });
+    return this.#createPopupRequestHandler({
+      requestType: MESSAGE_TYPES.CLIENT_REQUEST_DECRYPTED_MESSAGE,
+      responseType: MESSAGE_TYPES.CLIENT_REQUEST_DECRYPTED_MESSAGE_RESPONSE,
+      isDataValid: !!data?.message,
+    })({ data, onSuccess, onError });
   }
 
   /**
diff --git a/public/manifest.json b/public/manifest.json
index 3a52319..35013e0 100644
--- a/public/manifest.json
+++ b/public/manifest.json
@@ -1,7 +1,7 @@
 {
   "manifest_version": 3,
   "name": "MyDoge - Dogecoin Wallet",
-  "version": "1.1.2",
+  "version": "1.1.3",
   "description": "Dogecoin wallet for Chrome browser",
   "permissions": ["storage", "tabs", "notifications", "offscreen"],
   "host_permissions": ["http://*/*", "https://*/*"],