update dist

This commit is contained in:
CounterFire2023 2024-01-17 13:47:52 +08:00
parent 7af683d82c
commit c0ab142fea
25 changed files with 3816 additions and 59 deletions

11
dist/common/ZError.cjs vendored Normal file
View File

@ -0,0 +1,11 @@
"use strict";Object.defineProperty(exports, "__esModule", {value: true});// src/common/ZError.ts
var ZError = class {
constructor(statusCode, message) {
this.statusCode = statusCode;
this.message = message;
}
};
exports.ZError = ZError;
//# sourceMappingURL=ZError.cjs.map

1
dist/common/ZError.cjs.map vendored Normal file
View File

@ -0,0 +1 @@
{"version":3,"sources":["../../src/common/ZError.ts"],"names":[],"mappings":";AACO,IAAM,SAAN,MAA8B;AAAA,EAMnC,YAAY,YAAoB,SAAiB;AAC/C,SAAK,aAAa;AAClB,SAAK,UAAU;AAAA,EACjB;AACF","sourcesContent":["\nexport class ZError implements Error {\n code: string\n statusCode?: number\n message: string\n name: string\n\n constructor(statusCode: number, message: string) {\n this.statusCode = statusCode\n this.message = message\n }\n}\n"]}

9
dist/common/ZError.d.cts vendored Normal file
View File

@ -0,0 +1,9 @@
declare class ZError implements Error {
code: string;
statusCode?: number;
message: string;
name: string;
constructor(statusCode: number, message: string);
}
export { ZError };

9
dist/common/ZError.d.ts vendored Normal file
View File

@ -0,0 +1,9 @@
declare class ZError implements Error {
code: string;
statusCode?: number;
message: string;
name: string;
constructor(statusCode: number, message: string);
}
export { ZError };

11
dist/common/ZError.js vendored Normal file
View File

@ -0,0 +1,11 @@
// src/common/ZError.ts
var ZError = class {
constructor(statusCode, message) {
this.statusCode = statusCode;
this.message = message;
}
};
export {
ZError
};
//# sourceMappingURL=ZError.js.map

1
dist/common/ZError.js.map vendored Normal file
View File

@ -0,0 +1 @@
{"version":3,"sources":["../../src/common/ZError.ts"],"sourcesContent":["\nexport class ZError implements Error {\n code: string\n statusCode?: number\n message: string\n name: string\n\n constructor(statusCode: number, message: string) {\n this.statusCode = statusCode\n this.message = message\n }\n}\n"],"mappings":";AACO,IAAM,SAAN,MAA8B;AAAA,EAMnC,YAAY,YAAoB,SAAiB;AAC/C,SAAK,aAAa;AAClB,SAAK,UAAU;AAAA,EACjB;AACF;","names":[]}

86
dist/index.cjs vendored
View File

@ -1,36 +1,4 @@
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/index.ts
var src_exports = {};
__export(src_exports, {
ethUnitMap: () => ethUnitMap,
fromWei: () => fromWei,
hexToNumber: () => hexToNumber,
isHex: () => isHex,
toBigInt: () => toBigInt,
toBigWei: () => toBigWei,
toNumber: () => toNumber,
toWei: () => toWei
});
module.exports = __toCommonJS(src_exports);
// src/utils/bn.util.ts
"use strict";Object.defineProperty(exports, "__esModule", {value: true});// src/utils/bn.util.ts
var isHexStrict = (hex) => typeof hex === "string" && /^((-)?0x[0-9a-f]+|(0x))$/i.test(hex);
var isHex = (hex) => typeof hex === "number" || typeof hex === "bigint" || typeof hex === "string" && /^((-0x|0x|-)?[0-9a-f]+|(0x))$/i.test(hex);
var base = BigInt(10);
@ -90,7 +58,7 @@ var toNumber = (value) => {
}
try {
return toNumber(BigInt(value));
} catch {
} catch (e) {
throw new Error("ivalid number: " + value);
}
};
@ -149,15 +117,43 @@ var fromWei = (number, unit = "ether") => {
}
return `${integer}.${fraction}`;
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
ethUnitMap,
fromWei,
hexToNumber,
isHex,
toBigInt,
toBigWei,
toNumber,
toWei
});
// src/utils/date.util.ts
var ONE_DAY = 24 * 60 * 60 * 1e3;
var formatDate = (date) => {
const year = date.getFullYear();
const month = (date.getMonth() + 1 + "").padStart(2, "0");
const day = (date.getDate() + "").padStart(2, "0");
return `${year}${month}${day}`;
};
var yesterday = (date) => {
date = date || /* @__PURE__ */ new Date();
date.setDate(date.getDate() - 1);
return date;
};
var nextday = (date) => {
date = date || /* @__PURE__ */ new Date();
date.setDate(date.getDate() + 1);
return date;
};
function daysBetween(date1, date2) {
const diffInMs = Math.abs(date1.getTime() - date2.getTime());
const diffInDays = Math.round(diffInMs / ONE_DAY);
return diffInDays;
}
exports.ONE_DAY = ONE_DAY; exports.daysBetween = daysBetween; exports.ethUnitMap = ethUnitMap; exports.formatDate = formatDate; exports.fromWei = fromWei; exports.hexToNumber = hexToNumber; exports.isHex = isHex; exports.nextday = nextday; exports.toBigInt = toBigInt; exports.toBigWei = toBigWei; exports.toNumber = toNumber; exports.toWei = toWei; exports.yesterday = yesterday;
//# sourceMappingURL=index.cjs.map

