165 lines
4.1 KiB
TypeScript
165 lines
4.1 KiB
TypeScript
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
import { PublicKey } from '@solana/web3.js';
|
|
import BN from 'bn.js';
|
|
|
|
export function isValidPublicKey(key) {
|
|
if (!key) {
|
|
return false;
|
|
}
|
|
try {
|
|
new PublicKey(key);
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
export async function sleep(ms) {
|
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
}
|
|
|
|
export const percentFormat = new Intl.NumberFormat(undefined, {
|
|
style: 'percent',
|
|
minimumFractionDigits: 2,
|
|
maximumFractionDigits: 2,
|
|
});
|
|
|
|
export function floorToDecimal(
|
|
value: number,
|
|
decimals: number | undefined | null,
|
|
) {
|
|
return decimals
|
|
? Math.floor(value * 10 ** decimals) / 10 ** decimals
|
|
: Math.floor(value);
|
|
}
|
|
|
|
export function roundToDecimal(
|
|
value: number,
|
|
decimals: number | undefined | null,
|
|
) {
|
|
return decimals ? Math.round(value * 10 ** decimals) / 10 ** decimals : value;
|
|
}
|
|
|
|
export function getDecimalCount(value): number {
|
|
if (
|
|
!isNaN(value) &&
|
|
Math.floor(value) !== value &&
|
|
value.toString().includes('.')
|
|
)
|
|
return value.toString().split('.')[1].length || 0;
|
|
if (
|
|
!isNaN(value) &&
|
|
Math.floor(value) !== value &&
|
|
value.toString().includes('e')
|
|
)
|
|
return parseInt(value.toString().split('e-')[1] || '0');
|
|
return 0;
|
|
}
|
|
|
|
export function divideBnToNumber(numerator: BN, denominator: BN): number {
|
|
const quotient = numerator.div(denominator).toNumber();
|
|
const rem = numerator.umod(denominator);
|
|
const gcd = rem.gcd(denominator);
|
|
return quotient + rem.div(gcd).toNumber() / denominator.div(gcd).toNumber();
|
|
}
|
|
|
|
export function getTokenMultiplierFromDecimals(decimals: number): BN {
|
|
return new BN(10).pow(new BN(decimals));
|
|
}
|
|
|
|
const localStorageListeners = {};
|
|
|
|
export function useLocalStorageStringState(
|
|
key: string,
|
|
defaultState: string | null = null,
|
|
): [string | null, (newState: string | null) => void] {
|
|
const state = localStorage.getItem(key) || defaultState;
|
|
|
|
const [, notify] = useState(key + '\n' + state);
|
|
|
|
useEffect(() => {
|
|
if (!localStorageListeners[key]) {
|
|
localStorageListeners[key] = [];
|
|
}
|
|
localStorageListeners[key].push(notify);
|
|
return () => {
|
|
localStorageListeners[key] = localStorageListeners[key].filter(
|
|
(listener) => listener !== notify,
|
|
);
|
|
if (localStorageListeners[key].length === 0) {
|
|
delete localStorageListeners[key];
|
|
}
|
|
};
|
|
}, [key]);
|
|
|
|
const setState = useCallback<(newState: string | null) => void>(
|
|
(newState) => {
|
|
const changed = state !== newState;
|
|
if (!changed) {
|
|
return;
|
|
}
|
|
|
|
if (newState === null) {
|
|
localStorage.removeItem(key);
|
|
} else {
|
|
localStorage.setItem(key, newState);
|
|
}
|
|
localStorageListeners[key]?.forEach((listener) =>
|
|
listener(key + '\n' + newState),
|
|
);
|
|
},
|
|
[state, key],
|
|
);
|
|
|
|
return [state, setState];
|
|
}
|
|
|
|
export function useLocalStorageState<T = any>(
|
|
key: string,
|
|
defaultState: T | null = null,
|
|
): [T, (newState: T) => void] {
|
|
let [stringState, setStringState] = useLocalStorageStringState(
|
|
key,
|
|
JSON.stringify(defaultState),
|
|
);
|
|
return [
|
|
useMemo(() => stringState && JSON.parse(stringState), [stringState]),
|
|
(newState) => setStringState(JSON.stringify(newState)),
|
|
];
|
|
}
|
|
|
|
export function useEffectAfterTimeout(effect, timeout) {
|
|
useEffect(() => {
|
|
const handle = setTimeout(effect, timeout);
|
|
return () => clearTimeout(handle);
|
|
});
|
|
}
|
|
|
|
export function useListener(emitter, eventName) {
|
|
const [, forceUpdate] = useState(0);
|
|
useEffect(() => {
|
|
const listener = () => forceUpdate((i) => i + 1);
|
|
emitter.on(eventName, listener);
|
|
return () => emitter.removeListener(eventName, listener);
|
|
}, [emitter, eventName]);
|
|
}
|
|
|
|
export function abbreviateAddress(address: PublicKey, size = 4) {
|
|
const base58 = address.toBase58();
|
|
return base58.slice(0, size) + '…' + base58.slice(-size);
|
|
}
|
|
|
|
export function isEqual(obj1, obj2, keys) {
|
|
if (!keys && Object.keys(obj1).length !== Object.keys(obj2).length) {
|
|
return false;
|
|
}
|
|
keys = keys || Object.keys(obj1);
|
|
for (const k of keys) {
|
|
if (obj1[k] !== obj2[k]) {
|
|
// shallow comparison
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|