UNPKG

sparrow-controllers

Version:

Collection of platform-agnostic modules for creating secure data models for cryptocurrency wallets

815 lines 33.1 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.isTokenDetectionEnabledForNetwork = exports.SupportedTokenDetectionNetworks = exports.isValidJson = exports.isNonEmptyArray = exports.hasProperty = exports.isPlainObject = exports.getFormattedIpfsUrl = exports.addUrlProtocolPrefix = exports.getIpfsCIDv1AndPath = exports.removeIpfsProtocolPrefix = exports.validateMinimumIncrease = exports.isGasPriceValue = exports.isFeeMarketEIP1559Values = exports.validateGasValues = exports.getIncreasedPriceFromExisting = exports.getIncreasedPriceHex = exports.convertPriceToDecimal = exports.isEIP1559Transaction = exports.query = exports.normalizeEnsName = exports.timeoutFetch = exports.handleFetch = exports.successfulFetch = exports.isSmartContractCode = exports.validateTokenToWatch = exports.validateTypedSignMessageDataV3 = exports.validateTypedSignMessageDataV1 = exports.validateSignMessageData = exports.normalizeMessageData = exports.validateTransaction = exports.isValidHexAddress = exports.toChecksumHexAddress = exports.safelyExecuteWithTimeout = exports.safelyExecute = exports.normalizeTransaction = exports.toHex = exports.fromHex = exports.hexToText = exports.hexToBN = exports.handleTransactionFetch = exports.getEtherscanApiUrl = exports.getBuyURL = exports.weiHexToGweiDec = exports.gweiDecToWEIBN = exports.fractionBN = exports.BNToHex = void 0; const ethereumjs_util_1 = require("ethereumjs-util"); const ethjs_unit_1 = require("ethjs-unit"); const eth_rpc_errors_1 = require("eth-rpc-errors"); const eth_ens_namehash_1 = __importDefault(require("eth-ens-namehash")); const eth_sig_util_1 = require("eth-sig-util"); const jsonschema_1 = require("jsonschema"); const cid_1 = require("multiformats/cid"); const fast_deep_equal_1 = __importDefault(require("fast-deep-equal")); const constants_1 = require("./constants"); const hexRe = /^[0-9A-Fa-f]+$/gu; const NORMALIZERS = { data: (data) => (0, ethereumjs_util_1.addHexPrefix)(data), from: (from) => (0, ethereumjs_util_1.addHexPrefix)(from).toLowerCase(), gas: (gas) => (0, ethereumjs_util_1.addHexPrefix)(gas), gasPrice: (gasPrice) => (0, ethereumjs_util_1.addHexPrefix)(gasPrice), nonce: (nonce) => (0, ethereumjs_util_1.addHexPrefix)(nonce), to: (to) => (0, ethereumjs_util_1.addHexPrefix)(to).toLowerCase(), value: (value) => (0, ethereumjs_util_1.addHexPrefix)(value), maxFeePerGas: (maxFeePerGas) => (0, ethereumjs_util_1.addHexPrefix)(maxFeePerGas), maxPriorityFeePerGas: (maxPriorityFeePerGas) => (0, ethereumjs_util_1.addHexPrefix)(maxPriorityFeePerGas), estimatedBaseFee: (maxPriorityFeePerGas) => (0, ethereumjs_util_1.addHexPrefix)(maxPriorityFeePerGas), }; /** * Converts a BN object to a hex string with a '0x' prefix. * * @param inputBn - BN instance to convert to a hex string. * @returns A '0x'-prefixed hex string. */ function BNToHex(inputBn) { return (0, ethereumjs_util_1.addHexPrefix)(inputBn.toString(16)); } exports.BNToHex = BNToHex; /** * Used to multiply a BN by a fraction. * * @param targetBN - Number to multiply by a fraction. * @param numerator - Numerator of the fraction multiplier. * @param denominator - Denominator of the fraction multiplier. * @returns Product of the multiplication. */ function fractionBN(targetBN, numerator, denominator) { const numBN = new ethereumjs_util_1.BN(numerator); const denomBN = new ethereumjs_util_1.BN(denominator); return targetBN.mul(numBN).div(denomBN); } exports.fractionBN = fractionBN; /** * Used to convert a base-10 number from GWEI to WEI. Can handle numbers with decimal parts. * * @param n - The base 10 number to convert to WEI. * @returns The number in WEI, as a BN. */ function gweiDecToWEIBN(n) { if (Number.isNaN(n)) { return new ethereumjs_util_1.BN(0); } const parts = n.toString().split('.'); const wholePart = parts[0] || '0'; let decimalPart = parts[1] || ''; if (!decimalPart) { return (0, ethjs_unit_1.toWei)(wholePart, 'gwei'); } if (decimalPart.length <= 9) { return (0, ethjs_unit_1.toWei)(`${wholePart}.${decimalPart}`, 'gwei'); } const decimalPartToRemove = decimalPart.slice(9); const decimalRoundingDigit = decimalPartToRemove[0]; decimalPart = decimalPart.slice(0, 9); let wei = (0, ethjs_unit_1.toWei)(`${wholePart}.${decimalPart}`, 'gwei'); if (Number(decimalRoundingDigit) >= 5) { wei = wei.add(new ethereumjs_util_1.BN(1)); } return wei; } exports.gweiDecToWEIBN = gweiDecToWEIBN; /** * Used to convert values from wei hex format to dec gwei format. * * @param hex - The value in hex wei. * @returns The value in dec gwei as string. */ function weiHexToGweiDec(hex) { const hexWei = new ethereumjs_util_1.BN((0, ethereumjs_util_1.stripHexPrefix)(hex), 16); return (0, ethjs_unit_1.fromWei)(hexWei, 'gwei').toString(10); } exports.weiHexToGweiDec = weiHexToGweiDec; /** * Return a URL that can be used to obtain ETH for a given network. * * @param networkCode - Network code of desired network. * @param address - Address to deposit obtained ETH. * @param amount - How much ETH is desired. * @returns URL to buy ETH based on network. */ function getBuyURL(networkCode = '1', address, amount = 5) { switch (networkCode) { case '1': return `https://buy.coinbase.com/?code=9ec56d01-7e81-5017-930c-513daa27bb6a&amount=${amount}&address=${address}&crypto_currency=UBQ`; case '3': return 'https://faucet.metamask.io/'; case '4': return 'https://www.rinkeby.io/'; case '5': return 'https://goerli-faucet.slock.it/'; case '42': return 'https://github.com/kovan-testnet/faucet'; default: return undefined; } } exports.getBuyURL = getBuyURL; /** * Return a URL that can be used to fetch UBQ transactions. * * @param networkType - Network type of desired network. * @param urlParams - The parameters used to construct the URL. * @returns URL to fetch the access the endpoint. */ function getEtherscanApiUrl(networkType, urlParams) { let etherscanSubdomain = 'api'; if (networkType !== constants_1.MAINNET) { etherscanSubdomain = `api-${networkType}`; } const apiUrl = `https://${etherscanSubdomain}.etherscan.io`; let url = `${apiUrl}/api?`; for (const paramKey in urlParams) { if (urlParams[paramKey]) { url += `${paramKey}=${urlParams[paramKey]}&`; } } url += 'tag=latest&page=1'; return url; } exports.getEtherscanApiUrl = getEtherscanApiUrl; /** * Handles the fetch of incoming transactions. * * @param networkType - Network type of desired network. * @param address - Address to get the transactions from. * @param txHistoryLimit - The maximum number of transactions to fetch. * @param opt - Object that can contain fromBlock and Etherscan service API key. * @returns Responses for both ETH and ERC20 token transactions. */ function handleTransactionFetch(networkType, address, txHistoryLimit, opt) { return __awaiter(this, void 0, void 0, function* () { // transactions const urlParams = { module: 'account', address, startBlock: opt === null || opt === void 0 ? void 0 : opt.fromBlock, apikey: opt === null || opt === void 0 ? void 0 : opt.etherscanApiKey, offset: txHistoryLimit.toString(), order: 'desc', }; const etherscanTxUrl = getEtherscanApiUrl(networkType, Object.assign(Object.assign({}, urlParams), { action: 'txlist' })); const etherscanTxResponsePromise = handleFetch(etherscanTxUrl); // tokens const etherscanTokenUrl = getEtherscanApiUrl(networkType, Object.assign(Object.assign({}, urlParams), { action: 'tokentx' })); const etherscanTokenResponsePromise = handleFetch(etherscanTokenUrl); let [etherscanTxResponse, etherscanTokenResponse] = yield Promise.all([ etherscanTxResponsePromise, etherscanTokenResponsePromise, ]); if (etherscanTxResponse.status === '0' || etherscanTxResponse.result.length <= 0) { etherscanTxResponse = { status: etherscanTxResponse.status, result: [] }; } if (etherscanTokenResponse.status === '0' || etherscanTokenResponse.result.length <= 0) { etherscanTokenResponse = { status: etherscanTokenResponse.status, result: [], }; } return [etherscanTxResponse, etherscanTokenResponse]; }); } exports.handleTransactionFetch = handleTransactionFetch; /** * Converts a hex string to a BN object. * * @param inputHex - Number represented as a hex string. * @returns A BN instance. */ function hexToBN(inputHex) { return new ethereumjs_util_1.BN((0, ethereumjs_util_1.stripHexPrefix)(inputHex), 16); } exports.hexToBN = hexToBN; /** * A helper function that converts hex data to human readable string. * * @param hex - The hex string to convert to string. * @returns A human readable string conversion. */ function hexToText(hex) { try { const stripped = (0, ethereumjs_util_1.stripHexPrefix)(hex); const buff = Buffer.from(stripped, 'hex'); return buff.toString('utf8'); } catch (e) { /* istanbul ignore next */ return hex; } } exports.hexToText = hexToText; /** * Parses a hex string and converts it into a number that can be operated on in a bignum-safe, * base-10 way. * * @param value - A base-16 number encoded as a string. * @returns The number as a BN object in base-16 mode. */ function fromHex(value) { if (ethereumjs_util_1.BN.isBN(value)) { return value; } return new ethereumjs_util_1.BN(hexToBN(value).toString(10)); } exports.fromHex = fromHex; /** * Converts an integer to a hexadecimal representation. * * @param value - An integer, an integer encoded as a base-10 string, or a BN. * @returns The integer encoded as a hex string. */ function toHex(value) { if (typeof value === 'string' && (0, ethereumjs_util_1.isHexString)(value)) { return value; } const hexString = ethereumjs_util_1.BN.isBN(value) ? value.toString(16) : new ethereumjs_util_1.BN(value.toString(), 10).toString(16); return `0x${hexString}`; } exports.toHex = toHex; /** * Normalizes properties on a Transaction object. * * @param transaction - Transaction object to normalize. * @returns Normalized Transaction object. */ function normalizeTransaction(transaction) { const normalizedTransaction = { from: '' }; let key; for (key in NORMALIZERS) { if (transaction[key]) { normalizedTransaction[key] = NORMALIZERS[key](transaction[key]); } } return normalizedTransaction; } exports.normalizeTransaction = normalizeTransaction; /** * Execute and return an asynchronous operation without throwing errors. * * @param operation - Function returning a Promise. * @param logError - Determines if the error should be logged. * @returns Promise resolving to the result of the async operation. */ function safelyExecute(operation, logError = false) { return __awaiter(this, void 0, void 0, function* () { try { return yield operation(); } catch (error) { /* istanbul ignore next */ if (logError) { console.error(error); } return undefined; } }); } exports.safelyExecute = safelyExecute; /** * Execute and return an asynchronous operation with a timeout. * * @param operation - Function returning a Promise. * @param logError - Determines if the error should be logged. * @param timeout - Timeout to fail the operation. * @returns Promise resolving to the result of the async operation. */ function safelyExecuteWithTimeout(operation, logError = false, timeout = 500) { return __awaiter(this, void 0, void 0, function* () { try { return yield Promise.race([ operation(), new Promise((_, reject) => setTimeout(() => { reject(new Error('timeout')); }, timeout)), ]); } catch (error) { /* istanbul ignore next */ if (logError) { console.error(error); } return undefined; } }); } exports.safelyExecuteWithTimeout = safelyExecuteWithTimeout; /** * Convert an address to a checksummed hexidecimal address. * * @param address - The address to convert. * @returns A 0x-prefixed hexidecimal checksummed address. */ function toChecksumHexAddress(address) { const hexPrefixed = (0, ethereumjs_util_1.addHexPrefix)(address); if (!(0, ethereumjs_util_1.isHexString)(hexPrefixed)) { // Version 5.1 of ethereumjs-utils would have returned '0xY' for input 'y' // but we shouldn't waste effort trying to change case on a clearly invalid // string. Instead just return the hex prefixed original string which most // closely mimics the original behavior. return hexPrefixed; } return (0, ethereumjs_util_1.toChecksumAddress)(hexPrefixed); } exports.toChecksumHexAddress = toChecksumHexAddress; /** * Validates that the input is a hex address. This utility method is a thin * wrapper around ethereumjs-util.isValidAddress, with the exception that it * does not throw an error when provided values that are not hex strings. In * addition, and by default, this method will return true for hex strings that * meet the length requirement of a hex address, but are not prefixed with `0x` * Finally, if the mixedCaseUseChecksum flag is true and a mixed case string is * provided this method will validate it has the proper checksum formatting. * * @param possibleAddress - Input parameter to check against. * @param options - The validation options. * @param options.allowNonPrefixed - If true will first ensure '0x' is prepended to the string. * @returns Whether or not the input is a valid hex address. */ function isValidHexAddress(possibleAddress, { allowNonPrefixed = true } = {}) { const addressToCheck = allowNonPrefixed ? (0, ethereumjs_util_1.addHexPrefix)(possibleAddress) : possibleAddress; if (!(0, ethereumjs_util_1.isHexString)(addressToCheck)) { return false; } return (0, ethereumjs_util_1.isValidAddress)(addressToCheck); } exports.isValidHexAddress = isValidHexAddress; /** * Validates a Transaction object for required properties and throws in * the event of any validation error. * * @param transaction - Transaction object to validate. */ function validateTransaction(transaction) { if (!transaction.from || typeof transaction.from !== 'string' || !isValidHexAddress(transaction.from)) { throw new Error(`Invalid "from" address: ${transaction.from} must be a valid string.`); } if (transaction.to === '0x' || transaction.to === undefined) { if (transaction.data) { delete transaction.to; } else { throw new Error(`Invalid "to" address: ${transaction.to} must be a valid string.`); } } else if (transaction.to !== undefined && !isValidHexAddress(transaction.to)) { throw new Error(`Invalid "to" address: ${transaction.to} must be a valid string.`); } if (transaction.value !== undefined) { const value = transaction.value.toString(); if (value.includes('-')) { throw new Error(`Invalid "value": ${value} is not a positive number.`); } if (value.includes('.')) { throw new Error(`Invalid "value": ${value} number must be denominated in wei.`); } const intValue = parseInt(transaction.value, 10); const isValid = Number.isFinite(intValue) && !Number.isNaN(intValue) && !isNaN(Number(value)) && Number.isSafeInteger(intValue); if (!isValid) { throw new Error(`Invalid "value": ${value} number must be a valid number.`); } } } exports.validateTransaction = validateTransaction; /** * A helper function that converts rawmessageData buffer data to a hex, or just returns the data if * it is already formatted as a hex. * * @param data - The buffer data to convert to a hex. * @returns A hex string conversion of the buffer data. */ function normalizeMessageData(data) { try { const stripped = (0, ethereumjs_util_1.stripHexPrefix)(data); if (stripped.match(hexRe)) { return (0, ethereumjs_util_1.addHexPrefix)(stripped); } } catch (e) { /* istanbul ignore next */ } return (0, ethereumjs_util_1.bufferToHex)(Buffer.from(data, 'utf8')); } exports.normalizeMessageData = normalizeMessageData; /** * Validates a PersonalMessageParams and MessageParams objects for required properties and throws in * the event of any validation error. * * @param messageData - PersonalMessageParams object to validate. */ function validateSignMessageData(messageData) { const { from, data } = messageData; if (!from || typeof from !== 'string' || !isValidHexAddress(from)) { throw new Error(`Invalid "from" address: ${from} must be a valid string.`); } if (!data || typeof data !== 'string') { throw new Error(`Invalid message "data": ${data} must be a valid string.`); } } exports.validateSignMessageData = validateSignMessageData; /** * Validates a TypedMessageParams object for required properties and throws in * the event of any validation error for eth_signTypedMessage_V1. * * @param messageData - TypedMessageParams object to validate. */ function validateTypedSignMessageDataV1(messageData) { if (!messageData.from || typeof messageData.from !== 'string' || !isValidHexAddress(messageData.from)) { throw new Error(`Invalid "from" address: ${messageData.from} must be a valid string.`); } if (!messageData.data || !Array.isArray(messageData.data)) { throw new Error(`Invalid message "data": ${messageData.data} must be a valid array.`); } try { // typedSignatureHash will throw if the data is invalid. (0, eth_sig_util_1.typedSignatureHash)(messageData.data); } catch (e) { throw new Error(`Expected EIP712 typed data.`); } } exports.validateTypedSignMessageDataV1 = validateTypedSignMessageDataV1; /** * Validates a TypedMessageParams object for required properties and throws in * the event of any validation error for eth_signTypedMessage_V3. * * @param messageData - TypedMessageParams object to validate. */ function validateTypedSignMessageDataV3(messageData) { if (!messageData.from || typeof messageData.from !== 'string' || !isValidHexAddress(messageData.from)) { throw new Error(`Invalid "from" address: ${messageData.from} must be a valid string.`); } if (!messageData.data || typeof messageData.data !== 'string') { throw new Error(`Invalid message "data": ${messageData.data} must be a valid array.`); } let data; try { data = JSON.parse(messageData.data); } catch (e) { throw new Error('Data must be passed as a valid JSON string.'); } const validation = (0, jsonschema_1.validate)(data, eth_sig_util_1.TYPED_MESSAGE_SCHEMA); if (validation.errors.length > 0) { throw new Error('Data must conform to EIP-712 schema. See https://git.io/fNtcx.'); } } exports.validateTypedSignMessageDataV3 = validateTypedSignMessageDataV3; /** * Validates a ERC20 token to be added with EIP747. * * @param token - Token object to validate. */ function validateTokenToWatch(token) { const { address, symbol, decimals } = token; if (!address || !symbol || typeof decimals === 'undefined') { throw eth_rpc_errors_1.ethErrors.rpc.invalidParams(`Must specify address, symbol, and decimals.`); } if (typeof symbol !== 'string') { throw eth_rpc_errors_1.ethErrors.rpc.invalidParams(`Invalid symbol: not a string.`); } if (symbol.length > 11) { throw eth_rpc_errors_1.ethErrors.rpc.invalidParams(`Invalid symbol "${symbol}": longer than 11 characters.`); } const numDecimals = parseInt(decimals, 10); if (isNaN(numDecimals) || numDecimals > 36 || numDecimals < 0) { throw eth_rpc_errors_1.ethErrors.rpc.invalidParams(`Invalid decimals "${decimals}": must be 0 <= 36.`); } if (!isValidHexAddress(address)) { throw eth_rpc_errors_1.ethErrors.rpc.invalidParams(`Invalid address "${address}".`); } } exports.validateTokenToWatch = validateTokenToWatch; /** * Returns whether the given code corresponds to a smart contract. * * @param code - The potential smart contract code. * @returns Whether the code was smart contract code or not. */ function isSmartContractCode(code) { /* istanbul ignore if */ if (!code) { return false; } // Geth will return '0x', and ganache-core v2.2.1 will return '0x0' const smartContractCode = code !== '0x' && code !== '0x0'; return smartContractCode; } exports.isSmartContractCode = isSmartContractCode; /** * Execute fetch and verify that the response was successful. * * @param request - Request information. * @param options - Fetch options. * @returns The fetch response. */ function successfulFetch(request, options) { return __awaiter(this, void 0, void 0, function* () { const response = yield fetch(request, options); if (!response.ok) { throw new Error(`Fetch failed with status '${response.status}' for request '${request}'`); } return response; }); } exports.successfulFetch = successfulFetch; /** * Execute fetch and return object response. * * @param request - The request information. * @param options - The fetch options. * @returns The fetch response JSON data. */ function handleFetch(request, options) { return __awaiter(this, void 0, void 0, function* () { const response = yield successfulFetch(request, options); const object = yield response.json(); return object; }); } exports.handleFetch = handleFetch; /** * Fetch that fails after timeout. * * @param url - Url to fetch. * @param options - Options to send with the request. * @param timeout - Timeout to fail request. * @returns Promise resolving the request. */ function timeoutFetch(url, options, timeout = 500) { return __awaiter(this, void 0, void 0, function* () { return Promise.race([ successfulFetch(url, options), new Promise((_, reject) => setTimeout(() => { reject(new Error('timeout')); }, timeout)), ]); }); } exports.timeoutFetch = timeoutFetch; /** * Normalizes the given ENS name. * * @param ensName - The ENS name. * @returns The normalized ENS name string. */ function normalizeEnsName(ensName) { if (ensName && typeof ensName === 'string') { try { const normalized = eth_ens_namehash_1.default.normalize(ensName.trim()); // this regex is only sufficient with the above call to ensNamehash.normalize // TODO: change 7 in regex to 3 when shorter ENS domains are live if (normalized.match(/^(([\w\d-]+)\.)*[\w\d-]{7,}\.(eth|test)$/u)) { return normalized; } } catch (_) { // do nothing } } return null; } exports.normalizeEnsName = normalizeEnsName; /** * Wrapper method to handle EthQuery requests. * * @param ethQuery - EthQuery object initialized with a provider. * @param method - Method to request. * @param args - Arguments to send. * @returns Promise resolving the request. */ function query(ethQuery, method, args = []) { return new Promise((resolve, reject) => { const cb = (error, result) => { if (error) { reject(error); return; } resolve(result); }; if (typeof ethQuery[method] === 'function') { ethQuery[method](...args, cb); } else { ethQuery.sendAsync({ method, params: args }, cb); } }); } exports.query = query; /** * Checks if a transaction is EIP-1559 by checking for the existence of * maxFeePerGas and maxPriorityFeePerGas within its parameters. * * @param transaction - Transaction object to add. * @returns Boolean that is true if the transaction is EIP-1559 (has maxFeePerGas and maxPriorityFeePerGas), otherwise returns false. */ const isEIP1559Transaction = (transaction) => { const hasOwnProp = (obj, key) => Object.prototype.hasOwnProperty.call(obj, key); return (hasOwnProp(transaction, 'maxFeePerGas') && hasOwnProp(transaction, 'maxPriorityFeePerGas')); }; exports.isEIP1559Transaction = isEIP1559Transaction; const convertPriceToDecimal = (value) => parseInt(value === undefined ? '0x0' : value, 16); exports.convertPriceToDecimal = convertPriceToDecimal; const getIncreasedPriceHex = (value, rate) => (0, ethereumjs_util_1.addHexPrefix)(`${parseInt(`${value * rate}`, 10).toString(16)}`); exports.getIncreasedPriceHex = getIncreasedPriceHex; const getIncreasedPriceFromExisting = (value, rate) => { return (0, exports.getIncreasedPriceHex)((0, exports.convertPriceToDecimal)(value), rate); }; exports.getIncreasedPriceFromExisting = getIncreasedPriceFromExisting; const validateGasValues = (gasValues) => { Object.keys(gasValues).forEach((key) => { const value = gasValues[key]; if (typeof value !== 'string' || !(0, ethereumjs_util_1.isHexString)(value)) { throw new TypeError(`expected hex string for ${key} but received: ${value}`); } }); }; exports.validateGasValues = validateGasValues; const isFeeMarketEIP1559Values = (gasValues) => (gasValues === null || gasValues === void 0 ? void 0 : gasValues.maxFeePerGas) !== undefined || (gasValues === null || gasValues === void 0 ? void 0 : gasValues.maxPriorityFeePerGas) !== undefined; exports.isFeeMarketEIP1559Values = isFeeMarketEIP1559Values; const isGasPriceValue = (gasValues) => (gasValues === null || gasValues === void 0 ? void 0 : gasValues.gasPrice) !== undefined; exports.isGasPriceValue = isGasPriceValue; /** * Validates that the proposed value is greater than or equal to the minimum value. * * @param proposed - The proposed value. * @param min - The minimum value. * @returns The proposed value. * @throws Will throw if the proposed value is too low. */ function validateMinimumIncrease(proposed, min) { const proposedDecimal = (0, exports.convertPriceToDecimal)(proposed); const minDecimal = (0, exports.convertPriceToDecimal)(min); if (proposedDecimal >= minDecimal) { return proposed; } const errorMsg = `The proposed value: ${proposedDecimal} should meet or exceed the minimum value: ${minDecimal}`; throw new Error(errorMsg); } exports.validateMinimumIncrease = validateMinimumIncrease; /** * Removes IPFS protocol prefix from input string. * * @param ipfsUrl - An IPFS url (e.g. ipfs://{content id}) * @returns IPFS content identifier and (possibly) path in a string * @throws Will throw if the url passed is not IPFS. */ function removeIpfsProtocolPrefix(ipfsUrl) { if (ipfsUrl.startsWith('ipfs://ipfs/')) { return ipfsUrl.replace('ipfs://ipfs/', ''); } else if (ipfsUrl.startsWith('ipfs://')) { return ipfsUrl.replace('ipfs://', ''); } // this method should not be used with non-ipfs urls (i.e. startsWith('ipfs://') === true) throw new Error('this method should not be used with non ipfs urls'); } exports.removeIpfsProtocolPrefix = removeIpfsProtocolPrefix; /** * Extracts content identifier and path from an input string. * * @param ipfsUrl - An IPFS URL minus the IPFS protocol prefix * @returns IFPS content identifier (cid) and sub path as string. * @throws Will throw if the url passed is not ipfs. */ function getIpfsCIDv1AndPath(ipfsUrl) { const url = removeIpfsProtocolPrefix(ipfsUrl); // check if there is a path // (CID is everything preceding first forward slash, path is everything after) const index = url.indexOf('/'); const cid = index !== -1 ? url.substring(0, index) : url; const path = index !== -1 ? url.substring(index) : undefined; // We want to ensure that the CID is v1 (https://docs.ipfs.io/concepts/content-addressing/#identifier-formats) // because most cid v0s appear to be incompatible with IPFS subdomains return { cid: cid_1.CID.parse(cid).toV1().toString(), path, }; } exports.getIpfsCIDv1AndPath = getIpfsCIDv1AndPath; /** * Adds URL protocol prefix to input URL string if missing. * * @param urlString - An IPFS URL. * @returns A URL with a https:// prepended. */ function addUrlProtocolPrefix(urlString) { if (!urlString.match(/(^http:\/\/)|(^https:\/\/)/u)) { return `https://${urlString}`; } return urlString; } exports.addUrlProtocolPrefix = addUrlProtocolPrefix; /** * Formats URL correctly for use retrieving assets hosted on IPFS. * * @param ipfsGateway - The users preferred IPFS gateway (full URL or just host). * @param ipfsUrl - The IFPS URL pointed at the asset. * @param subdomainSupported - Boolean indicating whether the URL should be formatted with subdomains or not. * @returns A formatted URL, with the user's preferred IPFS gateway and format (subdomain or not), pointing to an asset hosted on IPFS. */ function getFormattedIpfsUrl(ipfsGateway, ipfsUrl, subdomainSupported) { const { host, protocol, origin } = new URL(addUrlProtocolPrefix(ipfsGateway)); if (subdomainSupported) { const { cid, path } = getIpfsCIDv1AndPath(ipfsUrl); return `${protocol}//${cid}.ipfs.${host}${path !== null && path !== void 0 ? path : ''}`; } const cidAndPath = removeIpfsProtocolPrefix(ipfsUrl); return `${origin}/ipfs/${cidAndPath}`; } exports.getFormattedIpfsUrl = getFormattedIpfsUrl; /** * Determines whether a value is a "plain" object. * * @param value - A value to check * @returns True if the passed value is a plain object */ function isPlainObject(value) { return Boolean(value) && typeof value === 'object' && !Array.isArray(value); } exports.isPlainObject = isPlainObject; const hasProperty = (object, key) => Reflect.hasOwnProperty.call(object, key); exports.hasProperty = hasProperty; /** * Type guard for {@link NonEmptyArray}. * * @template T - The non-empty array member type. * @param value - The value to check. * @returns Whether the value is a non-empty array. */ function isNonEmptyArray(value) { return Array.isArray(value) && value.length > 0; } exports.isNonEmptyArray = isNonEmptyArray; /** * Type guard for {@link Json}. * * @param value - The value to check. * @returns Whether the value is valid JSON. */ function isValidJson(value) { try { return (0, fast_deep_equal_1.default)(value, JSON.parse(JSON.stringify(value))); } catch (_) { return false; } } exports.isValidJson = isValidJson; /** * Networks where token detection is supported - Values are in decimal format */ var SupportedTokenDetectionNetworks; (function (SupportedTokenDetectionNetworks) { SupportedTokenDetectionNetworks["mainnet"] = "1"; SupportedTokenDetectionNetworks["bsc"] = "56"; SupportedTokenDetectionNetworks["polygon"] = "137"; SupportedTokenDetectionNetworks["avax"] = "43114"; })(SupportedTokenDetectionNetworks = exports.SupportedTokenDetectionNetworks || (exports.SupportedTokenDetectionNetworks = {})); /** * Check if token detection is enabled for certain networks. * * @param chainId - ChainID of network * @returns Whether the current network supports token detection */ function isTokenDetectionEnabledForNetwork(chainId) { return Object.values(SupportedTokenDetectionNetworks).includes(chainId); } exports.isTokenDetectionEnabledForNetwork = isTokenDetectionEnabledForNetwork; //# sourceMappingURL=util.js.map