2
dist/index.cjs.map vendored

File diff suppressed because one or more lines are too long

8
dist/index.d.cts vendored
View File

@ -69,4 +69,10 @@ declare const toWei: (number: Numbers, unit?: EtherUnits) => string;
*/
declare const fromWei: (number: Numbers, unit?: EtherUnits) => string;
export { type EtherUnits, type HexString, type Numbers, type ValidInputTypes, ethUnitMap, fromWei, hexToNumber, isHex, toBigInt, toBigWei, toNumber, toWei };
declare const ONE_DAY: number;
declare const formatDate: (date: Date) => string;
declare const yesterday: (date?: Date) => Date;
declare const nextday: (date?: Date) => Date;
declare function daysBetween(date1: Date, date2: Date): number;
export { type EtherUnits, type HexString, type Numbers, ONE_DAY, type ValidInputTypes, daysBetween, ethUnitMap, formatDate, fromWei, hexToNumber, isHex, nextday, toBigInt, toBigWei, toNumber, toWei, yesterday };

8
dist/index.d.ts vendored
View File

@ -69,4 +69,10 @@ declare const toWei: (number: Numbers, unit?: EtherUnits) => string;
*/
declare const fromWei: (number: Numbers, unit?: EtherUnits) => string;
export { type EtherUnits, type HexString, type Numbers, type ValidInputTypes, ethUnitMap, fromWei, hexToNumber, isHex, toBigInt, toBigWei, toNumber, toWei };
declare const ONE_DAY: number;
declare const formatDate: (date: Date) => string;
declare const yesterday: (date?: Date) => Date;
declare const nextday: (date?: Date) => Date;
declare function daysBetween(date1: Date, date2: Date): number;
export { type EtherUnits, type HexString, type Numbers, ONE_DAY, type ValidInputTypes, daysBetween, ethUnitMap, formatDate, fromWei, hexToNumber, isHex, nextday, toBigInt, toBigWei, toNumber, toWei, yesterday };

31
dist/index.js vendored
View File

@ -117,14 +117,43 @@ var fromWei = (number, unit = "ether") => {
}
return `${integer}.${fraction}`;
};
// src/utils/date.util.ts
var ONE_DAY = 24 * 60 * 60 * 1e3;
var formatDate = (date) => {
const year = date.getFullYear();
const month = (date.getMonth() + 1 + "").padStart(2, "0");
const day = (date.getDate() + "").padStart(2, "0");
return `${year}${month}${day}`;
};
var yesterday = (date) => {
date = date || /* @__PURE__ */ new Date();
date.setDate(date.getDate() - 1);
return date;
};
var nextday = (date) => {
date = date || /* @__PURE__ */ new Date();
date.setDate(date.getDate() + 1);
return date;
};
function daysBetween(date1, date2) {
const diffInMs = Math.abs(date1.getTime() - date2.getTime());
const diffInDays = Math.round(diffInMs / ONE_DAY);
return diffInDays;
}
export {
ONE_DAY,
daysBetween,
ethUnitMap,
formatDate,
fromWei,
hexToNumber,
isHex,
nextday,
toBigInt,
toBigWei,
toNumber,
toWei
toWei,
yesterday
};
//# sourceMappingURL=index.js.map

2
dist/index.js.map vendored

File diff suppressed because one or more lines are too long

View File

@ -15,11 +15,15 @@
},
"author": "",
"license": "ISC",
"dependencies": {
"@metamask/eth-sig-util": "^4.0.1",
"crypto-js": "^4.2.0",
"ethereumjs-util": "^7.1.5",
"web3": "^1.7.4"
},
"devDependencies": {
"ts-node": "^10.9.2",
"tsup": "^8.0.1",
"typescript": "^5.3.3"
},
"dependencies": {
"tsup": "^8.0.1"
}
}

12
src/common/ZError.ts Normal file
View File

