@ensdomains/unruggable-gateways
Version:
Trustless Ethereum Multichain CCIP-Read Gateway
90 lines (89 loc) • 3.09 kB
JavaScript
import { AbiCoder } from 'ethers/abi';
import { id as keccakStr } from 'ethers/hash';
export const ABI_CODER = AbiCoder.defaultAbiCoder();
// https://adraffy.github.io/keccak.js/test/demo.html#algo=keccak-256&s=&escape=1&encoding=utf8
// "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
export const NULL_CODE_HASH = keccakStr('');
export const EVM_BLOCKHASH_DEPTH = 256;
// TODO: make this a function of Chain
export const MAINNET_BLOCK_SEC = 12;
export const LATEST_BLOCK_TAG = 'latest';
// hex-prefixed w/o zero-padding
export function toUnpaddedHex(x) {
return '0x' + BigInt(x).toString(16);
}
// hex-prefixed left-pad w/truncation
export function toPaddedHex(x, width = 32) {
const i = x === '0x' ? 0n : BigInt.asUintN(width << 3, BigInt(x));
return '0x' + i.toString(16).padStart(width << 1, '0');
}
// manual polyfill: ES2024
export function withResolvers() {
let resolve;
let reject;
const promise = new Promise((ful, rej) => {
resolve = ful;
reject = rej;
});
return { promise, resolve, reject };
}
export function isBlockTag(x) {
return typeof x === 'string' && !x.startsWith('0x');
}
export async function fetchBlock(provider, relBlockTag = LATEST_BLOCK_TAG) {
if (!isBlockTag(relBlockTag)) {
let i = BigInt(relBlockTag);
if (i < 0)
i += await fetchBlockNumber(provider);
relBlockTag = toUnpaddedHex(i);
}
const json = await provider.send('eth_getBlockByNumber', [
relBlockTag,
false,
]);
if (!json)
throw new Error(`no block: ${relBlockTag}`);
return json;
}
export async function fetchBlockFromHash(provider, blockHash) {
const block = await provider.send('eth_getBlockByHash', [blockHash, false]);
if (!block)
throw new Error(`no blockhash: ${blockHash}`);
return block;
}
// avoid an rpc if possible
// use negative (-100) for offset from "latest" (#-100)
export async function fetchBlockNumber(provider, relBlockTag = LATEST_BLOCK_TAG) {
if (relBlockTag === LATEST_BLOCK_TAG) {
return BigInt(await provider.send('eth_blockNumber', []));
}
else if (isBlockTag(relBlockTag)) {
const info = await fetchBlock(provider, relBlockTag);
return BigInt(info.number);
}
else {
let i = BigInt(relBlockTag);
if (i < 0)
i += await fetchBlockNumber(provider);
return i;
}
}
// avoid an rpc if possible
// convert negative (-100) => absolute (#-100)
export async function fetchBlockTag(provider, relBlockTag = LATEST_BLOCK_TAG) {
return isBlockTag(relBlockTag)
? relBlockTag
: fetchBlockNumber(provider, relBlockTag);
}
export function isEthersError(err) {
return err instanceof Error && 'code' in err && 'shortMessage' in err;
}
export function isRevert(err) {
return isEthersError(err) && err.code === 'CALL_EXCEPTION';
}
export function isRPCError(err, code) {
return (isEthersError(err) &&
err.error instanceof Object &&
'code' in err.error &&
err.error.code === code);
}