UNPKG

@unique-nft/utils

Version:

A tiny library to work with Substrate and Ethereum addresses and do some more

552 lines (547 loc) 20 kB
"use strict"; var ExtensionTools = (() => { var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/ExtensionTools/index.ts var ExtensionTools_exports = {}; __export(ExtensionTools_exports, { Ethereum: () => Ethereum, ExtensionTools: () => ExtensionTools, Polkadot: () => Polkadot, UniqueChainName: () => UniqueChainName2, default: () => ExtensionTools_default }); // src/ExtensionTools/utils.ts var documentReadyPromiseAndWindowIsOk = () => { if (typeof window === "undefined") { return Promise.resolve(false); } else if (window.document.readyState === "complete") { return Promise.resolve(true); } else { return new Promise((resolve) => window.addEventListener("load", () => resolve(true), { once: true })); } }; // src/ExtensionTools/ethereum.ts var windowIsOkSync = () => { return typeof window !== "undefined" && !!window.ethereum; }; var UNIQUE_CHAINS = ["unique", "quartz", "opal", "sapphire"]; var UNIQUE_CHAIN_IDS = [8880, 8881, 8882, 8883]; var UniqueChainName = { unique: "unique", quartz: "quartz", opal: "opal", sapphire: "sapphire" }; var chainNameToChainId = { unique: 8880, quartz: 8881, opal: 8882, sapphire: 8883 }; var chainIdToChainName = { 8880: "unique", 8881: "quartz", 8882: "opal", 8883: "sapphire" }; var isUniqueChainFactory = (chainName) => () => { if (!windowIsOkSync()) { return false; } const chainId = parseInt(window.ethereum.chainId, 16); return chainId === chainNameToChainId[chainName]; }; var currentChainIs = { unique: isUniqueChainFactory("unique"), quartz: isUniqueChainFactory("quartz"), opal: isUniqueChainFactory("opal"), sapphire: isUniqueChainFactory("sapphire"), byName: (chainName) => { if (!UNIQUE_CHAINS.includes(chainName)) { throw new Error(`Invalid chain name: ${chainName}`); } return isUniqueChainFactory(chainName)(); }, anyUniqueChain: (chainId) => { if (!chainId) return false; return typeof chainId === "string" && UNIQUE_CHAINS.includes(chainId) || typeof chainId === "number" && UNIQUE_CHAIN_IDS.includes(chainId); } }; var getOrRequestAccounts = async (requestInsteadOfGet = false) => { const windowIsOk = await documentReadyPromiseAndWindowIsOk(); if (!windowIsOk) { return { address: "", chainId: 0 }; } if (!window.ethereum) { const error = new Error("No extension found"); error.extensionNotFound = true; error.userRejected = false; error.needToRequestAccess = false; error.chainId = null; throw error; } const ethereum = window.ethereum; let accounts = []; let chainId = null; try { accounts = await ethereum.request({ method: requestInsteadOfGet ? "eth_requestAccounts" : "eth_accounts" }); chainId = parseInt(ethereum.chainId, 16); } catch (_error) { const error = _error; error.userRejected = _error.code === 4001; error.extensionNotFound = false; error.needToRequestAccess = false; const chainIdStr = window.ethereum?.chainId; error.chainId = typeof chainIdStr === "string" ? parseInt(chainIdStr, 16) : NaN; throw error; } const account = accounts[0]; if (!account) { const error = new Error("Need to request account"); error.extensionNotFound = false; error.userRejected = false; error.needToRequestAccess = true; error.chainId = chainId; throw error; } return { address: accounts[0], chainId }; }; var getOrRequestAccountsSafe = async (requestInsteadOfGet = false) => { try { return { result: await getOrRequestAccounts(requestInsteadOfGet), error: null }; } catch (error) { return { result: null, error }; } }; var addChainToExtension = async (chainData) => { const windowIsOk = await documentReadyPromiseAndWindowIsOk(); if (!windowIsOk) return; const ethereum = window.ethereum; if (ethereum.chainId === chainData.chainId) { console.log(`No need to add the chain to wallet - wallet already has ${chainData.chainName}'s chainId: ${ethereum.chainId} (${parseInt(ethereum.chainId)})`); return; } try { await ethereum.request({ method: "wallet_addEthereumChain", params: [chainData] }); } catch (error) { console.error("Error during attempt to add chain to wallet", error.code, error); throw error; } }; var UNIQUE_CHAINS_DATA_FOR_EXTENSIONS = { unique: { chainId: "0x22b0", chainName: "Unique", nativeCurrency: { name: "Unique", symbol: "UNQ", decimals: 18 }, rpcUrls: [`https://rpc.unique.network`], iconUrls: [`https://ipfs.unique.network/ipfs/QmbJ7CGZ2GxWMp7s6jy71UGzRsMe4w3KANKXDAExYWdaFR`], blockExplorerUrls: ["https://uniquescan.io/unique/"] }, quartz: { chainId: "0x22b1", chainName: "Quartz by Unique", nativeCurrency: { name: "Quartz", symbol: "QTZ", decimals: 18 }, rpcUrls: [`https://rpc-quartz.unique.network`], iconUrls: [`https://ipfs.unique.network/ipfs/QmaGPdccULQEFcCGxzstnmE8THfac2kSiGwvWRAiaRq4dp`], blockExplorerUrls: ["https://uniquescan.io/quartz/"] }, opal: { chainId: "0x22b2", chainName: "Opal by Unique", nativeCurrency: { name: "Opal", symbol: "OPL", decimals: 18 }, rpcUrls: [`https://rpc-opal.unique.network`], iconUrls: [`https://ipfs.unique.network/ipfs/QmYJDpmWyjDa3H6BxweFmQXk4fU8b1GU7M9EqYcaUNvXzc`], blockExplorerUrls: ["https://uniquescan.io/opal/"] }, sapphire: { chainId: "0x22b3", chainName: "Sapphire by Unique", nativeCurrency: { name: "Quartz", symbol: "QTZ", decimals: 18 }, rpcUrls: [`https://rpc-sapphire.unique.network`], iconUrls: [`https://ipfs.unique.network/ipfs/Qmd1PGt4cDRjFbh4ihP5QKEd4XQVwN1MkebYKdF56V74pf`], blockExplorerUrls: ["https://uniquescan.io/sapphire/"] } }; var addChain = { unique: () => addChainToExtension(UNIQUE_CHAINS_DATA_FOR_EXTENSIONS.unique), quartz: () => addChainToExtension(UNIQUE_CHAINS_DATA_FOR_EXTENSIONS.quartz), opal: () => addChainToExtension(UNIQUE_CHAINS_DATA_FOR_EXTENSIONS.opal), sapphire: () => addChainToExtension(UNIQUE_CHAINS_DATA_FOR_EXTENSIONS.sapphire), anyChain: (chainData) => addChainToExtension(chainData), byName: (chainName) => { if (!UNIQUE_CHAINS.includes(chainName)) { throw new Error(`Invalid chain name: ${chainName}`); } return addChainToExtension(UNIQUE_CHAINS_DATA_FOR_EXTENSIONS[chainName]); } }; var switchToChain = async (chainId) => { const windowIsOk = await documentReadyPromiseAndWindowIsOk(); if (!windowIsOk) return; const parsedChainId = typeof chainId === "string" ? chainId : "0x" + chainId.toString(16); await window.ethereum.request({ method: "wallet_switchEthereumChain", params: [{ chainId: parsedChainId }] }); }; var switchChainTo = { unique: () => switchToChain(chainNameToChainId.unique), quartz: () => switchToChain(chainNameToChainId.quartz), opal: () => switchToChain(chainNameToChainId.opal), sapphire: () => switchToChain(chainNameToChainId.sapphire), anyChain: (chainId) => switchToChain(chainId), byName: (chainName) => { if (!UNIQUE_CHAINS.includes(chainName)) { throw new Error(`Invalid chain name: ${chainName}`); } return switchToChain(chainNameToChainId[chainName]); } }; var subscribeOnChanges = (cb) => { if (typeof window === "undefined" || !window.ethereum) { return () => void 0; } const ethereum = window.ethereum; const refresh = (reason) => { if (ethereum.chainId && ethereum.selectedAddress) { cb({ reason, address: ethereum.selectedAddress, chainId: parseInt(ethereum.chainId, 16) }); } else { getAccounts().then(({ chainId, address }) => { cb({ reason, chainId, address }); }).catch((e) => console.error(`Error during attempt to update account info in subscribeOnChanges`, e)); } }; ethereum.on("accountsChanged", () => { refresh("account"); }); ethereum.on("chainChanged", () => { refresh("chain"); }); return () => { ethereum.removeListener("accountsChanged", refresh); ethereum.removeListener("networkChanged", refresh); }; }; var parseEthersTxReceipt = (tx, options = { decimals: 18 }) => { const events = (tx.events || []).filter((event) => !!event.event).map((event, index) => { const args = event.args; return { name: event.event || `event_${index.toString().padStart(4, "0")}`, // args: event.args!, events: !args ? {} : Object.keys(args).filter((key) => isNaN(parseInt(key))).reduce((acc, key) => { const rawValue = args[key]; const value = typeof rawValue === "object" && rawValue?._isBigNumber ? rawValue.toBigInt() : rawValue; acc[key] = value; return acc; }, {}) }; }).reduce((acc, elem) => { acc[elem.name] = elem.events; return acc; }, {}); const rawPrice = tx.gasUsed.toBigInt() * tx.effectiveGasPrice.toBigInt(); const priceStr = rawPrice.toString().padStart(options.decimals + 1, "0"); const price = parseFloat([priceStr.slice(0, -options.decimals), ".", priceStr.slice(-options.decimals)].join("")); return { get tx() { return tx; }, from: tx.from, to: tx.to, rawPrice, price, rawEvents: tx.events || [], events, gasUsed: tx.gasUsed.toBigInt(), cumulativeGasUsed: tx.cumulativeGasUsed.toBigInt(), effectiveGasPrice: tx.effectiveGasPrice.toBigInt() }; }; var requestAccounts = () => getOrRequestAccounts(true); var getAccounts = () => getOrRequestAccounts(); var Ethereum = { getOrRequestAccounts, getOrRequestAccountsSafe, requestAccounts, getAccounts, requestAccountsSafe: () => getOrRequestAccountsSafe(true), getAccountsSafe: () => getOrRequestAccountsSafe(), subscribeOnChanges, chainNameToChainId, chainIdToChainName, currentChainIs, addChain, switchChainTo, UNIQUE_CHAINS_DATA_FOR_EXTENSIONS, UNIQUE_CHAINS, UNIQUE_CHAIN_IDS, UniqueChainName, parseEthersTxReceipt }; // src/ExtensionTools/polkadot.ts var IPFS_GATEWAY = "https://gateway.pinata.cloud/ipfs/"; var KNOWN_POLKADOT_EXTENSIONS = { "polkadot-js": { logoIpfsCid: "QmYWczavyNyh3yM56axyyQLgRqcFQYNKe5cDFPfLL94yrz", prettyName: "Polkadot{.js}", webpage: "https://polkadot.js.org/extension/" }, "subwallet-js": { logoIpfsCid: "QmZ8BvFzGL5DRugJ3pytc1Jo1rTVYjW3mKUWBx7SvaEsdS", prettyName: "Subwallet", webpage: "https://subwallet.app/" }, "talisman": { logoIpfsCid: "QmWHn4kVoG43U5coNhoR7Ec3Vus5YPjo9SkMvoiiLoy2bY", prettyName: "Talisman", webpage: "https://talisman.xyz/" } }; var FALLBACK_WALLET_LOGO_IPFS_CID = "QmSC1B9X9ugWkfHKtd5guKTidh2qjvgdn8xXzbFrrHMiBM"; var compareTwoStrings = (a, b) => a > b ? 1 : a < b ? -1 : 0; var knownPolkadotExtensions = Object.entries(KNOWN_POLKADOT_EXTENSIONS).map(([name, info]) => { return { name, prettyName: info.prettyName, logo: { ipfsCid: info.logoIpfsCid, url: IPFS_GATEWAY + info.logoIpfsCid }, webpage: info.webpage }; }).sort((a, b) => compareTwoStrings(a.name, b.name)); var isWeb3Environment = async () => { const windowIsOk = await documentReadyPromiseAndWindowIsOk(); if (!windowIsOk) { return false; } const injectedWeb3 = window.injectedWeb3; return !!injectedWeb3 && Object.keys(injectedWeb3).length !== 0; }; var getWalletInfo = (walletName, nonInjectedWallet) => { const isEnabled = nonInjectedWallet.isEnabled; const prettyName = KNOWN_POLKADOT_EXTENSIONS[walletName]?.prettyName || walletName; const logo = { ipfsCid: KNOWN_POLKADOT_EXTENSIONS[walletName]?.logoIpfsCid || FALLBACK_WALLET_LOGO_IPFS_CID, url: IPFS_GATEWAY + (KNOWN_POLKADOT_EXTENSIONS[walletName]?.logoIpfsCid || FALLBACK_WALLET_LOGO_IPFS_CID) }; return { name: walletName, version: nonInjectedWallet.version, prettyName, logo, isEnabled }; }; var listWallets = async () => { if (!await isWeb3Environment()) { return { wallets: [], info: { extensionFound: false, enabledWalletsNumber: 0 } }; } const walletsArray = Object.entries(window.injectedWeb3); const wallets = walletsArray.map(([name, injectedWallet]) => getWalletInfo(name, injectedWallet)); return { wallets, info: { extensionFound: true, enabledWalletsNumber: wallets.reduce((acc, wallet) => acc + (wallet.isEnabled ? 1 : 0), 0) } }; }; var loadWalletByNameSafe = async (walletName) => { if (!await isWeb3Environment()) { return { result: null, error: new Error(`now injected web3 found or environment not a browser`) }; } const injectedWeb3 = window.injectedWeb3; const rawWallet = injectedWeb3[walletName]; if (!rawWallet) { return { result: null, error: new Error(`Wallet with name ${walletName} not found in "window.injectedWeb3"`) }; } try { const walletInfo = getWalletInfo(walletName, rawWallet); const wallet = await rawWallet.enable(); const accounts = await wallet.accounts.get(); const parsedWallet = { ...walletInfo, ...wallet, accounts: accounts.map((account) => { const address = account.address; const addressShort = `${address.slice(0, 5)}...${address.slice(-5)}`; const id = `${walletName}/${address}`; const accountName = account.name || `${walletName}/${addressShort}`; const accountType = account.type || "sr25519"; const signRaw = async (payloadRaw) => { if (!wallet.signer.signRaw) { throw new Error(`no signRaw in the wallet ${walletName}`); } const payload = typeof payloadRaw === "string" ? { address, data: payloadRaw, type: "bytes" } : { address, ...payloadRaw, type: payloadRaw.type || "bytes" }; return wallet.signer.signRaw(payload); }; const signPayload = async (payloadJSON) => { if (!wallet.signer.signPayload) { throw new Error(`no signPayload in the wallet ${walletName}`); } return wallet.signer.signPayload({ address, ...payloadJSON }); }; const signer = { address, sign: async (unsignedTxPayload) => { const signatureType = accountType; if (unsignedTxPayload.signerPayloadJSON) { const { signature: signature2 } = await signPayload(unsignedTxPayload.signerPayloadJSON); return { signatureType, signature: signature2 }; } const toSign = unsignedTxPayload.signerPayloadRaw || unsignedTxPayload.signerPayloadHex; const { signature } = await signRaw(toSign); return { signatureType, signature }; } }; const enhancedAccount = { ...account, name: accountName, id, address, addressShort, type: accountType, meta: { genesisHash: account.genesisHash || null, name: accountName, source: walletName }, wallet: walletInfo, signRaw, signPayload, update: wallet.signer.update, signer }; return enhancedAccount; }) }; return { result: parsedWallet, error: null }; } catch (e) { return { result: null, error: e }; } }; var loadWalletByName = async (walletName) => { const { result, error } = await loadWalletByNameSafe(walletName); if (error) { throw error; } return result; }; var loadWallets = async (onlyEnabled = false) => { if (!await documentReadyPromiseAndWindowIsOk()) { return { wallets: [], accounts: [], rejectedWallets: [] }; } if (!await isWeb3Environment()) { const error = new Error(`No injected web3 found`); error.extensionNotFound = true; error.accountsNotFound = false; error.userHasWalletsButHasNoAccounts = false; error.userHasBlockedAllWallets = false; throw error; } const allWallets = await Promise.all( (await listWallets()).wallets.filter((wallet) => !onlyEnabled || wallet.isEnabled).map(async (wallet) => ({ info: wallet, enabled: await loadWalletByNameSafe(wallet.name) })) ); const wallets = allWallets.filter((wallet) => !!wallet.enabled.result).map((wallet) => wallet.enabled.result).sort((a, b) => compareTwoStrings(a.name, b.name)); const rejectedWallets = allWallets.filter((wallet) => !!wallet.enabled.error).map((wallet) => { const error = wallet.enabled.error; const result = { ...wallet.info, error, isBlockedByUser: error.message.indexOf("is not allowed to interact with this extension") >= 0 }; return result; }).sort((a, b) => compareTwoStrings(a.name, b.name)); const accounts = wallets.flatMap((wallet) => wallet.accounts).sort( (a, b) => compareTwoStrings(a.wallet.name + a.name + a.address, b.wallet.name + b.name + b.address) ); if (!accounts.length) { const error = new Error(`No accounts found`); error.extensionNotFound = false; error.accountsNotFound = true; error.userHasWalletsButHasNoAccounts = !!wallets.length; error.userHasBlockedAllWallets = !wallets.length && !!rejectedWallets.length && rejectedWallets.every((w) => w.isBlockedByUser); throw error; } return { wallets, rejectedWallets, accounts }; }; var loadWalletsSafe = async (onlyEnabled = false) => { try { return { result: await loadWallets(onlyEnabled), error: null }; } catch (e) { return { result: null, error: e }; } }; var Polkadot = { listWallets, enableAndLoadAllWallets: () => loadWallets(false), loadEnabledWallets: () => loadWallets(true), enableAndLoadAllWalletsSafe: () => loadWalletsSafe(false), loadEnabledWalletsSafe: () => loadWalletsSafe(true), loadWalletByName, loadWalletByNameSafe, constants: { knownPolkadotExtensions, extensionNames: { polkadot: "polkadot-js", subwallet: "subwallet-js", talisman: "talisman" } } }; // src/ExtensionTools/index.ts var ExtensionTools = { Ethereum, Polkadot }; var UniqueChainName2 = Ethereum.UniqueChainName; var ExtensionTools_default = ExtensionTools; return __toCommonJS(ExtensionTools_exports); })(); //# sourceMappingURL=extension.min.js.map