@unruggable/gateways
Version:
Trustless Ethereum Multichain CCIP-Read Gateway
136 lines (135 loc) • 4.82 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.LATEST_BLOCK_TAG = exports.MAINNET_BLOCK_SEC = exports.EVM_BLOCKHASH_DEPTH = exports.NULL_CODE_HASH = exports.ABI_CODER = void 0;
exports.toUnpaddedHex = toUnpaddedHex;
exports.toPaddedHex = toPaddedHex;
exports.withResolvers = withResolvers;
exports.isBlockTag = isBlockTag;
exports.fetchBlock = fetchBlock;
exports.fetchBlockFromHash = fetchBlockFromHash;
exports.fetchBlockNumber = fetchBlockNumber;
exports.fetchBlockTag = fetchBlockTag;
exports.fetchStorage = fetchStorage;
exports.isEthersError = isEthersError;
exports.isRevert = isRevert;
exports.isRPCError = isRPCError;
exports.flattenErrors = flattenErrors;
const abi_1 = require("ethers/abi");
const hash_1 = require("ethers/hash");
exports.ABI_CODER = abi_1.AbiCoder.defaultAbiCoder();
// https://adraffy.github.io/keccak.js/test/demo.html#algo=keccak-256&s=&escape=1&encoding=utf8
// "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
exports.NULL_CODE_HASH = (0, hash_1.id)('');
exports.EVM_BLOCKHASH_DEPTH = 256;
// TODO: make this a function of Chain
exports.MAINNET_BLOCK_SEC = 12;
exports.LATEST_BLOCK_TAG = 'latest';
// hex-prefixed w/o zero-padding
function toUnpaddedHex(x) {
return '0x' + BigInt(x).toString(16);
}
// hex-prefixed left-pad w/truncation
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
function withResolvers() {
let resolve;
let reject;
const promise = new Promise((ful, rej) => {
resolve = ful;
reject = rej;
});
return { promise, resolve, reject };
}
function isBlockTag(x) {
return typeof x === 'string' && !x.startsWith('0x');
}
async function fetchBlock(provider, relBlockTag = exports.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;
}
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)
async function fetchBlockNumber(provider, relBlockTag = exports.LATEST_BLOCK_TAG) {
if (relBlockTag === exports.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)
async function fetchBlockTag(provider, relBlockTag = exports.LATEST_BLOCK_TAG) {
return isBlockTag(relBlockTag)
? relBlockTag
: fetchBlockNumber(provider, relBlockTag);
}
async function fetchStorage(provider, target, slot, relBlockTag = exports.LATEST_BLOCK_TAG) {
const data = await provider.send('eth_getStorageAt', [
target,
toPaddedHex(slot),
relBlockTag,
]);
if (!data) {
throw new Error(`expected storage: ${target}<${toUnpaddedHex(slot)}>@${relBlockTag}`);
}
// i think i've seen "0x" before...
return data.length === 66 ? data : toPaddedHex(data);
}
function isEthersError(err) {
return err instanceof Error && 'code' in err && 'shortMessage' in err;
}
function isRevert(err) {
return isEthersError(err) && err.code === 'CALL_EXCEPTION';
}
function isRPCError(err, ...code) {
return (isEthersError(err) &&
err.error instanceof Object &&
'code' in err.error &&
typeof err.error.code === 'number' &&
code.includes(err.error.code));
}
function flattenErrors(err, stringify = stringifyError) {
const errors = [stringify(err)];
for (let e = err; e instanceof Error && e.cause; e = e.cause) {
errors.push(stringify(e.cause));
}
return errors.join(' <== ');
}
function stringifyError(err) {
if (isEthersError(err) && err.code === 'SERVER_ERROR') {
// this leaks api key via "requestUrl"
// https://github.com/ethers-io/ethers.js/blob/d2c9ca0e0fd15e7884bcaab7d5152d68662e3e43/src.ts/utils/fetch.ts#L953
return err.shortMessage;
}
else {
return String(err);
}
}