@unique-nft/utils
Version:
A tiny library to work with Substrate and Ethereum addresses and do some more
552 lines (547 loc) • 20 kB
JavaScript
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
;