@ -0,0 +1,12 @@
export class ZError implements Error {
code: string
statusCode?: number
message: string
name: string
constructor(statusCode: number, message: string) {
this.statusCode = statusCode
this.message = message
}
}

View File

@ -1 +1,2 @@
export * from './utils/bn.util'
export * from './utils/date.util'

74
src/utils/chain.util.ts Normal file
View File

@ -0,0 +1,74 @@
import { recoverTypedSignature, SignTypedDataVersion } from '@metamask/eth-sig-util'
import { soliditySha3, toWei } from 'web3-utils'
import Web3 from 'web3'
export function recoverTypedSignatureV4(signObj: any, signature: string) {
return recoverTypedSignature({
data: signObj,
signature,
version: SignTypedDataVersion.V4,
})
}
export function formatAddress(address: string) {
if (address.length >= 10) {
return address.substring(0, 8) + '...' + address.substring(address.length - 8)
} else if (address.length > 0 && address.length < 10) {
return address
} else {
return ''
}
}
export function buildLoginSignMsg(nonce: string, tips: string) {
const signMsg = {
tips,
nonce,
}
const signObj = {
types: {
EIP712Domain: [
{ name: 'name', type: 'string' },
{ name: 'version', type: 'string' },
],
set: [
{ name: 'tips', type: 'string' },
{ name: 'nonce', type: 'string' },
],
},
primaryType: 'set',
domain: {
name: 'Auth',
version: '1',
},
message: signMsg,
}
return signObj
}
export const sign = async ({
user,
token,
amount,
saltNonce,
}: {
user: string
token: string
amount: number | string
saltNonce?: string
}) => {
const web3 = new Web3()
let privateKey = process.env.SIGN_PRIVATE_KEY
const acc = web3.eth.accounts.privateKeyToAccount(privateKey)
const account = web3.eth.accounts.wallet.add(acc)
const executor = account.address
const amountBn = toWei(amount + '')
const chainId = process.env.CHAIN
const claimContract = process.env.CLAIM_CONTRACT
const startTime = (Date.now() / 1000) | 0
saltNonce = saltNonce || ((Math.random() * 1000) | 0) + ''
let signStr = soliditySha3.apply(this, [user, token, claimContract, chainId, amountBn, startTime, saltNonce])
let signature = await web3.eth.sign(signStr, executor)
signature = signature.replace(/00$/, '1b').replace(/01$/, '1c')
return { token, amount: amountBn, startTime, saltNonce, signature }
}

30
src/utils/date.util.ts Normal file
View File

@ -0,0 +1,30 @@
export const ONE_DAY = 24 * 60 * 60 * 1000
// format the date to the format we want
export const formatDate = (date: Date): string => {
const year = date.getFullYear()
const month = (date.getMonth() + 1 + '').padStart(2, '0')
const day = (date.getDate() + '').padStart(2, '0')
return `${year}${month}${day}`
}
// get formated datestring of yesterday
export const yesterday = (date?: Date) => {
date = date || new Date()
date.setDate(date.getDate() - 1)
return date
}
export const nextday = (date?: Date) => {
date = date || new Date()
date.setDate(date.getDate() + 1)
return date
}
// calc days between two Date
export function daysBetween(date1: Date, date2: Date) {
// hours*minutes*seconds*milliseconds
const diffInMs = Math.abs(date1.getTime() - date2.getTime())
const diffInDays = Math.round(diffInMs / ONE_DAY)
return diffInDays
}

148
src/utils/net.util.ts Normal file
View File

