diff --git a/package-lock.json b/package-lock.json index fb7c7ac..2ecadab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@perawallet/connect", - "version": "1.1.1", + "version": "1.2.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@perawallet/connect", - "version": "1.1.1", + "version": "1.2.0", "license": "ISC", "dependencies": { "@evanhahn/lottie-web-light": "5.8.1", @@ -36,7 +36,7 @@ "typescript": "^4.6.3" }, "peerDependencies": { - "algosdk": "^1.23.2" + "algosdk": "^2.1.0" } }, "node_modules/@babel/code-frame": { @@ -895,13 +895,13 @@ } }, "node_modules/algosdk": { - "version": "1.23.2", - "resolved": "https://registry.npmjs.org/algosdk/-/algosdk-1.23.2.tgz", - "integrity": "sha512-ZDq71Kq+e3bvHxnTi/hWqom2/YHeTrbkxenOOCvPidRQuwkaOQAs92c4fqImdn7+Y+MfLhdtQspBLFR1hQTE4A==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/algosdk/-/algosdk-2.1.0.tgz", + "integrity": "sha512-KogpdRK3u7Efvw0FVduyFQEOwI+uGvMJmXUvzdmQJjK0hWhEYUl13X/X1QqNKwyEZjw2qutf9pf23UbNnBEAwA==", "peer": true, "dependencies": { "algo-msgpack-with-bigint": "^2.1.1", - "buffer": "^6.0.2", + "buffer": "^6.0.3", "cross-fetch": "^3.1.5", "hi-base32": "^0.5.1", "js-sha256": "^0.9.0", @@ -8485,13 +8485,13 @@ "peer": true }, "algosdk": { - "version": "1.23.2", - "resolved": "https://registry.npmjs.org/algosdk/-/algosdk-1.23.2.tgz", - "integrity": "sha512-ZDq71Kq+e3bvHxnTi/hWqom2/YHeTrbkxenOOCvPidRQuwkaOQAs92c4fqImdn7+Y+MfLhdtQspBLFR1hQTE4A==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/algosdk/-/algosdk-2.1.0.tgz", + "integrity": "sha512-KogpdRK3u7Efvw0FVduyFQEOwI+uGvMJmXUvzdmQJjK0hWhEYUl13X/X1QqNKwyEZjw2qutf9pf23UbNnBEAwA==", "peer": true, "requires": { "algo-msgpack-with-bigint": "^2.1.1", - "buffer": "^6.0.2", + "buffer": "^6.0.3", "cross-fetch": "^3.1.5", "hi-base32": "^0.5.1", "js-sha256": "^0.9.0", diff --git a/package.json b/package.json index 1d88a6d..4fc3d0f 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "1.1.1", + "version": "1.2.0", "name": "@perawallet/connect", "description": "JavaScript SDK for integrating Pera Wallet to web applications.", "main": "dist/index.js", @@ -38,7 +38,7 @@ "qr-code-styling": "1.6.0-rc.1" }, "peerDependencies": { - "algosdk": "^1.23.2" + "algosdk": "^2.1.0" }, "types": "./dist/index.d.ts", "repository": { diff --git a/src/PeraWalletConnect.ts b/src/PeraWalletConnect.ts index dc40c9e..e3d6fa0 100644 --- a/src/PeraWalletConnect.ts +++ b/src/PeraWalletConnect.ts @@ -37,7 +37,12 @@ import {AlgorandChainIDs} from "./util/peraWalletTypes"; import {generateEmbeddedWalletURL} from "./util/peraWalletUtils"; import appTellerManager, {PeraTeller} from "./util/network/teller/appTellerManager"; import {getPeraWebWalletURL} from "./util/peraWalletConstants"; -import {getMetaInfo, waitForTabOpening} from "./util/dom/domUtils"; +import { + getMetaInfo, + waitForTabOpening, + WAIT_FOR_TAB_MAX_TRY_COUNT, + WAIT_FOR_TAB_TRY_INTERVAL +} from "./util/dom/domUtils"; interface PeraWalletConnectOptions { bridge?: string; @@ -102,157 +107,182 @@ class PeraWalletConnect { const webWalletURLs = getPeraWebWalletURL(webWalletURL); const peraWalletIframe = document.createElement("iframe"); - function onReceiveMessage(event: MessageEvent>) { - if (resolve && event.data.message.type === "CONNECT_CALLBACK") { - const accounts = event.data.message.data.addresses; + function onWebWalletConnect(peraWalletIframeWrapper: Element) { + if (browser === "Chrome") { + peraWalletIframe.setAttribute("id", PERA_WALLET_IFRAME_ID); + peraWalletIframe.setAttribute( + "src", + generateEmbeddedWalletURL(webWalletURLs.CONNECT) + ); - saveWalletDetailsToStorage(accounts, "pera-wallet-web"); + peraWalletIframeWrapper.appendChild(peraWalletIframe); - resolve(accounts); + if (peraWalletIframe.contentWindow) { + let count = 0; - onClose(); + const checkIframeIsInitialized = setInterval(() => { + count += 1; - document.getElementById(PERA_WALLET_IFRAME_ID)?.remove(); - } else if (event.data.message.type === "CONNECT_NETWORK_MISMATCH") { - reject( - new PeraWalletConnectError( - { - type: "CONNECT_NETWORK_MISMATCH", - detail: event.data.message.error - }, - event.data.message.error || - `Your wallet is connected to a different network to this dApp. Update your wallet to the correct network (MainNet or TestNet) to continue.` - ) - ); + if (count === WAIT_FOR_TAB_MAX_TRY_COUNT) { + clearInterval(checkIframeIsInitialized); - onClose(); + return; + } - document.getElementById(PERA_WALLET_IFRAME_ID)?.remove(); - } else if ( - ["CREATE_PASSCODE_EMBEDDED", "SELECT_ACCOUNT_EMBEDDED"].includes( - event.data.message.type - ) - ) { - if (event.data.message.type === "CREATE_PASSCODE_EMBEDDED") { - waitForTabOpening(webWalletURLs.CONNECT).then((newPeraWalletTab) => { - if (newPeraWalletTab) { - appTellerManager.sendMessage({ - message: { - type: "CONNECT", - data: { - ...getMetaInfo(), - chainId - } - }, + appTellerManager.sendMessage({ + message: { + type: "IFRAME_INITIALIZED" + }, - origin: webWalletURLs.CONNECT, - targetWindow: newPeraWalletTab - }); - } + origin: webWalletURLs.CONNECT, + targetWindow: peraWalletIframe.contentWindow! + }); + }, WAIT_FOR_TAB_TRY_INTERVAL); + + appTellerManager.setupListener({ + onReceiveMessage: (event: MessageEvent>) => { + if (event.data.message.type === "IFRAME_INITIALIZED_RECEIVED") { + clearInterval(checkIframeIsInitialized); + appTellerManager.sendMessage({ + message: { + type: "CONNECT", + data: { + ...getMetaInfo(), + chainId + } + }, + + origin: webWalletURLs.CONNECT, + targetWindow: peraWalletIframe.contentWindow! + }); + } else if (resolve && event.data.message.type === "CONNECT_CALLBACK") { + const accounts = event.data.message.data.addresses; + + saveWalletDetailsToStorage(accounts, "pera-wallet-web"); + + resolve(accounts); - const checkTabIsAliveInterval = setInterval(() => { - if (newPeraWalletTab?.closed === true) { + onClose(); + + document.getElementById(PERA_WALLET_IFRAME_ID)?.remove(); + } else if (event.data.message.type === "CONNECT_NETWORK_MISMATCH") { reject( new PeraWalletConnectError( { - type: "CONNECT_CANCELLED" + type: "CONNECT_NETWORK_MISMATCH", + detail: event.data.message.error }, - "Connect is cancelled by user" + event.data.message.error || + `Your wallet is connected to a different network to this dApp. Update your wallet to the correct network (MainNet or TestNet) to continue.` ) ); onClose(); - clearInterval(checkTabIsAliveInterval); - } - - // eslint-disable-next-line no-magic-numbers - }, 2000); - - appTellerManager.setupListener({ - onReceiveMessage: ( - newTabEvent: MessageEvent> - ) => { - if (resolve && newTabEvent.data.message.type === "CONNECT_CALLBACK") { - const accounts = newTabEvent.data.message.data.addresses; - - saveWalletDetailsToStorage(accounts, "pera-wallet-web"); - resolve(accounts); + document.getElementById(PERA_WALLET_IFRAME_ID)?.remove(); + } else if ( + ["CREATE_PASSCODE_EMBEDDED", "SELECT_ACCOUNT_EMBEDDED"].includes( + event.data.message.type + ) + ) { + if (event.data.message.type === "CREATE_PASSCODE_EMBEDDED") { + waitForTabOpening(webWalletURLs.CONNECT).then((newPeraWalletTab) => { + if (newPeraWalletTab) { + appTellerManager.sendMessage({ + message: { + type: "CONNECT", + data: { + ...getMetaInfo(), + chainId + } + }, + + origin: webWalletURLs.CONNECT, + targetWindow: newPeraWalletTab + }); + } + + const checkTabIsAliveInterval = setInterval(() => { + if (newPeraWalletTab?.closed === true) { + reject( + new PeraWalletConnectError( + { + type: "CONNECT_CANCELLED" + }, + "Connect is cancelled by user" + ) + ); + + onClose(); + clearInterval(checkTabIsAliveInterval); + } + + // eslint-disable-next-line no-magic-numbers + }, 2000); + + appTellerManager.setupListener({ + onReceiveMessage: ( + newTabEvent: MessageEvent> + ) => { + if ( + resolve && + newTabEvent.data.message.type === "CONNECT_CALLBACK" + ) { + const accounts = newTabEvent.data.message.data.addresses; + + saveWalletDetailsToStorage(accounts, "pera-wallet-web"); + + resolve(accounts); + + onClose(); + + newPeraWalletTab?.close(); + } + } + }); + }); + } else if (event.data.message.type === "SELECT_ACCOUNT_EMBEDDED") { + const peraWalletConnectModalWrapper = document.getElementById( + PERA_WALLET_CONNECT_MODAL_ID + ); - onClose(); + const peraWalletConnectModal = peraWalletConnectModalWrapper + ?.querySelector("pera-wallet-connect-modal") + ?.shadowRoot?.querySelector(`.${PERA_WALLET_MODAL_CLASSNAME}`); + + const peraWalletConnectModalDesktopMode = peraWalletConnectModal + ?.querySelector("pera-wallet-modal-desktop-mode") + ?.shadowRoot?.querySelector( + ".pera-wallet-connect-modal-desktop-mode" + ); + + if (peraWalletConnectModal && peraWalletConnectModalDesktopMode) { + peraWalletConnectModal.classList.add( + `${PERA_WALLET_MODAL_CLASSNAME}--select-account` + ); + peraWalletConnectModal.classList.remove( + `${PERA_WALLET_MODAL_CLASSNAME}--create-passcode` + ); + peraWalletConnectModalDesktopMode.classList.add( + `pera-wallet-connect-modal-desktop-mode--select-account` + ); + peraWalletConnectModalDesktopMode.classList.remove( + `pera-wallet-connect-modal-desktop-mode--create-passcode` + ); + } - newPeraWalletTab?.close(); + appTellerManager.sendMessage({ + message: { + type: "SELECT_ACCOUNT_EMBEDDED_CALLBACK" + }, + origin: webWalletURLs.CONNECT, + targetWindow: peraWalletIframe.contentWindow! + }); } } - }); - }); - } else if (event.data.message.type === "SELECT_ACCOUNT_EMBEDDED") { - const peraWalletConnectModalWrapper = document.getElementById( - PERA_WALLET_CONNECT_MODAL_ID - ); - - const peraWalletConnectModal = peraWalletConnectModalWrapper - ?.querySelector("pera-wallet-connect-modal") - ?.shadowRoot?.querySelector(`.${PERA_WALLET_MODAL_CLASSNAME}`); - - const peraWalletConnectModalDesktopMode = peraWalletConnectModal - ?.querySelector("pera-wallet-modal-desktop-mode") - ?.shadowRoot?.querySelector(".pera-wallet-connect-modal-desktop-mode"); - - if (peraWalletConnectModal && peraWalletConnectModalDesktopMode) { - peraWalletConnectModal.classList.add( - `${PERA_WALLET_MODAL_CLASSNAME}--select-account` - ); - peraWalletConnectModal.classList.remove( - `${PERA_WALLET_MODAL_CLASSNAME}--create-passcode` - ); - peraWalletConnectModalDesktopMode.classList.add( - `pera-wallet-connect-modal-desktop-mode--select-account` - ); - peraWalletConnectModalDesktopMode.classList.remove( - `pera-wallet-connect-modal-desktop-mode--create-passcode` - ); - } - - appTellerManager.sendMessage({ - message: { - type: "SELECT_ACCOUNT_EMBEDDED_CALLBACK" - }, - origin: webWalletURLs.CONNECT, - targetWindow: peraWalletIframe.contentWindow! - }); - } - } - } - - function onWebWalletConnect(peraWalletIframeWrapper: Element) { - if (browser === "Chrome") { - peraWalletIframe.setAttribute("id", PERA_WALLET_IFRAME_ID); - peraWalletIframe.setAttribute( - "src", - generateEmbeddedWalletURL(webWalletURLs.CONNECT) - ); - - peraWalletIframeWrapper.appendChild(peraWalletIframe); - - if (peraWalletIframe.contentWindow) { - appTellerManager.sendMessage({ - message: { - type: "CONNECT", - data: { - ...getMetaInfo(), - chainId - } - }, - - origin: webWalletURLs.CONNECT, - targetWindow: peraWalletIframe.contentWindow + } }); } - - appTellerManager.setupListener({ - onReceiveMessage - }); } else { waitForTabOpening(webWalletURLs.CONNECT) .then((newPeraWalletTab) => { @@ -590,74 +620,99 @@ class PeraWalletConnect { } if (peraWalletIframe.contentWindow) { - appTellerManager.sendMessage({ - message: { - type: "SIGN_TXN", - txn: signTxnRequestParams - }, + let count = 0; - origin: generateEmbeddedWalletURL(webWalletURLs.TRANSACTION_SIGN), - targetWindow: peraWalletIframe.contentWindow - }); - } + const checkIframeIsInitialized = setInterval(() => { + count += 1; - appTellerManager.setupListener({ - onReceiveMessage: (event: MessageEvent>) => { - if (event.data.message.type === "SIGN_TXN_CALLBACK") { - document.getElementById(PERA_WALLET_IFRAME_ID)?.remove(); - closePeraWalletSignTxnModal(); + if (count === WAIT_FOR_TAB_MAX_TRY_COUNT) { + clearInterval(checkIframeIsInitialized); - resolve( - event.data.message.signedTxns.map((txn) => - base64ToUint8Array(txn.signedTxn) - ) - ); + return; } - if (event.data.message.type === "SIGN_TXN_NETWORK_MISMATCH") { - reject( - new PeraWalletConnectError( - { - type: "SIGN_TXN_NETWORK_MISMATCH", - detail: event.data.message.error + appTellerManager.sendMessage({ + message: { + type: "IFRAME_INITIALIZED" + }, + + origin: webWalletURLs.CONNECT, + targetWindow: peraWalletIframe.contentWindow! + }); + }, WAIT_FOR_TAB_TRY_INTERVAL); + + appTellerManager.setupListener({ + onReceiveMessage: (event: MessageEvent>) => { + if (event.data.message.type === "IFRAME_INITIALIZED_RECEIVED") { + clearInterval(checkIframeIsInitialized); + + appTellerManager.sendMessage({ + message: { + type: "SIGN_TXN", + txn: signTxnRequestParams }, - event.data.message.error || "Network mismatch" - ) - ); - } - if (event.data.message.type === "SESSION_DISCONNECTED") { - document.getElementById(PERA_WALLET_IFRAME_ID)?.remove(); - closePeraWalletSignTxnModal(); + origin: generateEmbeddedWalletURL(webWalletURLs.TRANSACTION_SIGN), + targetWindow: peraWalletIframe.contentWindow! + }); + } - resetWalletDetailsFromStorage(); + if (event.data.message.type === "SIGN_TXN_CALLBACK") { + document.getElementById(PERA_WALLET_IFRAME_ID)?.remove(); + closePeraWalletSignTxnModal(); - reject( - new PeraWalletConnectError( - { - type: "SESSION_DISCONNECTED", - detail: event.data.message.error - }, - event.data.message.error - ) - ); - } + resolve( + event.data.message.signedTxns.map((txn) => + base64ToUint8Array(txn.signedTxn) + ) + ); + } - if (event.data.message.type === "SIGN_TXN_CALLBACK_ERROR") { - document.getElementById(PERA_WALLET_IFRAME_ID)?.remove(); - closePeraWalletSignTxnModal(); + if (event.data.message.type === "SIGN_TXN_NETWORK_MISMATCH") { + reject( + new PeraWalletConnectError( + { + type: "SIGN_TXN_NETWORK_MISMATCH", + detail: event.data.message.error + }, + event.data.message.error || "Network mismatch" + ) + ); + } - reject( - new PeraWalletConnectError( - { - type: "SIGN_TXN_CANCELLED" - }, - event.data.message.error - ) - ); + if (event.data.message.type === "SESSION_DISCONNECTED") { + document.getElementById(PERA_WALLET_IFRAME_ID)?.remove(); + closePeraWalletSignTxnModal(); + + resetWalletDetailsFromStorage(); + + reject( + new PeraWalletConnectError( + { + type: "SESSION_DISCONNECTED", + detail: event.data.message.error + }, + event.data.message.error + ) + ); + } + + if (event.data.message.type === "SIGN_TXN_CALLBACK_ERROR") { + document.getElementById(PERA_WALLET_IFRAME_ID)?.remove(); + closePeraWalletSignTxnModal(); + + reject( + new PeraWalletConnectError( + { + type: "SIGN_TXN_CANCELLED" + }, + event.data.message.error + ) + ); + } } - } - }); + }); + } // Returns a promise that waits for the response from the web wallet. // The promise is resolved when the web wallet responds with the signed txn. diff --git a/src/util/dom/domUtils.ts b/src/util/dom/domUtils.ts index 17f1a95..be2c92a 100644 --- a/src/util/dom/domUtils.ts +++ b/src/util/dom/domUtils.ts @@ -1,8 +1,8 @@ import appTellerManager, {PeraTeller} from "../network/teller/appTellerManager"; import PeraWalletConnectError from "../PeraWalletConnectError"; -const WAIT_FOR_TAB_TRY_INTERVAL = 300; -const WAIT_FOR_TAB_MAX_TRY_COUNT = 50; +export const WAIT_FOR_TAB_TRY_INTERVAL = 700; +export const WAIT_FOR_TAB_MAX_TRY_COUNT = 50; function getMetaInfo() { const metaTitle: HTMLElement | null = document.querySelector('meta[name="name"]'); diff --git a/src/util/network/teller/appTellerManager.ts b/src/util/network/teller/appTellerManager.ts index 1710127..67d8b52 100644 --- a/src/util/network/teller/appTellerManager.ts +++ b/src/util/network/teller/appTellerManager.ts @@ -62,6 +62,12 @@ export type PeraTeller = } | { type: "TAB_OPEN_RECEIVED"; + } + | { + type: "IFRAME_INITIALIZED"; + } + | { + type: "IFRAME_INITIALIZED_RECEIVED"; }; const appTellerManager = new Teller({ diff --git a/src/util/transaction/transactionUtils.ts b/src/util/transaction/transactionUtils.ts index 4629f6b..f6b08d7 100644 --- a/src/util/transaction/transactionUtils.ts +++ b/src/util/transaction/transactionUtils.ts @@ -16,6 +16,8 @@ function composeTransaction(transaction: SignerTransaction, signerAddress?: stri if (signerAddress && !(transaction.signers || []).includes(signerAddress)) { signers = []; + } else { + signers = transaction.signers; } const txnRequestParams: PeraWalletTransaction = {