abstractionkit
Version:
Account Abstraction 4337 SDK by Candidelabs
1,423 lines • 511 kB
JavaScript
import { t as __exportAll } from "./chunk-CfYAbeIz.mjs";
import { keccak_256 } from "@noble/hashes/sha3";
import { secp256k1 } from "@noble/curves/secp256k1";
//#region src/ethereUtils.ts
/**
* ethereUtils — copying related code from ethers v6 with some modification,
* covering exactly the surface area used by `abstractionkit`.
*
* Sources (ethers v6.13.x):
* src.ts/utils/{data,maths,utf8,rlp-encode}.ts
* src.ts/crypto/{keccak,signing-key,signature}.ts
* src.ts/hash/{id,message,solidity,typed-data}.ts
* src.ts/address/{address,checks}.ts
* src.ts/abi/{abi-coder, coders/*}.ts
* src.ts/transaction/address.ts
*/
function redactArgumentValue(name, value) {
if (value === null || value === void 0) return String(value);
if (typeof value === "string") {
const n = name.toLowerCase();
if (n.includes("key") || n.includes("secret") || n.includes("token") || n.includes("password") || n.includes("mnemonic")) return "[REDACTED]";
if (/^0x[0-9a-f]+$/i.test(value) && value.length > 18) return `${value.slice(0, 10)}…${value.slice(-6)}`;
if (value.length > 64) return `[REDACTED string length=${value.length}]`;
return value;
}
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") return String(value);
if (value instanceof Uint8Array) return `[REDACTED Uint8Array length=${value.length}]`;
return `[REDACTED ${typeof value}]`;
}
function assertArgument(check, message, name, value) {
if (!check) throw new Error(`invalid argument (${name}=${redactArgumentValue(name, value)}, message=${message})`);
}
function assertCheck(check, message) {
if (!check) throw new Error(message);
}
const HexCharacters = "0123456789abcdef";
function _getBytes(value, name, copy) {
if (value instanceof Uint8Array) return copy ? new Uint8Array(value) : value;
if (typeof value === "string" && value.length % 2 === 0 && value.match(/^0x[0-9a-f]*$/i)) {
const result = new Uint8Array((value.length - 2) / 2);
let offset = 2;
for (let i = 0; i < result.length; i++) {
result[i] = parseInt(value.substring(offset, offset + 2), 16);
offset += 2;
}
return result;
}
assertArgument(false, "invalid BytesLike value", name || "value", value);
}
function getBytes(value, name) {
return _getBytes(value, name, false);
}
function getBytesCopy(value, name) {
return _getBytes(value, name, true);
}
function isHexString(value, length) {
if (typeof value !== "string" || !value.match(/^0x[0-9A-Fa-f]*$/)) return false;
if (typeof length === "number" && value.length !== 2 + 2 * length) return false;
if (length === true && value.length % 2 !== 0) return false;
return true;
}
function hexlify(data) {
const bytes = getBytes(data);
let result = "0x";
for (let i = 0; i < bytes.length; i++) {
const v = bytes[i];
result += HexCharacters[(v & 240) >> 4] + HexCharacters[v & 15];
}
return result;
}
function concat(datas) {
return "0x" + datas.map((d) => hexlify(d).substring(2)).join("");
}
function dataLength(data) {
if (isHexString(data, true)) return (data.length - 2) / 2;
return getBytes(data).length;
}
function zeroPad(data, length, left) {
const bytes = getBytes(data);
assertCheck(length >= bytes.length, "padding exceeds data length");
const result = new Uint8Array(length);
result.fill(0);
if (left) result.set(bytes, length - bytes.length);
else result.set(bytes, 0);
return hexlify(result);
}
function zeroPadValue(data, length) {
return zeroPad(data, length, true);
}
function zeroPadBytes(data, length) {
return zeroPad(data, length, false);
}
const BN_0 = 0n;
const BN_1 = 1n;
const maxValue = 9007199254740991;
function getBigInt(value, name) {
switch (typeof value) {
case "bigint": return value;
case "number":
assertArgument(Number.isInteger(value), "underflow", name || "value", value);
assertArgument(value >= -maxValue && value <= maxValue, "overflow", name || "value", value);
return BigInt(value);
case "string": try {
if (value === "") throw new Error("empty string");
if (value[0] === "-" && value[1] !== "-") return -BigInt(value.substring(1));
return BigInt(value);
} catch (e) {
assertArgument(false, `invalid BigNumberish string: ${e.message}`, name || "value", value);
}
}
assertArgument(false, "invalid BigNumberish value", name || "value", value);
}
function getUint(value, name) {
const result = getBigInt(value, name);
assertCheck(result >= BN_0, "unsigned value cannot be negative");
return result;
}
function getNumber(value, name) {
switch (typeof value) {
case "bigint":
assertArgument(value >= -maxValue && value <= maxValue, "overflow", name || "value", value);
return Number(value);
case "number":
assertArgument(Number.isInteger(value), "underflow", name || "value", value);
assertArgument(value >= -maxValue && value <= maxValue, "overflow", name || "value", value);
return value;
case "string": try {
if (value === "") throw new Error("empty string");
return getNumber(BigInt(value), name);
} catch (e) {
assertArgument(false, `invalid numeric string: ${e.message}`, name || "value", value);
}
}
assertArgument(false, "invalid numeric value", name || "value", value);
}
const Nibbles = "0123456789abcdef";
function toBigInt(value) {
if (value instanceof Uint8Array) {
let result = "0x0";
for (const v of value) {
result += Nibbles[v >> 4];
result += Nibbles[v & 15];
}
return BigInt(result);
}
return getBigInt(value);
}
function toNumber(value) {
return getNumber(toBigInt(value));
}
function mask(_value, _bits) {
return getUint(_value, "value") & (BN_1 << BigInt(getNumber(_bits, "bits"))) - BN_1;
}
function toTwos(_value, _width) {
let value = getBigInt(_value, "value");
const width = BigInt(getNumber(_width, "width"));
const limit = BN_1 << width - BN_1;
if (value < BN_0) {
value = -value;
assertCheck(value <= limit, "toTwos: too low");
const m = (BN_1 << width) - BN_1;
return (~value & m) + BN_1;
}
assertCheck(value < limit, "toTwos: too high");
return value;
}
function fromTwos(_value, _width) {
const value = getUint(_value, "value");
const width = BigInt(getNumber(_width, "width"));
assertCheck(value >> width === BN_0, "fromTwos: overflow");
if (value >> width - BN_1) {
const m = (BN_1 << width) - BN_1;
return -((~value & m) + BN_1);
}
return value;
}
function toBeHex(_value, _width) {
const value = getUint(_value, "value");
let result = value.toString(16);
if (_width == null) {
if (result.length % 2) result = "0" + result;
} else {
const width = getNumber(_width, "width");
if (width === 0 && value === BN_0) return "0x";
assertCheck(width * 2 >= result.length, `value exceeds width (${width} bytes)`);
while (result.length < width * 2) result = "0" + result;
}
return "0x" + result;
}
function toBeArray(_value, _width) {
const value = getUint(_value, "value");
if (value === BN_0) {
const width = _width != null ? getNumber(_width, "width") : 0;
return new Uint8Array(width);
}
let hex = value.toString(16);
if (hex.length % 2) hex = "0" + hex;
if (_width != null) {
const width = getNumber(_width, "width");
while (hex.length < width * 2) hex = "00" + hex;
assertCheck(width * 2 === hex.length, `value exceeds width (${width} bytes)`);
}
const result = new Uint8Array(hex.length / 2);
for (let i = 0; i < result.length; i++) {
const offset = i * 2;
result[i] = parseInt(hex.substring(offset, offset + 2), 16);
}
return result;
}
function toUtf8Bytes(str) {
assertArgument(typeof str === "string", "invalid string value", "str", str);
const result = [];
for (let i = 0; i < str.length; i++) {
const c = str.charCodeAt(i);
if (c < 128) result.push(c);
else if (c < 2048) {
result.push(c >> 6 | 192);
result.push(c & 63 | 128);
} else if ((c & 64512) === 55296) {
i++;
const c2 = str.charCodeAt(i);
assertArgument(i < str.length && (c2 & 64512) === 56320, "invalid surrogate pair", "str", str);
const pair = 65536 + ((c & 1023) << 10) + (c2 & 1023);
result.push(pair >> 18 | 240);
result.push(pair >> 12 & 63 | 128);
result.push(pair >> 6 & 63 | 128);
result.push(pair & 63 | 128);
} else {
result.push(c >> 12 | 224);
result.push(c >> 6 & 63 | 128);
result.push(c & 63 | 128);
}
}
return new Uint8Array(result);
}
/**
* Decode UTF-8 bytes to a string. Pure JS so it works in every runtime,
* including React Native / Hermes where `TextDecoder` is not defined by default.
*
* Throws on bad prefix, truncated sequences, missing or unexpected continuation bytes,
* overlong encodings, surrogate code points (U+D800..U+DFFF), and code
* points above U+10FFFF. For ABI string payloads this is the right policy —
* well-formed contracts never emit broken UTF-8, so an error signals a
* real problem rather than silently corrupting the decoded value.
*
* @throws Error when `bytes` is not a valid UTF-8 sequence.
*/
function fromUtf8Bytes(bytes) {
const codepoints = [];
let i = 0;
while (i < bytes.length) {
const c = bytes[i++];
if ((c & 128) === 0) {
codepoints.push(c);
continue;
}
let extraLength;
let overlongMask;
if ((c & 224) === 192) {
extraLength = 1;
overlongMask = 127;
} else if ((c & 240) === 224) {
extraLength = 2;
overlongMask = 2047;
} else if ((c & 248) === 240) {
extraLength = 3;
overlongMask = 65535;
} else {
const kind = (c & 192) === 128 ? "unexpected continuation" : "bad prefix";
throw new Error(`invalid UTF-8: ${kind} byte 0x${c.toString(16)} at index ${i - 1}`);
}
if (i + extraLength > bytes.length) throw new Error(`invalid UTF-8: truncated sequence at index ${i - 1}`);
let res = c & (1 << 8 - extraLength - 1) - 1;
for (let j = 0; j < extraLength; j++) {
const next = bytes[i++];
if ((next & 192) !== 128) throw new Error(`invalid UTF-8: missing continuation byte 0x${next.toString(16)} at index ${i - 1}`);
res = res << 6 | next & 63;
}
if (res <= overlongMask) throw new Error(`invalid UTF-8: overlong encoding of U+${res.toString(16).toUpperCase()}`);
if (res >= 55296 && res <= 57343) throw new Error(`invalid UTF-8: surrogate code point U+${res.toString(16).toUpperCase()}`);
if (res > 1114111) throw new Error(`invalid UTF-8: code point U+${res.toString(16).toUpperCase()} out of range`);
codepoints.push(res);
}
let result = "";
for (const cp of codepoints) result += String.fromCodePoint(cp);
return result;
}
function keccak256(_data) {
return hexlify(keccak_256(getBytes(_data, "data")));
}
function id(value) {
return keccak256(toUtf8Bytes(value));
}
const MessagePrefix = "Ethereum Signed Message:\n";
function hashMessage(message) {
if (typeof message === "string") message = toUtf8Bytes(message);
return keccak256(concat([
toUtf8Bytes(MessagePrefix),
toUtf8Bytes(String(message.length)),
message
]));
}
function getChecksumAddress(address) {
address = address.toLowerCase();
const chars = address.substring(2).split("");
const expanded = new Uint8Array(40);
for (let i = 0; i < 40; i++) expanded[i] = chars[i].charCodeAt(0);
const hashed = getBytes(keccak256(expanded));
for (let i = 0; i < 40; i += 2) {
if (hashed[i >> 1] >> 4 >= 8) chars[i] = chars[i].toUpperCase();
if ((hashed[i >> 1] & 15) >= 8) chars[i + 1] = chars[i + 1].toUpperCase();
}
return "0x" + chars.join("");
}
function getAddress(address) {
assertArgument(typeof address === "string", "invalid address", "address", address);
if (address.match(/^(0x)?[0-9a-fA-F]{40}$/)) {
if (!address.startsWith("0x")) address = "0x" + address;
const result = getChecksumAddress(address);
assertArgument(!address.match(/([A-F].*[a-f])|([a-f].*[A-F])/) || result === address, "bad address checksum", "address", address);
return result;
}
assertArgument(false, "invalid address", "address", address);
}
function isAddress(value) {
try {
getAddress(value);
return true;
} catch {
return false;
}
}
const regexBytes = /^bytes([0-9]+)$/;
const regexNumber = /^(u?int)([0-9]*)$/;
const regexArray = /^(.*)\[([0-9]*)\]$/;
function _pack(type, value, isArray) {
switch (type) {
case "address":
if (isArray) return getBytes(zeroPadValue(value, 32));
return getBytes(getAddress(value));
case "string": return toUtf8Bytes(value);
case "bytes": return getBytes(value);
case "bool": {
const v = value ? "0x01" : "0x00";
if (isArray) return getBytes(zeroPadValue(v, 32));
return getBytes(v);
}
}
let match = type.match(regexNumber);
if (match) {
const signed = match[1] === "int";
let size = parseInt(match[2] || "256");
assertArgument((!match[2] || match[2] === String(size)) && size % 8 === 0 && size !== 0 && size <= 256, "invalid number type", "type", type);
if (isArray) size = 256;
return getBytes(zeroPadValue(toBeArray(signed ? toTwos(value, size) : value), size / 8));
}
match = type.match(regexBytes);
if (match) {
const size = parseInt(match[1]);
assertArgument(String(size) === match[1] && size !== 0 && size <= 32, "invalid bytes type", "type", type);
assertArgument(dataLength(value) === size, `invalid value for ${type}`, "value", value);
if (isArray) return getBytes(zeroPadBytes(value, 32));
return getBytes(value);
}
match = type.match(regexArray);
if (match && Array.isArray(value)) {
const baseType = match[1];
assertArgument(parseInt(match[2] || String(value.length)) === value.length, `invalid array length for ${type}`, "value", value);
const result = [];
for (const v of value) result.push(_pack(baseType, v, true));
return getBytes(concat(result));
}
assertArgument(false, "invalid type", "type", type);
}
function solidityPacked(types, values) {
assertArgument(types.length === values.length, "wrong number of values", "values", values);
const tight = [];
for (let i = 0; i < types.length; i++) tight.push(_pack(types[i], values[i]));
return hexlify(concat(tight));
}
function solidityPackedKeccak256(types, values) {
return keccak256(solidityPacked(types, values));
}
function arrayifyInteger(value) {
const result = [];
while (value) {
result.unshift(value & 255);
value >>= 8;
}
return result;
}
function _rlpEncode(object) {
if (Array.isArray(object)) {
let payload = [];
object.forEach((child) => {
payload = payload.concat(_rlpEncode(child));
});
if (payload.length <= 55) {
payload.unshift(192 + payload.length);
return payload;
}
const length = arrayifyInteger(payload.length);
length.unshift(247 + length.length);
return length.concat(payload);
}
const data = Array.prototype.slice.call(getBytes(object, "object"));
if (data.length === 1 && data[0] <= 127) return data;
if (data.length <= 55) {
data.unshift(128 + data.length);
return data;
}
const length = arrayifyInteger(data.length);
length.unshift(183 + length.length);
return length.concat(data);
}
function encodeRlp(object) {
let result = "0x";
for (const v of _rlpEncode(object)) {
result += Nibbles[v >> 4];
result += Nibbles[v & 15];
}
return result;
}
function splitTopLevel(s) {
const out = [];
let depth = 0, start = 0;
for (let i = 0; i < s.length; i++) {
const c = s[i];
if (c === "(" || c === "[") depth++;
else if (c === ")" || c === "]") depth--;
else if (c === "," && depth === 0) {
out.push(s.slice(start, i));
start = i + 1;
}
}
if (start < s.length) out.push(s.slice(start));
return out.map((x) => x.trim()).filter((x) => x.length > 0);
}
function parseType(s) {
s = s.trim();
const m = s.match(/^(.+?)((?:\[\d*\])*)$/);
assertArgument(!!m, `invalid type`, "type", s);
const base = m[1], suffix = m[2];
let t;
if (base.startsWith("(")) {
assertArgument(base.endsWith(")"), `unbalanced tuple`, "type", s);
t = {
kind: "tuple",
components: splitTopLevel(base.slice(1, -1)).map(parseType)
};
} else if (base === "address") t = { kind: "address" };
else if (base === "bool") t = { kind: "bool" };
else if (base === "string") t = { kind: "string" };
else if (base === "bytes") t = { kind: "bytes" };
else if (base === "uint" || base === "int") t = {
kind: "uint",
bits: 256,
signed: base === "int"
};
else {
const um = base.match(regexNumber);
if (um) {
const bits = parseInt(um[2] || "256");
assertArgument(bits !== 0 && bits <= 256 && bits % 8 === 0, "invalid number type", "type", base);
t = {
kind: "uint",
bits,
signed: um[1] === "int"
};
} else {
const bm = base.match(regexBytes);
assertArgument(!!bm, `unknown type ${base}`, "type", base);
const size = parseInt(bm[1]);
assertArgument(size !== 0 && size <= 32, "invalid bytes type", "type", base);
t = {
kind: "bytesN",
size
};
}
}
for (const ap of suffix.matchAll(/\[(\d*)\]/g)) t = {
kind: "array",
child: t,
size: ap[1] ? parseInt(ap[1]) : -1
};
return t;
}
function isDynamic(t) {
switch (t.kind) {
case "bytes":
case "string": return true;
case "array": return t.size === -1 || isDynamic(t.child);
case "tuple": return t.components.some(isDynamic);
default: return false;
}
}
function staticSize(t) {
switch (t.kind) {
case "uint":
case "address":
case "bool":
case "bytesN": return 32;
case "array": return t.size * staticSize(t.child);
case "tuple": return t.components.reduce((s, c) => s + staticSize(c), 0);
default: throw new Error(`staticSize: dynamic type ${t.kind}`);
}
}
const WordSize = 32;
const Padding = new Uint8Array(WordSize);
const MaxZeroFootprintArrayLen = 1 << 20;
function padLeft(b, size) {
assertCheck(b.length <= size, "padLeft: overflow");
const out = new Uint8Array(size);
out.set(b, size - b.length);
return out;
}
function padRight(b, size) {
assertCheck(b.length <= size, "padRight: overflow");
const out = new Uint8Array(size);
out.set(b, 0);
return out;
}
function padTo32(len) {
return Math.ceil(len / WordSize) * WordSize;
}
const BN_MAX_UINT256 = (1n << 256n) - 1n;
function encodeValue(t, v) {
switch (t.kind) {
case "uint": {
let value = getBigInt(v, "value");
const maxUintValue = mask(BN_MAX_UINT256, WordSize * 8);
if (t.signed) {
const bounds = mask(maxUintValue, t.bits - 1);
assertCheck(value <= bounds && value >= -(bounds + 1n), `int${t.bits}: value out-of-bounds`);
value = toTwos(value, 8 * WordSize);
} else assertCheck(value >= 0n && value <= mask(maxUintValue, t.bits), `uint${t.bits}: out-of-bounds`);
return padLeft(toBeArray(value), WordSize);
}
case "address": return padLeft(getBytes(getAddress(v)), WordSize);
case "bool": return padLeft(new Uint8Array([v ? 1 : 0]), WordSize);
case "bytesN": {
const bytes = getBytes(v);
assertCheck(bytes.length === t.size, `bytes${t.size}: wrong length`);
return padRight(bytes, WordSize);
}
case "bytes": {
const bytes = getBytes(v);
return concatBytes([padLeft(toBeArray(bytes.length), WordSize), padRight(bytes, padTo32(bytes.length))]);
}
case "string": {
const bytes = toUtf8Bytes(v);
return concatBytes([padLeft(toBeArray(bytes.length), WordSize), padRight(bytes, padTo32(bytes.length))]);
}
case "array": {
const arr = v;
assertCheck(t.size === -1 || arr.length === t.size, "array: length mismatch");
const inner = encodeTuple(Array(arr.length).fill(t.child), arr);
return t.size === -1 ? concatBytes([padLeft(toBeArray(arr.length), WordSize), inner]) : inner;
}
case "tuple": return encodeTuple(t.components, v);
}
}
function concatBytes(parts) {
const total = parts.reduce((s, p) => s + p.length, 0);
const out = new Uint8Array(total);
let off = 0;
for (const p of parts) {
out.set(p, off);
off += p.length;
}
return out;
}
function encodeTuple(types, values) {
assertCheck(types.length === values.length, "tuple: length mismatch");
const heads = [];
const tails = [];
let tailOff = types.reduce((s, t) => s + (isDynamic(t) ? WordSize : staticSize(t)), 0);
for (let i = 0; i < types.length; i++) {
const enc = encodeValue(types[i], values[i]);
if (isDynamic(types[i])) {
heads.push(padLeft(toBeArray(tailOff), WordSize));
tails.push(enc);
tailOff += enc.length;
} else heads.push(enc);
}
return concatBytes([...heads, ...tails]);
}
function ensureRange(data, offset, len, what) {
if (offset < 0 || len < 0 || offset + len > data.length) throw new Error(`ABI decode: out-of-bounds read for ${what} (offset=${offset}, length=${len}, data.length=${data.length})`);
}
function decodeValue(t, data, base) {
switch (t.kind) {
case "uint": {
ensureRange(data, base, WordSize, `${t.signed ? "int" : "uint"}${t.bits}`);
const masked = mask(toBigInt(data.slice(base, base + WordSize)), t.bits);
return t.signed ? fromTwos(masked, t.bits) : masked;
}
case "address":
ensureRange(data, base, WordSize, "address");
return getAddress(hexlify(data.slice(base + 12, base + WordSize)));
case "bool":
ensureRange(data, base, WordSize, "bool");
return data[base + 31] !== 0;
case "bytesN":
ensureRange(data, base, WordSize, `bytes${t.size}`);
return hexlify(data.slice(base, base + t.size));
case "bytes": {
ensureRange(data, base, WordSize, "bytes length");
const len = toNumber(data.slice(base, base + WordSize));
ensureRange(data, base + WordSize, len, "bytes payload");
return hexlify(data.slice(base + WordSize, base + WordSize + len));
}
case "string": {
ensureRange(data, base, WordSize, "string length");
const len = toNumber(data.slice(base, base + WordSize));
ensureRange(data, base + WordSize, len, "string payload");
return fromUtf8Bytes(data.slice(base + WordSize, base + WordSize + len));
}
case "array":
if (t.size === -1) {
ensureRange(data, base, WordSize, "array length");
const len = toNumber(data.slice(base, base + WordSize));
const elementHeadSize = isDynamic(t.child) ? WordSize : staticSize(t.child);
const bodyBytes = data.length - (base + WordSize);
const maxElements = elementHeadSize > 0 ? Math.floor(bodyBytes / elementHeadSize) : MaxZeroFootprintArrayLen;
if (len > maxElements) throw new Error(`ABI decode: array length ${len} exceeds payload capacity (maxElements=${maxElements}, data.length=${data.length})`);
return decodeTupleAt(Array(len).fill(t.child), data, base + WordSize);
}
return decodeTupleAt(Array(t.size).fill(t.child), data, base);
case "tuple": return decodeTupleAt(t.components, data, base);
}
}
function decodeTupleAt(types, data, base) {
const out = [];
let head = 0;
for (const t of types) if (isDynamic(t)) {
ensureRange(data, base + head, WordSize, "tuple offset slot");
const off = toNumber(data.slice(base + head, base + head + WordSize));
if (off < 0 || base + off > data.length) throw new Error(`ABI decode: tuple dynamic offset out-of-bounds (offset=${off}, base=${base}, data.length=${data.length})`);
out.push(decodeValue(t, data, base + off));
head += WordSize;
} else {
const size = staticSize(t);
ensureRange(data, base + head, size, `tuple static slot (${t.kind})`);
out.push(decodeValue(t, data, base + head));
head += size;
}
return out;
}
function encodeAbiParameters(types, values) {
assertCheck(types.length === values.length, "encodeAbiParameters: length mismatch");
return hexlify(encodeTuple(types.map(parseType), values));
}
function decodeAbiParameters(types, data) {
return decodeTupleAt(types.map(parseType), getBytes(data), 0);
}
const hexTrue = toBeHex(BN_1, 32);
const hexFalse = toBeHex(BN_0, 32);
function hexPadRight(value) {
const bytes = getBytes(value);
const padOffset = bytes.length % WordSize;
if (padOffset) return concat([bytes, Padding.slice(padOffset)]);
return hexlify(bytes);
}
function getBaseEncoder(type) {
{
const match = type.match(/^(u?)int(\d+)$/);
if (match) {
const signed = match[1] === "";
const width = parseInt(match[2]);
assertArgument(width % 8 === 0 && width !== 0 && width <= 256 && match[2] === String(width), "invalid numeric width", "type", type);
const boundsUpper = mask(BN_MAX_UINT256, signed ? width - 1 : width);
const boundsLower = signed ? (boundsUpper + BN_1) * -1n : BN_0;
return (v) => {
const value = getBigInt(v, "value");
assertCheck(value >= boundsLower && value <= boundsUpper, `value out-of-bounds for ${type}`);
return toBeHex(signed ? toTwos(value, 256) : value, 32);
};
}
}
{
const match = type.match(/^bytes(\d+)$/);
if (match) {
const width = parseInt(match[1]);
assertArgument(width !== 0 && width <= 32 && match[1] === String(width), "invalid bytes width", "type", type);
return (v) => {
assertCheck(getBytes(v).length === width, `invalid length for ${type}`);
return hexPadRight(v);
};
}
}
switch (type) {
case "address": return (v) => zeroPadValue(getAddress(v), 32);
case "bool": return (v) => !v ? hexFalse : hexTrue;
case "bytes": return (v) => keccak256(v);
case "string": return (v) => id(v);
}
return null;
}
function splitArray(type) {
const match = type.match(/^([^\x5b]*)((\x5b\d*\x5d)*)(\x5b(\d*)\x5d)$/);
if (match) return {
base: match[1],
index: match[2] + match[4],
array: {
base: match[1],
prefix: match[1] + match[2],
count: match[5] ? parseInt(match[5]) : -1
}
};
return { base: type };
}
function encodeType(name, fields) {
return `${name}(${fields.map(({ name, type }) => type + " " + name).join(",")})`;
}
/**
* Build a recursive encoder for `primaryType` that produces the EIP-712
* `encodeData` for a struct value. Internal `fullTypes` map captures the
* fully-described type string for each struct (`MyStruct(...)NestedStruct(...)`).
*/
function buildTypedDataState(_types) {
const fullTypes = /* @__PURE__ */ new Map();
const links = /* @__PURE__ */ new Map();
const parents = /* @__PURE__ */ new Map();
const subtypes = /* @__PURE__ */ new Map();
const types = {};
for (const type of Object.keys(_types)) {
types[type] = _types[type].map(({ name, type }) => {
let { base, index } = splitArray(type);
if (base === "int" && !_types["int"]) base = "int256";
if (base === "uint" && !_types["uint"]) base = "uint256";
return {
name,
type: base + (index || "")
};
});
links.set(type, /* @__PURE__ */ new Set());
parents.set(type, []);
subtypes.set(type, /* @__PURE__ */ new Set());
}
for (const name in types) {
const uniqueNames = /* @__PURE__ */ new Set();
for (const field of types[name]) {
assertArgument(!uniqueNames.has(field.name), `duplicate variable name ${JSON.stringify(field.name)} in ${JSON.stringify(name)}`, "types", _types);
uniqueNames.add(field.name);
const baseType = splitArray(field.type).base;
assertArgument(baseType !== name, `circular type reference to ${JSON.stringify(baseType)}`, "types", _types);
if (getBaseEncoder(baseType)) continue;
assertArgument(parents.has(baseType), `unknown type ${JSON.stringify(baseType)}`, "types", _types);
parents.get(baseType).push(name);
links.get(name).add(baseType);
}
}
const primaryTypes = Array.from(parents.keys()).filter((n) => parents.get(n).length === 0);
assertArgument(primaryTypes.length !== 0, "missing primary type", "types", _types);
assertArgument(primaryTypes.length === 1, `ambiguous primary types or unused types: ${primaryTypes.map((t) => JSON.stringify(t)).join(", ")}`, "types", _types);
const primaryType = primaryTypes[0];
function checkCircular(type, found) {
assertArgument(!found.has(type), `circular type reference to ${JSON.stringify(type)}`, "types", _types);
found.add(type);
for (const child of links.get(type)) {
if (!parents.has(child)) continue;
checkCircular(child, found);
for (const subtype of found) subtypes.get(subtype).add(child);
}
found.delete(type);
}
checkCircular(primaryType, /* @__PURE__ */ new Set());
for (const [name, set] of subtypes) {
const st = Array.from(set);
st.sort();
fullTypes.set(name, encodeType(name, types[name]) + st.map((t) => encodeType(t, types[t])).join(""));
}
const encoderCache = /* @__PURE__ */ new Map();
function getEncoder(type) {
const cached = encoderCache.get(type);
if (cached) return cached;
const built = buildEncoder(type);
encoderCache.set(type, built);
return built;
}
function buildEncoder(type) {
const base = getBaseEncoder(type);
if (base) return base;
const arr = splitArray(type).array;
if (arr) {
const subtype = arr.prefix;
const subEncoder = getEncoder(subtype);
return (value) => {
const arrVal = value;
assertCheck(arr.count === -1 || arr.count === arrVal.length, `array length mismatch; expected ${arr.count}`);
let result = arrVal.map(subEncoder);
if (fullTypes.has(subtype)) result = result.map(keccak256);
return keccak256(concat(result));
};
}
const fields = types[type];
if (fields) {
const encodedType = id(fullTypes.get(type));
return (value) => {
const obj = value;
const values = fields.map(({ name, type }) => {
const r = getEncoder(type)(obj[name]);
return fullTypes.has(type) ? keccak256(r) : r;
});
values.unshift(encodedType);
return concat(values);
};
}
assertArgument(false, `unknown type: ${type}`, "type", type);
}
function hashStruct(name, value) {
return keccak256(getEncoder(name)(value));
}
return {
primaryType,
hashStruct
};
}
const domainFieldTypes = {
name: "string",
version: "string",
chainId: "uint256",
verifyingContract: "address",
salt: "bytes32"
};
const domainFieldNames = [
"name",
"version",
"chainId",
"verifyingContract",
"salt"
];
function hashDomain(domain) {
const domainFields = [];
for (const name in domain) {
if (domain[name] == null) continue;
const type = domainFieldTypes[name];
assertArgument(!!type, `invalid typed-data domain key: ${JSON.stringify(name)}`, "domain", domain);
domainFields.push({
name,
type
});
}
domainFields.sort((a, b) => domainFieldNames.indexOf(a.name) - domainFieldNames.indexOf(b.name));
const { hashStruct } = buildTypedDataState({ EIP712Domain: domainFields });
return hashStruct("EIP712Domain", domain);
}
function hashTypedData(domain, types, value) {
const userTypes = {};
for (const [k, v] of Object.entries(types)) if (k !== "EIP712Domain") userTypes[k] = v;
const { primaryType, hashStruct } = buildTypedDataState(userTypes);
return keccak256(concat([
"0x1901",
hashDomain(domain),
hashStruct(primaryType, value)
]));
}
function computePublicKey(privateKey) {
const bytes = getBytes(privateKey, "key");
assertCheck(bytes.length === 32, "invalid private key");
return hexlify(secp256k1.getPublicKey(bytes, false));
}
function normalizePrivateKey(key) {
return key.startsWith("0x") ? key : "0x" + key;
}
function privateKeyToAddress(privateKey) {
return getAddress(keccak256("0x" + computePublicKey(normalizePrivateKey(privateKey)).substring(4)).substring(26));
}
function signHash$1(privateKey, hash) {
assertCheck(dataLength(hash) === 32, "invalid digest length");
const sig = secp256k1.sign(getBytesCopy(hash), getBytesCopy(normalizePrivateKey(privateKey)), { lowS: true });
const r = toBeHex(sig.r, 32);
const s = toBeHex(sig.s, 32);
const yParity = sig.recovery & 1;
const v = 27 + yParity;
return {
r,
s,
v,
yParity,
serialized: concat([
r,
s,
new Uint8Array([v])
])
};
}
function signTypedData(privateKey, domain, types, message) {
return signHash$1(privateKey, hashTypedData(domain, types, message)).serialized;
}
//#endregion
//#region src/errors.ts
/**
* Maps JSON-RPC numeric error codes to human-readable {@link BundlerErrorCode} values.
*/
const BundlerErrorCodeDict = {
"-32602": "INVALID_FIELDS",
"-32500": "SIMULATE_VALIDATION",
"-32501": "SIMULATE_PAYMASTER_VALIDATION",
"-32502": "OPCODE_VALIDATION",
"-32503": "EXPIRE_SHORTLY",
"-32504": "REPUTATION",
"-32505": "INSUFFICIENT_STAKE",
"-32506": "UNSUPPORTED_SIGNATURE_AGGREGATOR",
"-32507": "INVALID_SIGNATURE",
"-32508": "PAYMASTER_DEPOSIT_TOO_LOW",
"-32521": "EXECUTION_REVERTED"
};
/**
* Maps JSON-RPC numeric error codes to human-readable {@link JsonRpcErrorCode} values.
*/
const JsonRpcErrorDict = {
"-32700": "PARSE_ERROR",
"-32600": "INVALID_REQUEST",
"-32601": "METHOD_NOT_FOUND",
"-32602": "INVALID_PARAMS",
"-32603": "INTERNAL_ERROR"
};
/**
* Custom error class for the AbstractionKit SDK. Wraps bundler, JSON-RPC,
* and general errors with a structured code, optional numeric errno, and
* arbitrary JSON-serializable context.
*/
var AbstractionKitError = class extends Error {
code;
context;
errno;
/**
* The EntryPoint FailedOp revert code parsed from the message (e.g. "AA21"),
* when present. EntryPoint codes are contract-defined and stable across
* bundlers, so they are a reliable signal for classifying a failure without
* matching the human-readable message text.
*/
aaCode;
/**
* @param code - Error code identifying the category of failure
* @param message - Human-readable error description
* @param options - Optional cause, numeric errno, JSON-serializable context, and AAxx code
*/
constructor(code, message, options = {}) {
const { cause, errno, context, aaCode } = options;
super(message, { cause });
this.name = this.constructor.name;
this.code = code;
this.errno = errno;
this.context = context;
this.aaCode = aaCode;
}
/**
* Returns a JSON string representation of this error including name, code,
* message, cause, errno, context, and aaCode. Useful in React Native where
* the Error "cause" property is not shown in stack traces.
* @returns JSON string of the error
*/
stringify() {
return JSON.stringify(this, [
"name",
"code",
"message",
"cause",
"errno",
"context",
"aaCode"
]);
}
};
/**
* Parse the EntryPoint FailedOp revert code (e.g. "AA21") from a bundler error
* message, if one is present. Returns the uppercased code or undefined.
*/
function parseAaCode(message) {
return message.match(/\bAA\d{2}\b/i)?.[0].toUpperCase();
}
/**
* Coerces an unknown thrown value into an Error instance.
* If the value is already an Error it is returned as-is; otherwise it is
* stringified and wrapped in a new Error.
* @param value - The caught value to normalize
* @returns An Error instance
*/
function ensureError(value) {
if (value instanceof Error) return value;
let stringified = "[Unable to stringify the thrown value]";
try {
stringified = JSON.stringify(value);
} catch {}
return /* @__PURE__ */ new Error(`This value was thrown as is, not through an Error: ${stringified}`);
}
//#endregion
//#region src/transport/Transport.ts
/**
* Default {@link ProviderRpcError} implementation. {@link BaseRpcTransport}
* throws this automatically when a JSON-RPC envelope returns
* `{ error: { code, message, data } }`.
*/
var TransportRpcError = class extends Error {
code;
data;
/**
* @param code - Numeric JSON-RPC error code (e.g. -32601 for METHOD_NOT_FOUND)
* @param message - Human-readable error description
* @param data - Optional additional data returned by the RPC server
*/
constructor(code, message, data) {
super(message);
this.name = "TransportRpcError";
this.code = code;
this.data = data;
}
};
/**
* Narrowing helper for {@link EventfulTransport}.
*
* @param transport - Any transport to test
* @returns `true` when both `on` and `removeListener` methods are present
*/
function isEventfulTransport(transport) {
const t = transport;
return typeof t.on === "function" && typeof t.removeListener === "function";
}
//#endregion
//#region src/transport/BaseRpcTransport.ts
/**
* Optional convenience base class for users writing new wire-level
* {@link Transport} backends (WebSocket, IPC, custom HTTP, in-process mock,
* etc.). Handles JSON-RPC framing, id assignment, bigint serialization, and
* standard error parsing so subclasses implement only the byte-level
* `send(envelope, options)` hook.
*
* Users who already have a {@link Transport} in hand (window.ethereum, viem
* `WalletClient`, ethers `Eip1193Provider`-shaped object, etc.) do NOT need
* this class — they pass their object directly.
*
* @example
* ```ts
* class WebSocketTransport extends BaseRpcTransport {
* protected async send(envelope) {
* // serialize envelope to JSON, send over the socket, await response
* return JSON.parse(await this.socket.sendAndAwait(JSON.stringify(envelope)));
* }
* }
* ```
*/
var BaseRpcTransport = class BaseRpcTransport {
nextId = 1;
/**
* Build a JSON-RPC envelope, delegate the wire I/O to the subclass's
* {@link BaseRpcTransport.send} method, and parse the response.
*
* @throws {@link TransportRpcError} when the response contains an `error` field
* @throws {@link TransportRpcError} (code -32603) when the response is malformed
*/
async request(args, options) {
const envelope = {
jsonrpc: "2.0",
id: this.nextId++,
method: args.method,
params: args.params
};
const raw = await this.send(envelope, options);
return BaseRpcTransport.parseResponse(raw);
}
/**
* Serialize a JSON-RPC envelope to a string, converting bigint values to
* `0x`-prefixed hex strings (preserving the historical SDK behavior).
*/
static serializeEnvelope(envelope) {
return JSON.stringify(envelope, (_key, value) => typeof value === "bigint" ? `0x${value.toString(16)}` : value);
}
/**
* Parse a decoded JSON-RPC response. Returns the `result` field on success
* or throws a {@link TransportRpcError} on error / malformed response.
*/
static parseResponse(raw) {
if (raw == null || typeof raw !== "object") throw new TransportRpcError(-32603, "malformed JSON-RPC response", raw);
const response = raw;
if (response.error != null) {
if (typeof response.error === "object") {
const { code, message, data } = response.error;
throw new TransportRpcError(code, message, data);
}
throw new TransportRpcError(-32603, "malformed JSON-RPC response", raw);
}
if ("result" in response) return response.result;
throw new TransportRpcError(-32603, "malformed JSON-RPC response", raw);
}
};
//#endregion
//#region src/transport/HttpTransport.ts
/**
* Default concrete {@link Transport}: POSTs JSON-RPC envelopes to an HTTP
* endpoint. Used by every URL-string call site once a string is normalized
* into a transport.
*
* @example
* ```ts
* const t = new HttpTransport("https://api.candide.dev/public/v3/11155111");
* const chainId = await t.request<string>({ method: "eth_chainId" });
*
* // With auth headers and a custom fetch:
* const t2 = new HttpTransport("https://...", {
* headers: { Authorization: `Bearer ${token}` },
* fetch: myFetchWithRetry,
* });
* ```
*/
var HttpTransport = class HttpTransport extends BaseRpcTransport {
/** Endpoint URL this transport POSTs to. */
url;
/** Options passed at construction time. */
options;
/**
* @param url - JSON-RPC endpoint URL (bundler, paymaster, or node)
* @param options - Optional fetch override and static headers
*/
constructor(url, options = {}) {
super();
this.url = url;
this.options = options;
}
async send(envelope, options) {
const headers = {
...this.options.headers ?? {},
"Content-Type": "application/json"
};
const body = HttpTransport.serializeEnvelope(envelope);
const response = await (this.options.fetch ?? globalThis.fetch)(this.url, {
method: "POST",
headers,
body,
redirect: "follow",
signal: options?.signal
});
const responseText = await response.text();
let parsed;
try {
parsed = JSON.parse(responseText);
} catch {
throw new TransportRpcError(-32603, `HTTP ${response.status} ${response.statusText}: response body is not JSON`.trim(), responseText.slice(0, 1e3));
}
const rpcError = typeof parsed === "object" && parsed != null && "error" in parsed ? parsed.error : null;
const hasRpcError = typeof rpcError === "object" && rpcError != null && typeof rpcError.code === "number" && typeof rpcError.message === "string";
if (!response.ok && !hasRpcError) throw new TransportRpcError(-32603, `HTTP ${response.status} ${response.statusText}`.trim(), parsed);
return parsed;
}
};
/**
* Narrowing helper for {@link HttpTransport}. Useful for code that wants to
* read the underlying URL — e.g. when serializing a configured Bundler for
* logging or diagnostics.
*
* @example
* ```ts
* if (isHttpTransport(bundler.transport)) {
* console.log("Bundler URL:", bundler.transport.url);
* }
* ```
*/
function isHttpTransport(transport) {
return transport instanceof HttpTransport;
}
//#endregion
//#region src/types.ts
/**
* Specifies whether a transaction is a regular call or a delegatecall.
*/
let Operation = /* @__PURE__ */ function(Operation) {
/** Standard call to the target address */
Operation[Operation["Call"] = 0] = "Call";
/** Delegatecall (executes target code in caller's context) */
Operation[Operation["Delegate"] = 1] = "Delegate";
return Operation;
}({});
/**
* Multiplier to determine the gas price. Higher values result in faster inclusion but higher cost.
*/
let GasOption = /* @__PURE__ */ function(GasOption) {
/** 1x multiplier -- lowest cost, slowest inclusion */
GasOption[GasOption["Slow"] = 1] = "Slow";
/** 1.2x multiplier -- balanced cost and speed */
GasOption[GasOption["Medium"] = 1.2] = "Medium";
/** 1.5x multiplier -- highest cost, fastest inclusion */
GasOption[GasOption["Fast"] = 1.5] = "Fast";
return GasOption;
}({});
/** Polygon network identifier used to select the correct Polygon Gas Station endpoint. */
let PolygonChain = /* @__PURE__ */ function(PolygonChain) {
PolygonChain["Mainnet"] = "v2";
PolygonChain["ZkMainnet"] = "zkevm";
PolygonChain["Amoy"] = "amoy";
PolygonChain["Cardona"] = "cardona";
return PolygonChain;
}({});
//#endregion
//#region src/transport/normalize.ts
/**
* Recursively convert any `bigint` values inside an RPC param to `0x`-prefixed
* hex strings, descending into arrays and plain objects. Other values pass
* through unchanged.
*
* Required for user-supplied {@link Transport} implementations that don't go
* through {@link BaseRpcTransport.serializeEnvelope} (EIP-1193 providers, viem
* clients, etc.) and would otherwise see raw `bigint`s in `params`. Mirrors the
* normalization done in {@link sendJsonRpcRequest}.
*
* @internal
*/
function normalizeRpcValue(value) {
if (typeof value === "bigint") return `0x${value.toString(16)}`;
if (Array.isArray(value)) return value.map(normalizeRpcValue);
if (value !== null && typeof value === "object") {
const out = {};
for (const [k, v] of Object.entries(value)) out[k] = normalizeRpcValue(v);
return out;
}
return value;
}
/**
* Wrap a {@link Transport} so every outbound `request` has its `params`
* normalized (bigints → 0x-hex) before delegation. Idempotent: wrapping an
* already-normalizing transport is harmless because the second pass sees only
* strings.
*
* Applied once at each service boundary (Bundler, CandidePaymaster,
* Erc7677Paymaster, JsonRpcNode, …) so normalization is impossible to forget
* per-method. Required for user-supplied transports (EIP-1193 providers, viem
* clients, etc.) that don't route through {@link BaseRpcTransport.serializeEnvelope}.
*
* @internal
*/
function normalizingTransport(inner) {
return { request(args, options) {
const params = args.params == null ? args.params : normalizeRpcValue(args.params);
return inner.request({
method: args.method,
params
}, options);
} };
}
//#endregion
//#region src/transport/JsonRpcNode.ts
const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
/**
* High-level service class for the JSON-RPC node methods abstractionkit reads
* from. Intentionally NOT a general Ethereum client — only exposes the
* methods the SDK actually uses. For broader functionality, drop down to
* `.transport` and call `request({ method, params })` directly, or use a
* dedicated library like viem / ethers.
*
* Like {@link Bundler} and the paymaster classes, `JsonRpcNode` itself
* implements {@link Transport} so it can be passed back into any Transport
* position.
*
* @example
* ```ts
* const node = new JsonRpcNode("https://ethereum-sepolia.publicnode.com");
* const id = await node.chainId();
* const code = await node.getCode("0x...");
*
* // Also a Transport — can be slotted in:
* const bundler = new Bundler(node); // bundler will speak through this node
* ```
*/
var JsonRpcNode = class JsonRpcNode {
/**
* The raw transport the user passed in (or {@link HttpTransport} when a URL
* string was passed). Exposed for introspection — reading `.url`,
* `isHttpTransport(...)` checks, passing it back into another service.
*
* Calls made directly on this field (`node.transport.request(...)`) go
* to the raw transport and skip SDK-level behavior like bigint param
* normalization. For SDK-pipeline behavior, use {@link JsonRpcNode.request}
* or the typed methods.
*/
transport;
/** Normalizing wrapper around {@link transport}, used for every SDK-outbound call. */
outbound;
/**
* @param rpc - Node JSON-RPC endpoint URL, or any {@link Transport}
*/
constructor(rpc) {
this.transport = typeof rpc === "string" ? new HttpTransport(rpc) : rpc;
this.outbound = normalizingTransport(this.transport);
}
/**
* Normalize any acceptable input into a {@link JsonRpcNode}. Used at every
* public-API widening site (in account / paymaster classes). When the
* input is already a `JsonRpcNode`, the same instance is returned by
* reference, so a user's preconstructed `JsonRpcNode` is never re-wrapped.
*
* @param input - URL string, Transport, or existing JsonRpcNode
*/
static from(input) {
return input instanceof JsonRpcNode ? input : new JsonRpcNode(input);
}
/**
* Transport delegate. Routes through the normalizing wrapper so `bigint`
* values in `params` are converted to `0x`-hex before reaching
* user-supplied transports that don't go through
* {@link BaseRpcTransport.serializeEnvelope}. Lets a `JsonRpcNode` itself
* slot into any other transport position.
*/
request(args, options) {
return this.outbound.request(args, options);
}
/**
* `eth_chainId`. Returns the hex-encoded chain id (e.g. `"0xaa36a7"` for
* Sepolia).
*/
async chainId(options) {
try {
const result = await this.outbound.request({ method: "eth_chainId" }, options);
if (typeof result !== "string") throw new AbstractionKitError("BAD_DATA", "eth_chainId returned ill formed data", { context: JSON.stringify(result) });
return result;
} catch (err) {
throw translateNodeError(err, "eth_chainId");
}
}
/**
* `eth_blockNumber`. Returns the latest block number as a bigint.
*/
async blockNumber(options) {
try {
const result = await this.outbound.request({ method: "eth_blockNumber" }, options);
if (typeof result !== "string") throw new AbstractionKitError("BAD_DATA", "eth_blockNumber returned ill formed data", { context: JSON.stringify(result) });
return BigInt(result);
} catch (err) {
throw translateNodeError(err, "eth_blockNumber");
}
}
/**
* `eth_getCode`. Returns the deployed bytecode at `address` at the given
* block tag (default `"latest"`).
*/
async getCode(address, blockTag = "latest", options) {
try {
const result = await this.outbound.request({
method: "eth_getCode",
params: [address, blockTag]
}, options);
if (typeof result !== "string") throw new AbstractionKitError("BAD_DATA", "eth_getCode returned ill formed data", { context: JSON.stringify(result) });
return result;
} catch (err) {
throw translateNodeError(err, "eth_getCode");
}
}
/**
* `eth_getStorageAt`. Returns the 32-byte storage word at `slot` for
* `address` at the given block tag (default `"latest"`), as a hex string.
*/
async getStorageAt(address, slot, blockTag = "latest", options) {
try {
const result = await this.outbound.request({
method: "eth_getStorageAt",
params: [
address,
slot,
blockTag
]
}, options);
if (typeof result !== "string") throw new AbstractionKitError("BAD_DATA", "eth_getStorageAt returned ill formed data", { context: JSON.stringify(result) });
return result;
} catch (err) {
throw translateNodeError(err, "eth_getStorageAt");
}
}
/**
* `eth_call`. Executes a read-only call against `to` and returns the raw
* return data as a hex string. Supports state overrides via the optional
* third parameter.
*/
async call(tx, blockTag = "latest", stateOverrides, options) {
const params = stateOverrides == null ? [tx, blockTag] : [
tx,
blockTag,
stateOverrides
];
try {
const result = await this.outbound.request({
method: "eth_call",
params
}, options);
if (typeof result !== "string") throw new AbstractionKitError("BAD_DATA", "eth_call returned ill formed data", { context: JSON.stringify(result) });
return result;
} catch (err) {
throw translateNodeError(err, "eth_call");
}
}
/**
* `eth_getTransactionCount`. Returns the transaction count (account nonce
* at the EOA level — not the EntryPoint nonce; see
* {@link JsonRpcNode.getEntryPointNonce}) as a bigint.
*/
async getTransactionCount(address, blockTag = "latest", options) {
try {
const result = await this.outbound.request({
method: "eth_getTransactionCount",
params: [address, blockTag]
}, options);
if (typeof result !== "string") throw new AbstractionKitError("BAD_DATA", "eth_getTransactionCount returned ill formed data", { context: JSON.stringify(result) });
return BigInt(result);
} catch (err) {
throw translateNodeError(err, "eth_getTransactionCount");
}
}
/**
* Fetch current gas prices and apply a level multiplier.
*
* Tries `eth_maxPriorityFeePerGas` + `eth_gasPrice` first (EIP-1559),
* falling back to `eth_gasPrice` alone if the priority-fee method is
* unsupported, and finally to a 1 gwei floor multiplied by `gasLevel`.
*
* @param gasLevel - {@link GasOption} multiplier (default: Medium = 1.2x)
* @returns `[maxFeePerGas, maxPriorityFeePerGas]` as bigints
*/
async getFeeData(gasLevel = GasOption.Medium, options) {
try {
let gasPrice = null;
let maxPriorityFeePerGas = null;
try {
const result = await this.outbound.request({ method: "eth_gasPrice" }, options);
if (typeof result !== "string") throw new Abstraction