/*!----------------------------------------------------------- * Copyright (c) IJS Technologies. All rights reserved. * Released under dual AGPLv3/commercial license * https://ijs.network *-----------------------------------------------------------*/ import { Wallet } from "./wallet"; import { IGetMerkleLeafDataOptions, IGetMerkleProofOptions, IMerkleTreeOptions, MerkleTree } from './merkleTree'; import {IMerkleTreeAbiItem, EIP712TypeMap, IEIP712Domain, MessageTypes, TypedMessage} from './nodeTypes'; import { BigNumber } from "bignumber.js"; import { EIP712DomainAbi } from "./constants"; let EthersLib: any; if (typeof window !== "undefined") { EthersLib = window["ethers"]; } else { EthersLib = require("ethers"); } export function sleep(millisecond: number) { return new Promise(function (resolve) { setTimeout(function () { resolve(null); }, millisecond); }); }; export function numberToBytes32(value: number | BigNumber, prefix?: boolean) { let v = new BigNumber(value).toString(16) v = v.replace("0x", ""); v = padLeft(v, 64); if (prefix) v = '0x' + v return v; } export function padLeft(string: string, chars: number, sign?: string): string { return new Array(chars - string.length + 1).join(sign ? sign : "0") + string; } export function padRight(string: string, chars: number, sign?: string): string { return string + new Array(chars - string.length + 1).join(sign ? sign : "0"); } type stringArray = string | _stringArray; interface _stringArray extends Array { } export function stringToBytes32(value: string | stringArray): string | string[] { if (Array.isArray(value)) { let result = []; for (let i = 0; i < value.length; i++) { result.push(stringToBytes32(value[i])); } return result; } else { if (value.length === 66 && value.startsWith('0x')) { return value; } let hex = '0x' + Array.from(value) .map(char => char.charCodeAt(0).toString(16).padStart(2, '0')) .join(''); if (hex.length < 66) { hex = hex.padEnd(66, '0'); } return hex; } } export function stringToBytes(value: string | stringArray, nByte?: number): string | string[] { if (Array.isArray(value)) { let result = []; for (let i = 0; i < value.length; i++) { result.push(stringToBytes(value[i], nByte)); } return result; } else { if (nByte) { if (new RegExp(`^0x[0-9a-fA-F]{${2 * nByte}}$`).test(value)) { return value; } else if (/^0x([0-9a-fA-F][0-9a-fA-F])*$/.test(value)) { if (value.length >= ((nByte * 2) + 2)) { return value; } else { return "0x" + value.substring(2).padEnd(nByte * 2, "0"); } } else if (/^([0-9a-fA-F][0-9a-fA-F])+$/.test(value)) { if (value.length >= (nByte * 2)) { return value; } else { return "0x" + value.padEnd(nByte * 2, "0"); } } else { const hex = "0x" + Array.from(value) .map(char => char.charCodeAt(0).toString(16).padStart(2, "0")) .join(""); return hex.padEnd(nByte * 2 + 2, "0"); } } else { if (/^0x([0-9a-fA-F][0-9a-fA-F])*$/.test(value)) { return value; } else if (/^([0-9a-fA-F][0-9a-fA-F])+$/.test(value)) { return "0x" + value; } else { return "0x" + Array.from(value) .map(char => char.charCodeAt(0).toString(16).padStart(2, "0")) .join(""); } } } } export function addressToBytes32(value: string, prefix?: boolean): string { let v = value v = v.replace("0x", ""); v = padLeft(v, 64); if (prefix) v = '0x' + v; return v; } export function bytes32ToAddress(value: string): string { return '0x' + value.replace('0x000000000000000000000000', ''); } export function bytes32ToString(value: string): string { if (!value.startsWith("0x")) { throw new Error("Invalid bytes32 value. It must start with '0x'."); } const hex = value.slice(2); let result = ""; for (let i = 0; i < hex.length; i += 2) { const charCode = parseInt(hex.substring(i, i + 2), 16); if (charCode === 0) { break; } result += String.fromCharCode(charCode); } return result; } export function addressToBytes32Right(value: string, prefix?: boolean): string { let v = value v = v.replace("0x", ""); v = padRight(v, 64); if (prefix) v = '0x' + v; return v; } export function toNumber(value: string | number | BigNumber | bigint): number { if (typeof value === 'number') { return value; } else if (typeof value === 'string') { return new BigNumber(value).toNumber(); } else if (typeof value === "bigint") { return Number(value); } else { return (value as BigNumber).toNumber(); } } export function toDecimals(value: BigNumber | number | string, decimals?: number): BigNumber { decimals = decimals || 18; return new BigNumber(value).shiftedBy(decimals); } export function fromDecimals(value: BigNumber | number | string, decimals?: number): BigNumber { decimals = decimals || 18; return new BigNumber(value).shiftedBy(-decimals); } export function toString(value: any) { if (Array.isArray(value)) { let result = []; for (let i = 0; i < value.length; i++) { result.push(toString(value[i])); } return result; } else if (typeof value === "number") return value.toString(10); else if (BigNumber.isBigNumber(value)) return value.toFixed(); else return value; } export const nullAddress = "0x0000000000000000000000000000000000000000"; export function constructTypedMessageData( domain: IEIP712Domain, customTypes: EIP712TypeMap, primaryType: string, message: Record ) { let data: TypedMessage = { types: { EIP712Domain: EIP712DomainAbi, ...customTypes }, primaryType: primaryType, domain: domain, message: message }; return data; } export function soliditySha3(...val: any[]) { const ethers = EthersLib.ethers; const types: string[] = []; const values: any[] = []; val.forEach(arg => { if (typeof arg === 'object' && arg !== null && 'type' in arg && 'value' in arg) { types.push(arg.type); values.push(arg.value); } else { throw new Error("Invalid input format for soliditySha3. Expected {type: string, value: any}."); } }); const soliditySha3Value = ethers.solidityPackedKeccak256(types, values); return soliditySha3Value; } export function toChecksumAddress(address: string) { const ethers = EthersLib.ethers; return ethers.getAddress(address); } export function getSha3HashBufferFunc(wallet: Wallet, abi: IMerkleTreeAbiItem[]) { return (leafData: Record): string => { let encodePackedInput = abi.map((abiItem) => { return { t: abiItem.type, v: leafData[abiItem.name] } }) let hex = wallet.soliditySha3.apply(wallet, encodePackedInput) return hex; }; } export function generateMerkleTree( wallet: Wallet, options: IMerkleTreeOptions ) { const merkleTree = new MerkleTree(wallet, options); return merkleTree; } export function getMerkleProofs( wallet: Wallet, tree: MerkleTree, options: IGetMerkleProofOptions ) { let proofs: string[][] = []; if (options.key) { proofs = tree.getHexProofsByKey(options.key); } else if (options.leafData) { let abi = tree.getABI(); const hashFunc = getSha3HashBufferFunc(wallet, abi); let leaf = hashFunc(options.leafData); proofs.push(tree.getHexProof(leaf)); } return proofs; } export function getMerkleLeavesData( tree: MerkleTree, options: IGetMerkleLeafDataOptions ) { let data: Record[]; if (options.key) { data = tree.getLeavesDataByKey(options.key); } else if (options.hash) { data.push(tree.getLeafData(options.hash)); } return data; }