@ -0,0 +1,148 @@
import { ZError } from 'common/ZError'
const TIMEOUT_ERROR = new Error('timeout')
const hexRe = /^[0-9A-Fa-f]+$/gu
/**
* Execute fetch and verify that the response was successful.
*
* @param request - Request information.
* @param options - Fetch options.
* @returns The fetch response.
*/
export async function successfulFetch(request: string, options?: RequestInit) {
const response = await fetch(request, options)
if (!response.ok) {
throw new Error(`Fetch failed with status '${response.status}' for request '${request}'`)
}
return response
}
/**
* Execute fetch and return object response.
*
* @param request - The request information.
* @param options - The fetch options.
* @returns The fetch response JSON data.
*/
export async function handleFetch(request: string, options?: RequestInit) {
const response = await successfulFetch(request, options)
const object = await response.json()
return object
}
/**
* Execute fetch and return object response, log if known error thrown, otherwise rethrow error.
*
* @param request - the request options object
* @param request.url - The request url to query.
* @param request.options - The fetch options.
* @param request.timeout - Timeout to fail request
* @param request.errorCodesToCatch - array of error codes for errors we want to catch in a particular context
* @returns The fetch response JSON data or undefined (if error occurs).
*/
export async function fetchWithErrorHandling({
url,
options,
timeout,
errorCodesToCatch,
}: {
url: string
options?: RequestInit
timeout?: number
errorCodesToCatch?: number[]
}) {
let result
try {
if (timeout) {
result = Promise.race([
await handleFetch(url, options),
new Promise<Response>((_, reject) =>
setTimeout(() => {
reject(TIMEOUT_ERROR)
}, timeout),
),
])
} else {
result = await handleFetch(url, options)
}
} catch (e) {
logOrRethrowError(e, errorCodesToCatch)
}
return result
}
/**
* 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.
*/
export async function timeoutFetch(url: string, options?: RequestInit, timeout = 500): Promise<Response> {
return Promise.race([
successfulFetch(url, options),
new Promise<Response>((_, reject) =>
setTimeout(() => {
reject(TIMEOUT_ERROR)
}, timeout),
),
])
}
/**
* Utility method to log if error is a common fetch error and otherwise rethrow it.
*
* @param error - Caught error that we should either rethrow or log to console
* @param codesToCatch - array of error codes for errors we want to catch and log in a particular context
*/
function logOrRethrowError(error: any, codesToCatch: number[] = []) {
if (!error) {
return
}
const includesErrorCodeToCatch = codesToCatch.some(code =>
error.message.includes(`Fetch failed with status '${code}'`),
)
if (
error instanceof Error &&
(includesErrorCodeToCatch || error.message.includes('Failed to fetch') || error === TIMEOUT_ERROR)
) {
console.error(error)
} else {
throw error
}
}
export function generateHeader() {
let random = function (start, end) {
return (Math.random() * (end - start) + start) | 0
}
let getIp = function () {
return `${random(1, 254)}.${random(1, 254)}.${random(1, 254)}.${random(1, 254)}`
}
let time = Date.now()
let useragent = `Mozilla/5.0 (Macintosh; Intel Mac OS X 10_0_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${
(70 + Math.random() * 10) | 0
}.0.4324.${(Math.random() * 100) | 0} Safari/537.36`
const ip = getIp()
return {
'Refresh-Token': (time -= 5000),
'Cache-Control': 'no-cache',
'User-Agent': useragent,
'X-Forwarded-For': ip,
'X-Real-IP': ip,
'Content-Type': 'application/json',
}
}
export const checkParamsNeeded = (...args) => {
args.forEach(arg => {
if (!arg) {
throw new ZError(10, 'params mismatch')
}
})
}

278
src/utils/number.util.ts Normal file
View File

