inject-script.js

import { MESSAGE_TYPES } from './helpers/constants';

const createResponseHandler =
  () =>
  ({ 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;

      if (error) {
        onError?.(new Error(error));
        reject(new Error(error));
      } else if (data) {
        onSuccess?.(data);
        resolve(data);
      }

      window.removeEventListener('message', listener);
    }
    window.addEventListener('message', listener);
  };

/**
 * Class representing the MyDoge API to interact with the Dogecoin wallet.
 */
class MyDogeWallet {
  #requestQueue = [];

  #isRequestPending = false;

  constructor() {
    this.isMyDoge = true;
    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
   * @async
   * @param {function({ approved: boolean, address: string, balance: number }): void} [onSuccess] - Optional callback function to execute upon successful connection.
   *                                                           Receives an object containing the wallet address and balance.
   * @param {function(string): void} [onError] - Optional callback function to execute upon connection error.
   * @returns {Promise<{ approved: boolean, address: string, publicKey: string, balance: number }>} Promise object representing the outcome of the connection attempt, resolving to an object with the connected address information.
   * @method
   * @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));
   */
  connect(onSuccess, onError) {
    return this.#createPopupRequestHandler({
      requestType: MESSAGE_TYPES.CLIENT_REQUEST_CONNECTION,
      responseType: MESSAGE_TYPES.CLIENT_REQUEST_CONNECTION_RESPONSE,
    })({ onSuccess, onError });
  }

  /**
   * Retrieves the balance from the connected wallet.
   * @function
   * @async
   * @param {function({ address: string, balance: number }): void} [onSuccess] - Optional callback function to execute upon successful retrieval of balance.
   *                                                           Receives an object containing the wallet address and balance.
   * @param {function(string): void} [onError] - Optional callback function to execute upon error in retrieving balance.
   * @returns {Promise<{ address: string, balance: number }>} Promise object representing the outcome of the balance retrieval, resolving to an object with the wallet address and balance.
   * @method
   * @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));
   */
  getBalance(onSuccess, onError) {
    return new Promise((resolve, reject) => {
      window.postMessage(
        { type: MESSAGE_TYPES.CLIENT_GET_BALANCE },
        window.location.origin
      );

      createResponseHandler()({
        resolve,
        reject,
        onSuccess,
        onError,
        messageType: MESSAGE_TYPES.CLIENT_GET_BALANCE_RESPONSE,
      });
    });
  }

  /**
   * Retrieves the DRC20 token balance based on provided data.
   * @function
   * @async
   * @param {Object} data - Data required to fetch the DRC20 balance, must contain 'ticker'.
   * @param {string} data.ticker - The ticker symbol for the DRC20 token.
   * @param {function({ availableBalance: number, transferableBalance: number, ticker: string, address: string }): void} [onSuccess] - Optional callback function to execute upon successful retrieval.
   *                                                           Receives an object containing the available balance, transferable balance, ticker symbol, and wallet address.
   * @param {function(string): void} [onError] - Optional callback function to execute upon error in retrieving balance.
   * @returns {Promise<{ availableBalance: number, transferableBalance: number, ticker: string, address: string }>} Promise object representing the outcome of the balance retrieval, resolving to an object with the wallet address, available balance, and transferable balance.
   * @method
   * @example
   * getDRC20Balance(
   *   { ticker: 'DRC20' },
   *   (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));
   */

  getDRC20Balance(data, onSuccess, onError) {
    return new Promise((resolve, reject) => {
      if (!data?.ticker) {
        onError?.(new Error('Invalid data'));
        reject(new Error('Invalid data'));
        return;
      }

      window.postMessage(
        { type: MESSAGE_TYPES.CLIENT_GET_DRC20_BALANCE, data },
        window.location.origin
      );

      createResponseHandler()({
        resolve,
        reject,
        onSuccess,
        onError,
        messageType: MESSAGE_TYPES.CLIENT_GET_DRC20_BALANCE_RESPONSE,
      });
    });
  }

  /**
   * Retrieves transferable DRC20 inscriptions based on provided data.
   * @function
   * @async
   * @param {Object} data - Data required for the query, must contain 'ticker'.
   * @param {string} data.ticker - The ticker symbol for the DRC20 token.
   * @param {function({ inscriptions: Array<{ txid: string, vout: number, ticker: string, contentType: string, content: string, location: string, amount: number }>, ticker: string, address: string }): void} [onSuccess] - Optional callback function to execute upon successful retrieval.
   *                                                           Receives an object containing the transferable inscriptions, ticker symbol, and wallet address.
   * @param {function(string): void} [onError] - Optional callback function to execute upon error in fetching the transferable balance.
   * @returns {Promise<{ inscriptions: Array<{ txid: string, vout: number, ticker: string, contentType: string, content: string, location: string, amount: number }>, ticker: string, address: string }>} Promise object representing the outcome of the balance retrieval, resolving to an object with the wallet address, transferable inscriptions, and ticker symbol.}
   * @method
   * @example
   * getTransferableDRC20(
   *   { ticker: 'DRC20' },*
   *   (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));
   */
  getTransferableDRC20(data, onSuccess, onError) {
    return new Promise((resolve, reject) => {
      if (!data?.ticker) {
        onError?.(new Error('Invalid data'));
        reject(new Error('Invalid data'));
        return;
      }

      window.postMessage(
        { type: MESSAGE_TYPES.CLIENT_GET_TRANSFERABLE_DRC20, data },
        window.location.origin
      );

      createResponseHandler()({
        resolve,
        reject,
        onSuccess,
        onError,
        messageType: MESSAGE_TYPES.CLIENT_GET_TRANSFERABLE_DRC20_RESPONSE,
      });
    });
  }

  /**
   * Retrieves the Dunes token balance based on provided data.
   * @function
   * @async
   * @param {Object} data - Data required to fetch the Dunes balance, must contain 'ticker'.
   * @param {string} data.ticker - The ticker symbol for the Dunes token.
   * @param {function({ balance: number, ticker: string, address: string }): void} [onSuccess] - Optional callback function to execute upon successful retrieval.
   *                                                           Receives an object containing the balance, ticker symbol, and wallet address.
   * @param {function(string): void} [onError] - Optional callback function to execute upon error in retrieving balance.
   * @returns {Promise<{ balance: number, ticker: string, address: string }>} Promise object representing the outcome of the balance retrieval, resolving to an object with the wallet address, ticker and balance.
   * @method
   * @example
   * getDunesBalance(
   *   { ticker: 'DUNES' },
   *   (result) => console.log(`Balance: ${result.balance}`),
   *   (error) => console.error(`Balance retrieval failed: ${error}`)
   * ).then(result => console.log(result.availableBalance))
   *   .catch(error => console.error(error));
   */

  getDunesBalance(data, onSuccess, onError) {
    return new Promise((resolve, reject) => {
      if (!data?.ticker) {
        onError?.(new Error('Invalid data'));
        reject(new Error('Invalid data'));
        return;
      }

      window.postMessage(
        { type: MESSAGE_TYPES.CLIENT_GET_DUNES_BALANCE, data },
        window.location.origin
      );

      createResponseHandler()({
        resolve,
        reject,
        onSuccess,
        onError,
        messageType: MESSAGE_TYPES.CLIENT_GET_DUNES_BALANCE_RESPONSE,
      });
    });
  }

  /**
   * Requests a Dogecoin transaction based on the specified data.
   * @function
   * @async
   * @param {Object} data - Data needed for the transaction, must contain 'recipientAddress' and 'dogeAmount'.
   * @param {string} data.recipientAddress - The recipient address.
   * @param {number} data.dogeAmount - The amount of Dogecoin to send.
   * @param {function({ txId: string }): void} [onSuccess] - Optional callback function to execute upon successful transaction request.
   *                                                           Receives an object containing the transaction ID.
   * @param {function(string): void} [onError] - Optional callback function to execute upon error in processing the transaction request.
   * @returns {Promise<{ txId: string }>} Promise object representing the outcome of the transaction request, resolving to an object with the transaction ID.
   * @method
   * @example
   * requestTransaction(
   *   { recipientAddress: 'DAHkCF5LajV6jYyi5o4eMvtpqXRcm9eZYq', dogeAmount: 100 },
   *   (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));
   */
  requestTransaction(data, onSuccess, onError) {
    return this.#createPopupRequestHandler({
      requestType: MESSAGE_TYPES.CLIENT_REQUEST_TRANSACTION,
      responseType: MESSAGE_TYPES.CLIENT_REQUEST_TRANSACTION_RESPONSE,
      isDataValid: data?.recipientAddress && data?.dogeAmount,
    })({ data, onSuccess, onError });
  }

  /**
   * Requests an inscription transaction for Doginal/DRC-20 based on the specified data.
   * @function
   * @async
   * @param {Object} data - Data required for the transaction, must contain 'recipientAddress' and 'output'.
   * @param {string} data.recipientAddress - The recipient address.
   * @param {string} data.location - The location of the inscription in the format txid:vout:offset.
   * @param {function({ txId: string }): void} [onSuccess] - Optional callback function to execute upon successful transaction request.
   *                                                           Receives an object containing the transaction ID.
   * @param {function(string): void} [onError] - Optional function to execute upon error in processing the transaction request.
   * @returns {Promise<{ txId: string }>} Promise object representing the outcome of the transaction request, resolving to an object with the transaction ID.
   * @method
   * @example
   * requestInscriptionTransaction(
   *   { recipientAddress: 'DAHkCF5LajV6jYyi5o4eMvtpqXRcm9eZYq', location: '18d83f35060323a20e158805805e217b3ab7d849d5a1131f0ed8eba3a31c39a7:0:0' },
   *   (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));
   */
  requestInscriptionTransaction(data, onSuccess, onError) {
    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 });
  }

  /**
   * Requests a transaction for available DRC20 tokens based on specified data.
   * @function
   * @async
   * @param {Object} data - Data required for the transaction, must contain 'ticker' and 'amount'.
   * @param {string} data.ticker - The ticker symbol for the DRC20 token.
   * @param {string} data.amount - The amount of DRC20 tokens to make available.
   * @param {function({ txId: string, ticker: string, amount: number }): void} [onSuccess] - Optional callback function to execute upon successful transaction request.
   *
   * Receives an object containing the transaction ID, ticker symbol, and amount.
   * @param {function(string): void} [onError] - Optional callback function to execute upon error in processing the transaction request.
   * @returns {Promise<{ txId: string, ticker: string, amount: number }>} Promise object representing the outcome of the transaction request, resolving to an object with the transaction ID, ticker symbol, and amount.
   * @method
   * @example
   * requestInscriptionTransaction(
   *   { ticker: 'DRC20', amount: 100 },
   *   (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));
   */
  requestAvailableDRC20Transaction(data, onSuccess, onError) {
    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 });
  }

  /**
   * Requests a transaction for Dunes tokens based on specified data.
   * @function
   * @async
   * @param {Object} data - Data required for the transaction, must contain 'ticker' and 'amount'.
   * @param {string} data.ticker - The ticker symbol for the Dunes token.
   * @param {string} data.amount - The amount of Dunes tokens to make available.
   * @param {string} data.recipientAddress - The Dogecoin address of the recipient.
   * @param {function({ txId: string, ticker: string, amount: number }): void} [onSuccess] - Optional callback function to execute upon successful transaction request.
   *
   * Receives an object containing the transaction ID, ticker symbol, and amount.
   * @param {function(string): void} [onError] - Optional callback function to execute upon error in processing the transaction request.
   * @returns {Promise<{ txId: string, ticker: string, amount: number }>} Promise object representing the outcome of the transaction request, resolving to an object with the transaction ID, ticker symbol, and amount.
   * @method
   * @example
   * requestDunesTransaction(
   *   { ticker: 'DUNES', amount: 100, recipientAddress: 'DAHkCF5LajV6jYyi5o4eMvtpqXRcm9eZYq' },
   *   (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));
   */
  requestDunesTransaction(data, onSuccess, onError) {
    return this.#createPopupRequestHandler({
      requestType: MESSAGE_TYPES.CLIENT_REQUEST_DUNES_TRANSACTION,
      responseType: MESSAGE_TYPES.CLIENT_REQUEST_DUNES_TRANSACTION_RESPONSE,
      isDataValid: data?.ticker && data?.amount && data?.recipientAddress,
    })({ data, onSuccess, onError });
  }

  /**
   * Requests the signing of a partially signed Bitcoin transaction (PSBT) based on provided data.
   * @function
   * @async
   * @param {Object} data - Data required for signing the PSBT, must contain 'rawTx' and an array of indexes to sign 'indexes'.
   * @param {string} data.rawTx - The raw transaction to be signed.
   * @param {number[]} data.indexes - The indexes of the inputs to be signed.
   * @param {boolean} data.signOnly - A flag to indicate whether to return the raw tx after signing instead of signing + sending (default: false)
   * @param {boolean} data.partial - A flag to indicate whether to create partial signatures. Only effective when signOnly is true (default: false)
   * @param {number} data.sighashType - The signature hash type to use. Only effective when partial is true. Possible values:
   *                                   - SIGHASH_ALL (0x01): Signs all inputs and outputs (default)
   *                                   - SIGHASH_SINGLE (0x03): Signs all inputs, and the output with the same index
   *                                   - SIGHASH_ANYONECANPAY (0x80): Can be combined with above types using bitwise OR
   *                                   Combinations:
   *                                   - SIGHASH_ALL|SIGHASH_ANYONECANPAY (0x81): Signs one input and all outputs
   *                                   - SIGHASH_SINGLE|SIGHASH_ANYONECANPAY (0x83): Signs one input and one output at same index
   *                                   Note:
   *                                   - SIGHASH_NONE (0x02) is not supported for security reasons - it signs inputs but no outputs, allowing transaction outputs to be modified after signing
   * @param {function({ txId: string }): void} [onSuccess] - Optional callback function to execute upon successful signing.
   *                                                           Receives an object containing the transaction ID.
   * @param {function(string): void} [onError] - Callback function to execute upon error in signing the PSBT.
   * @returns {Promise<{ txId: string, signedRawTx: string }>} Promise object representing the outcome of the transaction request, resolving to an object with the transaction ID or the signed raw transaction if signOnly = true.
   * @method
   * @example
   * requestPsbt(
   *   { rawTx: '02000000000101...', indexes: [0] },
   *   (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));
   */
  requestPsbt(data, onSuccess, onError) {
    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 });
  }

  /**
   * Requests the signing of an arbitrary message based on provided data.
   * @function
   * @async
   * @param {Object} data - Data required for the message signing, must contain 'message'.
   * @param {string} data.message - The message to be signed.
   * @param {function({ signedMessage: string }): void} [onSuccess] - Optional callback function to execute upon successful message signing.
   *                                                           Receives an object containing the signed message.
   * @param {function(string): void} [onError] - Callback function to execute upon error in signing the message.
   * @returns {Promise<{ signedMessage: string }>} Promise object representing the outcome of the request, resolving to an object with the base64 signed message.
   * @method
   * @example
   * requestSignedMessage(
   *   { message: 'Hello, World!' },
   *   (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));
   */
  requestSignedMessage(data, onSuccess, onError) {
    return this.#createPopupRequestHandler({
      requestType: MESSAGE_TYPES.CLIENT_REQUEST_SIGNED_MESSAGE,
      responseType: MESSAGE_TYPES.CLIENT_REQUEST_SIGNED_MESSAGE_RESPONSE,
      isDataValid: !!data?.message,
    })({ data, onSuccess, onError });
  }

  /**
   * Requests the decrypting of an arbitrary message encrypted by the connected address public key.
   * @function
   * @async
   * @param {Object} data - Data required for the decryption, must contain 'message'.
   * @param {string} data.message - The message to be decrypted.
   * @param {function({ decryptedMessage: string }): void} [onSuccess] - Optional callback function to execute upon successful message signing.
   *                                                           Receives an object containing the decrypted message.
   * @param {function(string): void} [onError] - Callback function to execute upon error in decrypting the message.
   * @returns {Promise<{ decryptedMessage: string }>} Promise object representing the outcome of the request, resolving to an object with the decrypted message.
   * @method
   * @example
   * requestDecryptedMessage(
   *   { message: 'STjKie7Bsm9/MtwkCimz2A==' },
   *   (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));
   */
  requestDecryptedMessage(data, onSuccess, onError) {
    return this.#createPopupRequestHandler({
      requestType: MESSAGE_TYPES.CLIENT_REQUEST_DECRYPTED_MESSAGE,
      responseType: MESSAGE_TYPES.CLIENT_REQUEST_DECRYPTED_MESSAGE_RESPONSE,
      isDataValid: !!data?.message,
    })({ data, onSuccess, onError });
  }

  /**
   * Disconnects the current session with the wallet.
   * @function
   * @async
   * @param {function(): void} [onSuccess] - Optional callback function to execute upon successful disconnection.
   *
   * @param {function(string): void} [onError] - Optional callback function to execute upon error in disconnecting.
   * @returns {Promise<void>} Promise object representing the disconnection outcome.
   * @method
   * @example
   * disconnect(
   *   () => console.log(`Disconnected from wallet`),
   *   (error) => console.error(`Disconnection failed: ${error}`)
   * ).then(() => console.log('Disconnected from wallet'))
   *   .catch(error => console.error(error));
   */
  disconnect(onSuccess, onError) {
    return new Promise((resolve, reject) => {
      window.postMessage(
        { type: MESSAGE_TYPES.CLIENT_DISCONNECT },
        window.location.origin
      );

      createResponseHandler()({
        resolve,
        reject,
        onSuccess,
        onError,
        messageType: MESSAGE_TYPES.CLIENT_DISCONNECT_RESPONSE,
      });
    });
  }

  /**
   * Retrieves the connection status with the wallet.
   * @function
   * @async
   * @param {function({ connected: boolean, address: string, selectedWalletAddress: string }): void} [onSuccess] - Optional callback function to execute upon successfully retrieving the status.
   *                                                           Receives an object containing the wallet address, selected wallet address, and connection status.
   * @param {function(string): void} [onError] - Optional callback function to execute upon error in retrieving the connection status.
   * @returns {Promise<{ connected: boolean, address: string, selectedWalletAddress: string }>} Promise object representing the outcome of the connection status retrieval, resolving to an object with the wallet address, selected wallet address, and connection status.
   * @method
   * @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));
   */
  getConnectionStatus(onSuccess, onError) {
    return new Promise((resolve, reject) => {
      window.postMessage(
        { type: MESSAGE_TYPES.CLIENT_CONNECTION_STATUS },
        window.location.origin
      );

      createResponseHandler()({
        resolve,
        reject,
        onSuccess,
        onError,
        messageType: MESSAGE_TYPES.CLIENT_CONNECTION_STATUS_RESPONSE,
      });
    });
  }

  /**
   * Retrieves the status of a specific transaction based on provided data.
   * @function
   * @async
   * @param {Object} data - Data required for the query, must contain 'txId'.
   * @param {string} data.txId - The transaction ID to query.
   * @param {function({ txId: string, confirmations: number, status: string, dogeAmount: number, blockTime: number, address: string }): void} [onSuccess] - Optional callback function to execute upon successfully retrieving the status.
   *                                                           Receives an object containing the number of txId, confirmations, status, amount, blockTime and address for the given tx.
   * @param {function(string): void} [onError] - Optional callback function to execute upon error in retrieving the tx status.
   * @returns {Promise<{ txId: string, confirmations: number, status: string, dogeAmount: number, blockTime: number, address: string }>} Promise object representing the outcome of the tx retrieval, resolving to an object with the txId, confirmations, status, amount, blockTime and address for the given tx.
   * @method
   * @example
   * getTransactionStatus(
   *   { txId: '18d83f35060323a20e158805805e217b3ab7d849d5a1131f0ed8eba3a31c39a7' },
   *   (result) => console.log(`Trasaction status: ${result.status}`),
   *   (error) => console.error(`Transaction status retrieval failed: ${error}`)
   * ).then(result => console.log(result.status))
   *   .catch(error => console.error(error));
   */
  getTransactionStatus(data, onSuccess, onError) {
    return new Promise((resolve, reject) => {
      if (!data?.txId) {
        onError?.(new Error('Invalid data'));
        reject(new Error('Invalid data'));
        return;
      }
      window.postMessage(
        { type: MESSAGE_TYPES.CLIENT_TRANSACTION_STATUS, data },
        window.location.origin
      );

      createResponseHandler()({
        resolve,
        reject,
        onSuccess,
        onError,
        messageType: MESSAGE_TYPES.CLIENT_TRANSACTION_STATUS_RESPONSE,
      });
    });
  }
}

// API we expose to allow websites to detect & interact with extension
const doge = new MyDogeWallet();

window.addEventListener('load', () => {
  window.doge = doge;
  window.dispatchEvent(new Event('doge#initialized'));
  console.info('MyDoge API dispatched to window object');
});