UNPKG

@unruggable/gateways

Version:

Trustless Ethereum Multichain CCIP-Read Gateway

136 lines (135 loc) 4.82 kB
"use strict"; 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); } }