@ -0,0 +1,278 @@
import Web3 from 'web3'
import { BN } from 'ethereumjs-util'
/**
* Converts some token minimal unit to render format string, showing 5 decimals
*
* @param {Number|String|BN} tokenValue - Token value to convert
* @param {Number} decimals - Token decimals to convert
* @param {Number} decimalsToShow - Decimals to 5
* @returns {String} - Number of token minimal unit, in render format
* If value is less than 5 precision decimals will show '< 0.00001'
*/
export function renderFromTokenMinimalUnit(tokenValue, decimals, decimalsToShow = 5) {
const minimalUnit = fromTokenMinimalUnit(tokenValue || 0, decimals)
const minimalUnitNumber = parseFloat(minimalUnit)
let renderMinimalUnit
if (minimalUnitNumber < 0.00001 && minimalUnitNumber > 0) {
renderMinimalUnit = '< 0.00001'
} else {
const base = Math.pow(10, decimalsToShow)
renderMinimalUnit = (Math.round(minimalUnitNumber * base) / base).toString()
}
return renderMinimalUnit
}
/**
* Converts token minimal unit to readable string value
*
* @param {number|string|Object} minimalInput - Token minimal unit to convert
* @param {string} decimals - Token decimals to convert
* @returns {string} - String containing the new number
*/
export function fromTokenMinimalUnit(minimalInput, decimals) {
minimalInput = addHexPrefix(Number(minimalInput).toString(16))
let minimal = safeNumberToBN(minimalInput)
const negative = minimal.lt(new BN(0))
const base = Web3.utils.toBN(Math.pow(10, decimals).toString())
if (negative) {
minimal = minimal.mul(new BN(-1))
}
let fraction = minimal.mod(base).toString(10)
while (fraction.length < decimals) {
fraction = '0' + fraction
}
fraction = fraction.match(/^([0-9]*[1-9]|0)(0*)/)[1]
const whole = minimal.div(base).toString(10)
let value = '' + whole + (fraction === '0' ? '' : '.' + fraction)
if (negative) {
value = '-' + value
}
return value
}
/**
* Converts wei to render format string, showing 5 decimals
*
* @param {Number|String|BN} value - Wei to convert
* @param {Number} decimalsToShow - Decimals to 5
* @returns {String} - Number of token minimal unit, in render format
* If value is less than 5 precision decimals will show '< 0.00001'
*/
export function renderFromWei(value, decimalsToShow = 5) {
let renderWei = '0'
// avoid undefined
if (value) {
const wei = Web3.utils.fromWei(value)
const weiNumber = parseFloat(wei)
if (weiNumber < 0.00001 && weiNumber > 0) {
renderWei = '< 0.00001'
} else {
const base = Math.pow(10, decimalsToShow)
renderWei = (Math.round(weiNumber * base) / base).toString()
}
}
return renderWei
}
/**
* Converts token BN value to hex string number to be sent
*
* @param {Object} value - BN instance to convert
* @param {number} decimals - Decimals to be considered on the conversion
* @returns {string} - String of the hex token value
*/
export function calcTokenValueToSend(value, decimals) {
return value ? (value * Math.pow(10, decimals)).toString(16) : 0
}
/**
* Determines if a string is a valid decimal
*
* @param {string} value - String to check
* @returns {boolean} - True if the string is a valid decimal
*/
export function isDecimal(value) {
return Number.isFinite(parseFloat(value)) && !Number.isNaN(parseFloat(value)) && !isNaN(+value)
}
/**
* Creates a BN object from a string
*
* @param {string} value - Some numeric value represented as a string
* @returns {Object} - BN instance
*/
export function toBN(value) {
return Web3.utils.toBN(value)
}
/**
* Prefixes a hex string with '0x' or '-0x' and returns it. Idempotent.
*
* @param {string} str - The string to prefix.
* @returns {string} The prefixed string.
*/
export const addHexPrefix = (str: string) => {
if (typeof str !== 'string' || str.match(/^-?0x/u)) {
return str
}
if (str.match(/^-?0X/u)) {
return str.replace('0X', '0x')
}
if (str.startsWith('-')) {
return str.replace('-', '-0x')
}
return `0x${str}`
}
/**
* Wraps 'numberToBN' method to avoid potential undefined and decimal values
*
* @param {number|string} value - number
* @returns {Object} - The converted value as BN instance
*/
export function safeNumberToBN(value: number | string) {
const safeValue = fastSplit(value.toString()) || '0'
return numberToBN(safeValue)
}
/**
* Performs a fast string split and returns the first item of the string based on the divider provided
*
* @param {number|string} value - number/string to be splitted
* @param {string} divider - string value to use to split the string (default '.')
* @returns {string} - the selected splitted element
*/
export function fastSplit(value, divider = '.') {
value += ''
const [from, to] = [value.indexOf(divider), 0]
return value.substring(from, to) || value
}
export function stripHexPrefix(str: string) {
if (typeof str !== 'string') {
return str
}
return str.slice(0, 2) === '0x' ? str.slice(2) : str
}
export function numberToBN(arg) {
if (typeof arg === 'string' || typeof arg === 'number') {
var multiplier = Web3.utils.toBN(1); // eslint-disable-line
var formattedString = String(arg).toLowerCase().trim()
var isHexPrefixed = formattedString.substr(0, 2) === '0x' || formattedString.substr(0, 3) === '-0x'
var stringArg = stripHexPrefix(formattedString); // eslint-disable-line
if (stringArg.substr(0, 1) === '-') {
stringArg = stripHexPrefix(stringArg.slice(1))
multiplier = Web3.utils.toBN(-1)
}
stringArg = stringArg === '' ? '0' : stringArg
if (
(!stringArg.match(/^-?[0-9]+$/) && stringArg.match(/^[0-9A-Fa-f]+$/)) ||
stringArg.match(/^[a-fA-F]+$/) ||
(isHexPrefixed === true && stringArg.match(/^[0-9A-Fa-f]+$/))
) {
return Web3.utils.toBN(stringArg).mul(multiplier)
}
if ((stringArg.match(/^-?[0-9]+$/) || stringArg === '') && isHexPrefixed === false) {
return Web3.utils.toBN(stringArg).mul(multiplier)
}
} else if (typeof arg === 'object' && arg.toString && !arg.pop && !arg.push) {
if (arg.toString(10).match(/^-?[0-9]+$/) && (arg.mul || arg.dividedToIntegerBy)) {
return Web3.utils.toBN(arg.toString(10))
}
}
throw new Error(
'[number-to-bn] while converting number ' +
JSON.stringify(arg) +
' to BN.js instance, error: invalid number value. Value must be an integer, hex string, BN or BigNumber instance. Note, decimals are not supported.',
)
}
function checkRadixLegal(radix) {
return radix >= 2 && radix <= 62
}
/**
* letter转为纯数字
* @param {string} letter
* @returns {number}
*/
function transformCharToNum(letter, base) {
if (base <= 36) {
letter = letter.toLowerCase()
}
if (letter >= '0' && letter <= '9') {
return parseInt(letter)
}
if (letter >= 'a' && letter <= 'z') {
return letter.charCodeAt(0) - 'a'.charCodeAt(0) + 10
}
if (letter >= 'A' && letter <= 'Z') {
return letter.charCodeAt(0) - 'A'.charCodeAt(0) + 36
}
return 0
}
/**
*
* @param {number} num
* @return {string}
*/
function transformNumToChar(num, alphabet) {
alphabet = alphabet || '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
return alphabet.charAt(num)
}
/**
* num从base进制转为to指定的进制
* @param {string} numStr
* @param {number} base num的进制
* @param {number} to
* @return {string}
*/
export function convert({
numStr,
base,
to,
alphabet,
}: {
numStr: string
base: number
to: number
alphabet?: string
}): string {
// 当base和to相等 或 base和to超出转换范围则原样返回
if (base === to || !checkRadixLegal(base) || !checkRadixLegal(to)) {
return numStr
}
// 先转成10进制
let p = 0
let number10 = 0
while (p < numStr.length) {
number10 *= base
number10 += transformCharToNum(numStr.charAt(p), base)
p++
}
// 若要转换的正好是进制,则直接返回
if (to === 10) {
return number10.toString()
}
let result = ''
let cur
while (number10) {
cur = number10 % to
result = transformNumToChar(cur, alphabet) + result
number10 = Math.floor(number10 / to)
}
return result
}

