Remove duplicated utilities Upgrade some tooling including typescript and next Fix error from missing background imagepull/52/head
parent
131e38da65
commit
79a3101bd7
@ -1,38 +0,0 @@ |
||||
import jazzicon from '@metamask/jazzicon'; |
||||
import { CSSProperties, memo } from 'react'; |
||||
|
||||
import { isValidAddress, normalizeAddress } from '../../utils/addresses'; |
||||
|
||||
type Props = { |
||||
address: string; |
||||
size?: number; |
||||
styles?: CSSProperties; |
||||
}; |
||||
|
||||
// This should match metamask: https://github.com/MetaMask/metamask-extension/blob/master/ui/helpers/utils/icon-factory.js#L84
|
||||
function addressToSeed(address: string) { |
||||
const addrStub = normalizeAddress(address).slice(2, 10); |
||||
return parseInt(addrStub, 16); |
||||
} |
||||
|
||||
function _Identicon({ address, size: _size, styles }: Props) { |
||||
const size = _size ?? 34; |
||||
|
||||
if (!isValidAddress(address)) return null; |
||||
|
||||
const jazziconResult = jazzicon(size, addressToSeed(address)); |
||||
|
||||
return ( |
||||
<div |
||||
style={{ height: size, ...styles }} |
||||
ref={(nodeElement) => { |
||||
if (nodeElement) { |
||||
nodeElement.innerHTML = ''; |
||||
nodeElement.appendChild(jazziconResult); |
||||
} |
||||
}} |
||||
></div> |
||||
); |
||||
} |
||||
|
||||
export const Identicon = memo(_Identicon); |
@ -1,12 +1,12 @@ |
||||
import { ensureLeading0x, trimLeading0x } from '../../../utils/addresses'; |
||||
import { ensure0x, strip0x } from '@hyperlane-xyz/utils'; |
||||
|
||||
export function stringToPostgresBytea(hexString: string) { |
||||
const trimmed = trimLeading0x(hexString).toLowerCase(); |
||||
const trimmed = strip0x(hexString).toLowerCase(); |
||||
const prefix = `\\x`; |
||||
return `${prefix}${trimmed}`; |
||||
} |
||||
|
||||
export function postgresByteaToString(byteString: string) { |
||||
if (!byteString || byteString.length < 4) throw new Error('Invalid byte string'); |
||||
return ensureLeading0x(byteString.substring(2)); |
||||
return ensure0x(byteString.substring(2)); |
||||
} |
||||
|
@ -1,69 +0,0 @@ |
||||
import { getAddress, isAddress } from '@ethersproject/address'; |
||||
|
||||
import { logger } from './logger'; |
||||
|
||||
// Validates content and checksum
|
||||
export function isValidAddress(address: string) { |
||||
// Need to catch because ethers' isAddress throws in some cases (bad checksum)
|
||||
try { |
||||
const isValid = address && isAddress(address); |
||||
return !!isValid; |
||||
} catch (error) { |
||||
logger.warn('Invalid address', error, address); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
// Faster then above and avoids exceptions but less thorough
|
||||
const addressRegex = /^(0x)?[a-fA-F0-9]{40}$/; |
||||
export function isValidAddressFast(address: string) { |
||||
return addressRegex.test(address); |
||||
} |
||||
|
||||
export function validateAddress(address: string, context: string) { |
||||
if (!address || !isAddress(address)) { |
||||
const errorMsg = `Invalid addresses for ${context}: ${address}`; |
||||
logger.error(errorMsg); |
||||
throw new Error(errorMsg); |
||||
} |
||||
} |
||||
|
||||
export function normalizeAddress(address: string) { |
||||
validateAddress(address, 'normalize'); |
||||
return getAddress(address); |
||||
} |
||||
|
||||
export function shortenAddress(address: string, capitalize?: boolean) { |
||||
try { |
||||
const normalized = normalizeAddress(address); |
||||
const shortened = |
||||
normalized.substring(0, 5) + '...' + normalized.substring(normalized.length - 4); |
||||
return capitalize ? capitalizeAddress(shortened) : shortened; |
||||
} catch (error) { |
||||
logger.error('Unable to shorten invalid address', address, error); |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
export function capitalizeAddress(address: string) { |
||||
return '0x' + address.substring(2).toUpperCase(); |
||||
} |
||||
|
||||
export function areAddressesEqual(a1: string, a2: string) { |
||||
validateAddress(a1, 'compare'); |
||||
validateAddress(a2, 'compare'); |
||||
return getAddress(a1) === getAddress(a2); |
||||
} |
||||
|
||||
export function trimLeading0x(input: string) { |
||||
return input.startsWith('0x') ? input.substring(2) : input; |
||||
} |
||||
|
||||
export function ensureLeading0x(input: string) { |
||||
return input.startsWith('0x') ? input : `0x${input}`; |
||||
} |
||||
|
||||
const txHashRegex = /^(0x)?([A-Fa-f0-9]{64})$/; |
||||
export function isValidTransactionHash(input: string) { |
||||
return txHashRegex.test(input); |
||||
} |
@ -1,78 +0,0 @@ |
||||
import { formatUnits, parseUnits } from '@ethersproject/units'; |
||||
import BigNumber from 'bignumber.js'; |
||||
|
||||
import { DISPLAY_DECIMALS, MIN_ROUNDED_VALUE } from '../consts/values'; |
||||
|
||||
import { logger } from './logger'; |
||||
|
||||
export type NumberT = BigNumber.Value; |
||||
|
||||
export function fromWei(value: NumberT | null | undefined, toUnitName?: string): number { |
||||
if (!value) return 0; |
||||
const valueString = value.toString().trim(); |
||||
const flooredValue = new BigNumber(valueString).toFixed(0, BigNumber.ROUND_FLOOR); |
||||
return parseFloat(formatUnits(flooredValue, toUnitName)); |
||||
} |
||||
|
||||
// Similar to fromWei above but rounds to set number of decimals
|
||||
// with a minimum floor, configured per token
|
||||
export function fromWeiRounded( |
||||
value: NumberT | null | undefined, |
||||
toUnitName?: string, |
||||
roundDownIfSmall = true, |
||||
decimals = DISPLAY_DECIMALS, |
||||
): string { |
||||
if (!value) return '0'; |
||||
const flooredValue = new BigNumber(value).toFixed(0, BigNumber.ROUND_FLOOR); |
||||
const amount = new BigNumber(formatUnits(flooredValue, toUnitName)); |
||||
if (amount.isZero()) return '0'; |
||||
|
||||
// If amount is less than min value
|
||||
if (amount.lt(MIN_ROUNDED_VALUE)) { |
||||
if (roundDownIfSmall) return '0'; |
||||
else return MIN_ROUNDED_VALUE.toString(); |
||||
} |
||||
|
||||
return amount.toFixed(decimals).toString(); |
||||
} |
||||
|
||||
export function toWei(value: NumberT | null | undefined): BigNumber { |
||||
if (!value) return new BigNumber(0); |
||||
const valueString = value.toString().trim(); |
||||
const components = valueString.split('.'); |
||||
if (components.length === 1) { |
||||
return new BigNumber(parseUnits(valueString).toString()); |
||||
} else if (components.length === 2) { |
||||
const trimmedFraction = components[1].substring(0); |
||||
return new BigNumber(parseUnits(`${components[0]}.${trimmedFraction}`).toString()); |
||||
} else { |
||||
throw new Error(`Cannot convert ${valueString} to wei`); |
||||
} |
||||
} |
||||
|
||||
export function tryParseAmount(value: NumberT | null | undefined): BigNumber | null { |
||||
try { |
||||
if (!value) return null; |
||||
const parsed = new BigNumber(value); |
||||
if (!parsed || parsed.isNaN() || !parsed.isFinite()) return null; |
||||
else return parsed; |
||||
} catch (error) { |
||||
logger.warn('Error parsing amount', value); |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
// Checks if an amount is equal of nearly equal to balance within a small margin of error
|
||||
// Necessary because amounts in the UI are often rounded
|
||||
export function areAmountsNearlyEqual(amountInWei1: BigNumber, amountInWei2: NumberT) { |
||||
const minValueWei = toWei(MIN_ROUNDED_VALUE); |
||||
// Is difference btwn amount and balance less than min amount shown for token
|
||||
return amountInWei1.minus(amountInWei2).abs().lt(minValueWei); |
||||
} |
||||
|
||||
export function BigNumberMin(bn1: BigNumber, bn2: BigNumber) { |
||||
return bn1.gte(bn2) ? bn2 : bn1; |
||||
} |
||||
export function BigNumberMax(bn1: BigNumber, bn2: BigNumber) { |
||||
return bn1.lte(bn2) ? bn2 : bn1; |
||||
} |
@ -1,22 +0,0 @@ |
||||
import { logger } from './logger'; |
||||
|
||||
export function toBase64(data: any): string | undefined { |
||||
try { |
||||
if (!data) throw new Error('No data to encode'); |
||||
return btoa(JSON.stringify(data)); |
||||
} catch (error) { |
||||
logger.error('Unable to serialize + encode data to base64', data); |
||||
return undefined; |
||||
} |
||||
} |
||||
|
||||
export function fromBase64<T>(data: string | string[]): T | undefined { |
||||
try { |
||||
if (!data) throw new Error('No data to decode'); |
||||
const msg = Array.isArray(data) ? data[0] : data; |
||||
return JSON.parse(atob(msg)); |
||||
} catch (error) { |
||||
logger.error('Unable to decode + deserialize data from base64', data); |
||||
return undefined; |
||||
} |
||||
} |
@ -0,0 +1,9 @@ |
||||
import BigNumber from 'bignumber.js'; |
||||
|
||||
export function BigNumberMin(bn1: BigNumber, bn2: BigNumber) { |
||||
return bn1.gte(bn2) ? bn2 : bn1; |
||||
} |
||||
|
||||
export function BigNumberMax(bn1: BigNumber, bn2: BigNumber) { |
||||
return bn1.lte(bn2) ? bn2 : bn1; |
||||
} |
@ -1,16 +0,0 @@ |
||||
import { logger } from './logger'; |
||||
import { trimToLength } from './string'; |
||||
|
||||
export function errorToString(error: any, maxLength = 300) { |
||||
if (!error) return 'Unknown Error'; |
||||
if (typeof error === 'string') return trimToLength(error, maxLength); |
||||
if (typeof error === 'number') return `Error code: ${error}`; |
||||
const details = error.message || error.reason || error; |
||||
if (typeof details === 'string') return trimToLength(details, maxLength); |
||||
return trimToLength(JSON.stringify(details), maxLength); |
||||
} |
||||
|
||||
export function logAndThrow(message: string, error?: any) { |
||||
logger.error(message, error); |
||||
throw new Error(message); |
||||
} |
@ -1,30 +0,0 @@ |
||||
export function invertKeysAndValues(data: any) { |
||||
return Object.fromEntries(Object.entries(data).map(([key, value]) => [value, key])); |
||||
} |
||||
|
||||
// Get the subset of the object from key list
|
||||
export function pick<K extends string | number, V = any>(obj: Record<K, V>, keys: K[]) { |
||||
const ret: Partial<Record<K, V>> = {}; |
||||
for (const key of keys) { |
||||
ret[key] = obj[key]; |
||||
} |
||||
return ret as Record<K, V>; |
||||
} |
||||
|
||||
// Remove a particular key from an object if it exists
|
||||
export function omit<K extends string | number, V = any>(obj: Record<K, V>, key: K) { |
||||
const ret: Partial<Record<K, V>> = {}; |
||||
for (const k of Object.keys(obj)) { |
||||
if (k === key) continue; |
||||
ret[k] = obj[k]; |
||||
} |
||||
return ret as Record<K, V>; |
||||
} |
||||
|
||||
// Returns an object with the keys as values from an array and value set to true
|
||||
export function arrayToObject(keys: Array<string | number>, val = true) { |
||||
return keys.reduce((result, k) => { |
||||
result[k] = val; |
||||
return result; |
||||
}, {}); |
||||
} |
@ -1,20 +0,0 @@ |
||||
import { logger } from './logger'; |
||||
import { sleep } from './timeout'; |
||||
|
||||
// If all the tries fail it raises the last thrown exception
|
||||
export async function retryAsync<T>(runner: () => T, attempts = 3, delay = 500) { |
||||
let saveError; |
||||
for (let i = 0; i < attempts; i++) { |
||||
try { |
||||
const result = await runner(); |
||||
if (result) return result; |
||||
else throw new Error('Empty result'); |
||||
} catch (error) { |
||||
logger.error(`retryAsync: Failed to execute function on attempt #${i}:`, error); |
||||
saveError = error; |
||||
await sleep(delay * (i + 1)); |
||||
} |
||||
} |
||||
logger.error(`retryAsync: All attempts failed`); |
||||
throw saveError; |
||||
} |
@ -1,4 +0,0 @@ |
||||
export function isNullish<T>(val: T) { |
||||
if (val === null || val === undefined) return true; |
||||
else return false; |
||||
} |
Loading…
Reference in new issue