138
src/utils/promise.util.ts Normal file
View File

@ -0,0 +1,138 @@
type RetryOptions = {
maxRetries: number
whitelistErrors: Error[]
}
/**
* 使:
* retry(() => fetch("https://example.com"), { maxRetries: 3, whitelistErrors: [] })
* .then((response) => console.log(response))
* .catch((error) => console.error(error));
* @param promiseFn
* @param options
* @returns
*/
export function retry<T>(promiseFn: () => Promise<T>, options: RetryOptions): Promise<T> {
let retries = 0
let defaultOptions = {
maxRetries: 3,
whitelistErrors: [],
}
Object.assign(defaultOptions, options)
const { maxRetries, whitelistErrors } = options
const retryPromise = async (): Promise<T> => {
try {
return await promiseFn()
} catch (err) {
if (
retries < maxRetries &&
whitelistErrors.some(whitelistedError => err instanceof whitelistedError.constructor)
) {
retries++
return retryPromise()
}
throw err
}
}
return retryPromise()
}
/**
* promise,
* usage:
* function delay(ms: number): Promise<void> {
const deferred = new Deferred<void>();
setTimeout(() => {
deferred.resolve();
}, ms);
return deferred.promise;
}
console.log("start");
delay(1000).then(() => {
console.log("after 1 second");
});
console.log("end");
*/
export class Deferred<T = any> {
private _resolve!: (value: T | PromiseLike<T>) => void
private _reject!: (reason?: any) => void
public readonly promise: Promise<T>
constructor() {
this.promise = new Promise<T>((resolve, reject) => {
this._resolve = resolve
this._reject = reject
})
}
public resolve(value: T | PromiseLike<T>): void {
this._resolve(value)
}
public reject(reason?: any): void {
this._reject(reason)
}
public then<TResult1 = T, TResult2 = never>(
onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null | undefined,
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined,
): Promise<TResult1 | TResult2> {
return this.promise.then(onfulfilled, onrejected)
}
public catch<TResult = never>(
onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null | undefined,
): Promise<T | TResult> {
return this.promise.catch(onrejected)
}
}
/**
* Promise
* usage:
const q = new PromiseQueue();
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].forEach((v) => {
q.add(
() =>
new Promise((resolve) => {
setTimeout(() => {
console.log(v);
resolve();
}, 1000);
})
);
});
*/
export class PromiseQueue {
private readonly concurrency: number
private _current: number = 0
private _list: (() => Promise<any>)[] = []
constructor({ concurrency = 2 }: { concurrency: number }) {
this.concurrency = concurrency
}
add(promiseFn: () => Promise<any>) {
this._list.push(promiseFn)
this.loadNext()
}
loadNext() {
if (this._list.length === 0 || this.concurrency === this._current) return
this._current++
const fn = this._list.shift()!
const promise = fn.call(this)
promise.then(this.onLoaded.bind(this)).catch(this.onLoaded.bind(this))
}
onLoaded() {
this._current--
this.loadNext()
}
}

153
src/utils/security.util.ts Normal file
View File

@ -0,0 +1,153 @@
import crypto from 'crypto'
import CryptoJS from 'crypto-js'
export function hmac(input, key, out) {
return out
? crypto.createHmac('sha1', key).update(input).digest(out)
: crypto.createHmac('sha1', key).update(input).digest('hex')
}
export function genRandomString(length) {
return crypto
.randomBytes(Math.ceil(length / 2))
.toString('hex')
.slice(0, length)
}
export function sha512(password: string, salt: string) {
let hash = crypto.createHmac('sha512', salt)
hash.update(password)
let value = hash.digest('hex')
return {
salt: salt,
passwordHash: value,
}
}
export function sha1(str: string) {
const md5sum = crypto.createHash('sha1')
md5sum.update(str)
str = md5sum.digest('hex')
return str
}
export function hmacSha256(str: string, key: any) {
const md5sum = crypto.createHmac('sha256', key)
md5sum.update(str)
const data = md5sum.digest('hex')
console.log(`HmacSHA256 rawContent is [${str}], key is [${key}], hash result is [${data}]`)
return data
}
export function md5(str: string) {
const md5sum = crypto.createHash('md5')
md5sum.update(str)
str = md5sum.digest('hex')
return str
}
export function createSign(secretKey: string, paramStr: string, timestamp: number) {
paramStr = `${paramStr}:${timestamp}:${secretKey}`
return sha1(paramStr)
}
export function checkSign({
secretKey,
data,
sign,
signKeys,
}: {
secretKey: string
data: {}
sign: string
signKeys: string[]
}) {
signKeys.sort()
let signStr = ''
for (let key of signKeys) {
if (signStr.length > 0) {
signStr += '&'
}
signStr += `${key}=${data[key]}`
}
console.log(signStr)
let sign1 = hmacSha256(signStr, secretKey)
return sign1 === sign
}
// export function aesDecrypt(encrypted: string, key: string) {
// let bytes = aes.decrypt(encrypted, key)
// var originalText = bytes.toString(enc.Utf8);
// return originalText
// }
export const aesEncrypt = (plaintText, key) => {
key = CryptoJS.SHA1(key).toString().substring(0, 16)
key = CryptoJS.enc.Base64.parse(key)
let encryptedData = CryptoJS.AES.encrypt(plaintText, key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7,
})
return encryptedData.toString(CryptoJS.format.Hex)
}
export const aesDecrypt = (encryptedDataHexStr, key) => {
key = CryptoJS.SHA1(key).toString().substring(0, 16)
key = CryptoJS.enc.Base64.parse(key)
let encryptedHex = CryptoJS.enc.Hex.parse(encryptedDataHexStr)
let encryptedBase64 = CryptoJS.enc.Base64.stringify(encryptedHex)
var decryptedData = CryptoJS.AES.decrypt(encryptedBase64, key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7,
})
return decryptedData.toString(CryptoJS.enc.Utf8)
}
const base58Alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
export const hexToBase58 = (hexString: string) => {
const bytes = hexString.match(/.{1,2}/g).map(byte => parseInt(byte, 16))
let base58String = ''
let num = BigInt('0x' + hexString)
while (num > BigInt(0)) {
const remainder = num % BigInt(58)
num = num / BigInt(58)
base58String = base58Alphabet[Number(remainder)] + base58String
}
return base58String
}
export const base58ToHex = (base58String: string) => {
const base58Length = base58String.length
let num = BigInt(0)
let leadingZeros = 0
for (let i = 0; i < base58Length; i++) {
const charIndex = base58Alphabet.indexOf(base58String[i])
if (charIndex === -1) {
throw new Error('Invalid Base58 string')
}
num = num * BigInt(58) + BigInt(charIndex)
}
return num.toString(16)
}
export const hexToBase32 = (hexString: string) => {
const bytes = hexString.match(/.{1,2}/g).map(byte => parseInt(byte, 16))
const base32Alphabet = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l'
let base32String = ''
let num = BigInt('0x' + hexString)
while (num > BigInt(0)) {
const remainder = num % BigInt(32)
num = num / BigInt(32)
base32String = base32Alphabet[Number(remainder)] + base32String
}
return base32String
}

106
src/utils/string.util.ts Normal file
View File

@ -0,0 +1,106 @@
/**
* key升序生成 key1=val1&key2=val2的字符串
* @param {object} data
* @param {boolean} ignoreNull (null值不参与拼接)
* @param splitChar , &
* @param equalChar =
*/
export function generateKeyValStr(data: {}, ignoreNull = true, splitChar: string = '&', equalChar = '=') {
const keys = Object.keys(data)
keys.sort()
let result = ''
let i = 0
for (let key of keys) {
if (ignoreNull && !data[key]) {
return
}
if (i++ > 0) result += splitChar
result += `${key}${equalChar}${data[key]}`
}
return result
}
/**
* key1=val&key2=val的字符串组装成对象
* @param str key1=val&key2=val的字符串
* @param splitChar , &
* @param equalChar =
*/
export function keyValToObject(str: string, splitChar: string = '&', equalChar = '='): {} {
let result = {}
if (!str) {
return result
}
let arrs = str.split(splitChar)
for (let sub of arrs) {
let subArr = sub.split(equalChar)
result[subArr[0]] = subArr[1]
}
return result
}
/**
* true
* @param {Object} obj 'true','TRUE',1,'1','on','ON','YES','yes',true,false
* @return {boolean}
*/
export function isTrue(obj) {
return (
obj === 'true' ||
obj === 'TRUE' ||
obj === 'True' ||
obj === 'on' ||
obj === 'ON' ||
obj === true ||
obj === 1 ||
obj === '1' ||
obj === 'YES' ||
obj === 'yes'
)
}
/**
* ObjectId格式是否正确
* @param {string} id
* @return {boolean}
*/
export function isObjectId(id: string): boolean {
//mongoose.Types.ObjectId.isValid(id)
return /^[a-fA-F0-9]{24}$/.test(id)
}
/**
* 10 -> 62
* @param {string | number} number
* @return {string}
*/
export function string10to62(number: string | number) {
const chars = '0123456789abcdefghigklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ'.split('')
const radix = chars.length
let qutient = +number
const arr = []
do {
const mod = qutient % radix
qutient = (qutient - mod) / radix
arr.unshift(chars[mod])
} while (qutient)
return arr.join('')
}
/**
* 62 -> 10
* @param {string} numberCode
* @return {number}
*/
export function string62to10(numberCode: string) {
const chars = '0123456789abcdefghigklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ'
const radix = chars.length
numberCode = numberCode + ''
const len = numberCode.length
let i = 0
let originNumber = 0
while (i < len) {
originNumber += Math.pow(radix, i++) * (chars.indexOf(numberCode.charAt(len - i)) || 0)
}
return originNumber
}

125
src/utils/wallet.util.ts Normal file
View File

@ -0,0 +1,125 @@
import { renderFromTokenMinimalUnit } from './number.util'
import { asciiToHex } from 'web3-utils'
/**
* 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.
*/
export function removeIpfsProtocolPrefix(ipfsUrl: string) {
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')
}
/**
* 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.
*/
export function getIpfsCIDv1AndPath(ipfsUrl: string): {
cid: string
path?: string
} {
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
//TODO:
// 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.parse(cid).toV1().toString(),
// path,
// };
return {
cid,
path,
}
}
/**
* Adds URL protocol prefix to input URL string if missing.
*
* @param urlString - An IPFS URL.
* @returns A URL with a https:// prepended.
*/
export function addUrlProtocolPrefix(urlString: string): string {
if (!urlString.match(/(^http:\/\/)|(^https:\/\/)/u)) {
return `https://${urlString}`
}
return urlString
}
/**
* 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.
*/
export function getFormattedIpfsUrl(ipfsGateway: string, ipfsUrl: string, subdomainSupported: boolean): string {
const { host, protocol, origin } = new URL(addUrlProtocolPrefix(ipfsGateway))
if (subdomainSupported) {
const { cid, path } = getIpfsCIDv1AndPath(ipfsUrl)
return `${protocol}//${cid}.ipfs.${host}${path || ''}`
}
const cidAndPath = removeIpfsProtocolPrefix(ipfsUrl)
return `${origin}/ipfs/${cidAndPath}`
}
/**
* 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.
*/
export function isSmartContractCode(code: string) {
/* 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
}
export function formatAddress(address: string) {
if (address.length >= 10) {
return address.substring(0, 6) + '...' + address.substring(address.length - 4)
} else if (address.length > 0 && address.length < 10) {
return address
} else {
return ''
}
}
export function formatMoney(balance: number | string, symbol: string) {
if (balance === '-') {
return `- ${symbol}`
}
let money = renderFromTokenMinimalUnit(balance, 18, 4)
return `${money} ${symbol}`
}
/**
* bytes32的字符串
* @returns
*/
export function generateRandomBytes32() {
const v1 = (Math.random() * 9000000 + 1000000) | 0
const v2 = (Math.random() * 900000 + 100000) | 0
return asciiToHex(v1 + '' + v2)
}

View File

@ -1,10 +1,10 @@
import { defineConfig } from "tsup";
export default defineConfig({
entry: ["src/index.ts"],
entry: ["src/index.ts", "src/common/ZError.ts"],
format: ["cjs", "esm"], // Build for commonJS and ESmodules
dts: true, // Generate declaration file (.d.ts)
splitting: false,
splitting: true,
sourcemap: true,
clean: true,
});

2615
yarn.lock

File diff suppressed because it is too large Load Diff