@web3-onboard/core
Version:
Web3-Onboard makes it simple to connect Ethereum hardware and software wallets to your dapp. Features standardized spec compliant web3 providers for all supported wallets, framework agnostic modern javascript UI with code splitting, CSS customization, mul
1,445 lines (1,409 loc) • 241 kB
JavaScript
import { h as hexToNumber, d as defineFormatter, a as hexToBigInt, c as createCursor, b as bytesToHex, e as hexToBytes, B as BaseError, H as Hash, f as createView, g as exists, t as toBytes, o as output, w as wrapConstructor, r as rotr, i as isHex, j as toBytes$1, k as toHex, s as size, l as slice, I as InvalidChainIdError, m as isAddress, n as InvalidAddressError, F as FeeCapTooHighError, T as TipAboveFeeCapError, p as InvalidSerializableTransactionError, q as InvalidStorageKeySizeError, u as concatHex, v as InvalidLegacyVError, x as trim, y as defineTransactionRequest, z as pad } from './transactionRequest-be6a8ea9.js';
function defineChain(chain) {
return {
formatters: undefined,
fees: undefined,
serializers: undefined,
...chain,
};
}
const acala = /*#__PURE__*/ defineChain({
id: 787,
name: 'Acala',
network: 'acala',
nativeCurrency: {
name: 'Acala',
symbol: 'ACA',
decimals: 18,
},
rpcUrls: {
public: {
http: ['https://eth-rpc-acala.aca-api.network'],
webSocket: ['wss://eth-rpc-acala.aca-api.network'],
},
default: {
http: ['https://eth-rpc-acala.aca-api.network'],
webSocket: ['wss://eth-rpc-acala.aca-api.network'],
},
},
blockExplorers: {
default: {
name: 'Acala Blockscout',
url: 'https://blockscout.acala.network',
apiUrl: 'https://blockscout.acala.network/api',
},
},
testnet: false,
});
/**
* Predeploy contracts for OP Stack.
* @see https://github.com/ethereum-optimism/optimism/blob/develop/specs/predeploys.md
*/
const contracts = {
gasPriceOracle: { address: '0x420000000000000000000000000000000000000F' },
l1Block: { address: '0x4200000000000000000000000000000000000015' },
l2CrossDomainMessenger: {
address: '0x4200000000000000000000000000000000000007',
},
l2Erc721Bridge: { address: '0x4200000000000000000000000000000000000014' },
l2StandardBridge: { address: '0x4200000000000000000000000000000000000010' },
l2ToL1MessagePasser: {
address: '0x4200000000000000000000000000000000000016',
},
};
const transactionType = {
'0x0': 'legacy',
'0x1': 'eip2930',
'0x2': 'eip1559',
'0x3': 'eip4844',
};
function formatTransaction(transaction) {
const transaction_ = {
...transaction,
blockHash: transaction.blockHash ? transaction.blockHash : null,
blockNumber: transaction.blockNumber
? BigInt(transaction.blockNumber)
: null,
chainId: transaction.chainId ? hexToNumber(transaction.chainId) : undefined,
gas: transaction.gas ? BigInt(transaction.gas) : undefined,
gasPrice: transaction.gasPrice ? BigInt(transaction.gasPrice) : undefined,
maxFeePerBlobGas: transaction.maxFeePerBlobGas
? BigInt(transaction.maxFeePerBlobGas)
: undefined,
maxFeePerGas: transaction.maxFeePerGas
? BigInt(transaction.maxFeePerGas)
: undefined,
maxPriorityFeePerGas: transaction.maxPriorityFeePerGas
? BigInt(transaction.maxPriorityFeePerGas)
: undefined,
nonce: transaction.nonce ? hexToNumber(transaction.nonce) : undefined,
to: transaction.to ? transaction.to : null,
transactionIndex: transaction.transactionIndex
? Number(transaction.transactionIndex)
: null,
type: transaction.type
? transactionType[transaction.type]
: undefined,
typeHex: transaction.type ? transaction.type : undefined,
value: transaction.value ? BigInt(transaction.value) : undefined,
v: transaction.v ? BigInt(transaction.v) : undefined,
};
transaction_.yParity = (() => {
// If `yParity` is provided, we will use it.
if (transaction.yParity)
return Number(transaction.yParity);
// If no `yParity` provided, try derive from `v`.
if (typeof transaction_.v === 'bigint') {
if (transaction_.v === 0n || transaction_.v === 27n)
return 0;
if (transaction_.v === 1n || transaction_.v === 28n)
return 1;
if (transaction_.v >= 35n)
return transaction_.v % 2n === 0n ? 1 : 0;
}
return undefined;
})();
if (transaction_.type === 'legacy') {
delete transaction_.accessList;
delete transaction_.maxFeePerBlobGas;
delete transaction_.maxFeePerGas;
delete transaction_.maxPriorityFeePerGas;
delete transaction_.yParity;
}
if (transaction_.type === 'eip2930') {
delete transaction_.maxFeePerBlobGas;
delete transaction_.maxFeePerGas;
delete transaction_.maxPriorityFeePerGas;
}
if (transaction_.type === 'eip1559') {
delete transaction_.maxFeePerBlobGas;
}
return transaction_;
}
const defineTransaction = /*#__PURE__*/ defineFormatter('transaction', formatTransaction);
function formatBlock(block) {
const transactions = block.transactions?.map((transaction) => {
if (typeof transaction === 'string')
return transaction;
return formatTransaction(transaction);
});
return {
...block,
baseFeePerGas: block.baseFeePerGas ? BigInt(block.baseFeePerGas) : null,
blobGasUsed: block.blobGasUsed ? BigInt(block.blobGasUsed) : undefined,
difficulty: block.difficulty ? BigInt(block.difficulty) : undefined,
excessBlobGas: block.excessBlobGas
? BigInt(block.excessBlobGas)
: undefined,
gasLimit: block.gasLimit ? BigInt(block.gasLimit) : undefined,
gasUsed: block.gasUsed ? BigInt(block.gasUsed) : undefined,
hash: block.hash ? block.hash : null,
logsBloom: block.logsBloom ? block.logsBloom : null,
nonce: block.nonce ? block.nonce : null,
number: block.number ? BigInt(block.number) : null,
size: block.size ? BigInt(block.size) : undefined,
timestamp: block.timestamp ? BigInt(block.timestamp) : undefined,
transactions,
totalDifficulty: block.totalDifficulty
? BigInt(block.totalDifficulty)
: null,
};
}
const defineBlock = /*#__PURE__*/ defineFormatter('block', formatBlock);
function formatLog(log, { args, eventName, } = {}) {
return {
...log,
blockHash: log.blockHash ? log.blockHash : null,
blockNumber: log.blockNumber ? BigInt(log.blockNumber) : null,
logIndex: log.logIndex ? Number(log.logIndex) : null,
transactionHash: log.transactionHash ? log.transactionHash : null,
transactionIndex: log.transactionIndex
? Number(log.transactionIndex)
: null,
...(eventName ? { args, eventName } : {}),
};
}
const receiptStatuses = {
'0x0': 'reverted',
'0x1': 'success',
};
function formatTransactionReceipt(transactionReceipt) {
const receipt = {
...transactionReceipt,
blockNumber: transactionReceipt.blockNumber
? BigInt(transactionReceipt.blockNumber)
: null,
contractAddress: transactionReceipt.contractAddress
? transactionReceipt.contractAddress
: null,
cumulativeGasUsed: transactionReceipt.cumulativeGasUsed
? BigInt(transactionReceipt.cumulativeGasUsed)
: null,
effectiveGasPrice: transactionReceipt.effectiveGasPrice
? BigInt(transactionReceipt.effectiveGasPrice)
: null,
gasUsed: transactionReceipt.gasUsed
? BigInt(transactionReceipt.gasUsed)
: null,
logs: transactionReceipt.logs
? transactionReceipt.logs.map((log) => formatLog(log))
: null,
to: transactionReceipt.to ? transactionReceipt.to : null,
transactionIndex: transactionReceipt.transactionIndex
? hexToNumber(transactionReceipt.transactionIndex)
: null,
status: transactionReceipt.status
? receiptStatuses[transactionReceipt.status]
: null,
type: transactionReceipt.type
? transactionType[transactionReceipt.type] || transactionReceipt.type
: null,
};
if (transactionReceipt.blobGasPrice)
receipt.blobGasPrice = BigInt(transactionReceipt.blobGasPrice);
if (transactionReceipt.blobGasUsed)
receipt.blobGasUsed = BigInt(transactionReceipt.blobGasUsed);
return receipt;
}
const defineTransactionReceipt = /*#__PURE__*/ defineFormatter('transactionReceipt', formatTransactionReceipt);
const formatters$2 = {
block: /*#__PURE__*/ defineBlock({
format(args) {
const transactions = args.transactions?.map((transaction) => {
if (typeof transaction === 'string')
return transaction;
const formatted = formatTransaction(transaction);
if (formatted.typeHex === '0x7e') {
formatted.isSystemTx = transaction.isSystemTx;
formatted.mint = transaction.mint
? hexToBigInt(transaction.mint)
: undefined;
formatted.sourceHash = transaction.sourceHash;
formatted.type = 'deposit';
}
return formatted;
});
return {
transactions,
stateRoot: args.stateRoot,
};
},
}),
transaction: /*#__PURE__*/ defineTransaction({
format(args) {
const transaction = {};
if (args.type === '0x7e') {
transaction.isSystemTx = args.isSystemTx;
transaction.mint = args.mint ? hexToBigInt(args.mint) : undefined;
transaction.sourceHash = args.sourceHash;
transaction.type = 'deposit';
}
return transaction;
},
}),
transactionReceipt: /*#__PURE__*/ defineTransactionReceipt({
format(args) {
return {
l1GasPrice: args.l1GasPrice ? hexToBigInt(args.l1GasPrice) : null,
l1GasUsed: args.l1GasUsed ? hexToBigInt(args.l1GasUsed) : null,
l1Fee: args.l1Fee ? hexToBigInt(args.l1Fee) : null,
l1FeeScalar: args.l1FeeScalar ? Number(args.l1FeeScalar) : null,
};
},
}),
};
function toRlp(bytes, to = 'hex') {
const encodable = getEncodable(bytes);
const cursor = createCursor(new Uint8Array(encodable.length));
encodable.encode(cursor);
if (to === 'hex')
return bytesToHex(cursor.bytes);
return cursor.bytes;
}
function getEncodable(bytes) {
if (Array.isArray(bytes))
return getEncodableList(bytes.map((x) => getEncodable(x)));
return getEncodableBytes(bytes);
}
function getEncodableList(list) {
const bodyLength = list.reduce((acc, x) => acc + x.length, 0);
const sizeOfBodyLength = getSizeOfLength(bodyLength);
const length = (() => {
if (bodyLength <= 55)
return 1 + bodyLength;
return 1 + sizeOfBodyLength + bodyLength;
})();
return {
length,
encode(cursor) {
if (bodyLength <= 55) {
cursor.pushByte(0xc0 + bodyLength);
}
else {
cursor.pushByte(0xc0 + 55 + sizeOfBodyLength);
if (sizeOfBodyLength === 1)
cursor.pushUint8(bodyLength);
else if (sizeOfBodyLength === 2)
cursor.pushUint16(bodyLength);
else if (sizeOfBodyLength === 3)
cursor.pushUint24(bodyLength);
else
cursor.pushUint32(bodyLength);
}
for (const { encode } of list) {
encode(cursor);
}
},
};
}
function getEncodableBytes(bytesOrHex) {
const bytes = typeof bytesOrHex === 'string' ? hexToBytes(bytesOrHex) : bytesOrHex;
const sizeOfBytesLength = getSizeOfLength(bytes.length);
const length = (() => {
if (bytes.length === 1 && bytes[0] < 0x80)
return 1;
if (bytes.length <= 55)
return 1 + bytes.length;
return 1 + sizeOfBytesLength + bytes.length;
})();
return {
length,
encode(cursor) {
if (bytes.length === 1 && bytes[0] < 0x80) {
cursor.pushBytes(bytes);
}
else if (bytes.length <= 55) {
cursor.pushByte(0x80 + bytes.length);
cursor.pushBytes(bytes);
}
else {
cursor.pushByte(0x80 + 55 + sizeOfBytesLength);
if (sizeOfBytesLength === 1)
cursor.pushUint8(bytes.length);
else if (sizeOfBytesLength === 2)
cursor.pushUint16(bytes.length);
else if (sizeOfBytesLength === 3)
cursor.pushUint24(bytes.length);
else
cursor.pushUint32(bytes.length);
cursor.pushBytes(bytes);
}
},
};
}
function getSizeOfLength(length) {
if (length < 2 ** 8)
return 1;
if (length < 2 ** 16)
return 2;
if (length < 2 ** 24)
return 3;
if (length < 2 ** 32)
return 4;
throw new BaseError('Length is too large.');
}
/**
* Compute commitments from a list of blobs.
*
* @example
* ```ts
* import { blobsToCommitments, toBlobs } from 'viem'
* import { kzg } from './kzg'
*
* const blobs = toBlobs({ data: '0x1234' })
* const commitments = blobsToCommitments({ blobs, kzg })
* ```
*/
function blobsToCommitments(parameters) {
const { kzg } = parameters;
const to = parameters.to ?? (typeof parameters.blobs[0] === 'string' ? 'hex' : 'bytes');
const blobs = (typeof parameters.blobs[0] === 'string'
? parameters.blobs.map((x) => hexToBytes(x))
: parameters.blobs);
const commitments = [];
for (const blob of blobs)
commitments.push(Uint8Array.from(kzg.blobToKzgCommitment(blob)));
return (to === 'bytes'
? commitments
: commitments.map((x) => bytesToHex(x)));
}
/**
* Compute the proofs for a list of blobs and their commitments.
*
* @example
* ```ts
* import {
* blobsToCommitments,
* toBlobs
* } from 'viem'
* import { kzg } from './kzg'
*
* const blobs = toBlobs({ data: '0x1234' })
* const commitments = blobsToCommitments({ blobs, kzg })
* const proofs = blobsToProofs({ blobs, commitments, kzg })
* ```
*/
function blobsToProofs(parameters) {
const { kzg } = parameters;
const to = parameters.to ?? (typeof parameters.blobs[0] === 'string' ? 'hex' : 'bytes');
const blobs = (typeof parameters.blobs[0] === 'string'
? parameters.blobs.map((x) => hexToBytes(x))
: parameters.blobs);
const commitments = (typeof parameters.commitments[0] === 'string'
? parameters.commitments.map((x) => hexToBytes(x))
: parameters.commitments);
const proofs = [];
for (let i = 0; i < blobs.length; i++) {
const blob = blobs[i];
const commitment = commitments[i];
proofs.push(Uint8Array.from(kzg.computeBlobKzgProof(blob, commitment)));
}
return (to === 'bytes'
? proofs
: proofs.map((x) => bytesToHex(x)));
}
// Polyfill for Safari 14
function setBigUint64(view, byteOffset, value, isLE) {
if (typeof view.setBigUint64 === 'function')
return view.setBigUint64(byteOffset, value, isLE);
const _32n = BigInt(32);
const _u32_max = BigInt(0xffffffff);
const wh = Number((value >> _32n) & _u32_max);
const wl = Number(value & _u32_max);
const h = isLE ? 4 : 0;
const l = isLE ? 0 : 4;
view.setUint32(byteOffset + h, wh, isLE);
view.setUint32(byteOffset + l, wl, isLE);
}
// Base SHA2 class (RFC 6234)
class SHA2 extends Hash {
constructor(blockLen, outputLen, padOffset, isLE) {
super();
this.blockLen = blockLen;
this.outputLen = outputLen;
this.padOffset = padOffset;
this.isLE = isLE;
this.finished = false;
this.length = 0;
this.pos = 0;
this.destroyed = false;
this.buffer = new Uint8Array(blockLen);
this.view = createView(this.buffer);
}
update(data) {
exists(this);
const { view, buffer, blockLen } = this;
data = toBytes(data);
const len = data.length;
for (let pos = 0; pos < len;) {
const take = Math.min(blockLen - this.pos, len - pos);
// Fast path: we have at least one block in input, cast it to view and process
if (take === blockLen) {
const dataView = createView(data);
for (; blockLen <= len - pos; pos += blockLen)
this.process(dataView, pos);
continue;
}
buffer.set(data.subarray(pos, pos + take), this.pos);
this.pos += take;
pos += take;
if (this.pos === blockLen) {
this.process(view, 0);
this.pos = 0;
}
}
this.length += data.length;
this.roundClean();
return this;
}
digestInto(out) {
exists(this);
output(out, this);
this.finished = true;
// Padding
// We can avoid allocation of buffer for padding completely if it
// was previously not allocated here. But it won't change performance.
const { buffer, view, blockLen, isLE } = this;
let { pos } = this;
// append the bit '1' to the message
buffer[pos++] = 0b10000000;
this.buffer.subarray(pos).fill(0);
// we have less than padOffset left in buffer, so we cannot put length in current block, need process it and pad again
if (this.padOffset > blockLen - pos) {
this.process(view, 0);
pos = 0;
}
// Pad until full block byte with zeros
for (let i = pos; i < blockLen; i++)
buffer[i] = 0;
// Note: sha512 requires length to be 128bit integer, but length in JS will overflow before that
// You need to write around 2 exabytes (u64_max / 8 / (1024**6)) for this to happen.
// So we just write lowest 64 bits of that value.
setBigUint64(view, blockLen - 8, BigInt(this.length * 8), isLE);
this.process(view, 0);
const oview = createView(out);
const len = this.outputLen;
// NOTE: we do division by 4 later, which should be fused in single op with modulo by JIT
if (len % 4)
throw new Error('_sha2: outputLen should be aligned to 32bit');
const outLen = len / 4;
const state = this.get();
if (outLen > state.length)
throw new Error('_sha2: outputLen bigger than state');
for (let i = 0; i < outLen; i++)
oview.setUint32(4 * i, state[i], isLE);
}
digest() {
const { buffer, outputLen } = this;
this.digestInto(buffer);
const res = buffer.slice(0, outputLen);
this.destroy();
return res;
}
_cloneInto(to) {
to || (to = new this.constructor());
to.set(...this.get());
const { blockLen, buffer, length, finished, destroyed, pos } = this;
to.length = length;
to.pos = pos;
to.finished = finished;
to.destroyed = destroyed;
if (length % blockLen)
to.buffer.set(buffer);
return to;
}
}
// SHA2-256 need to try 2^128 hashes to execute birthday attack.
// BTC network is doing 2^67 hashes/sec as per early 2023.
// Choice: a ? b : c
const Chi = (a, b, c) => (a & b) ^ (~a & c);
// Majority function, true if any two inpust is true
const Maj = (a, b, c) => (a & b) ^ (a & c) ^ (b & c);
// Round constants:
// first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311)
// prettier-ignore
const SHA256_K = /* @__PURE__ */ new Uint32Array([
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
]);
// Initial state (first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19):
// prettier-ignore
const IV = /* @__PURE__ */ new Uint32Array([
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
]);
// Temporary buffer, not used to store anything between runs
// Named this way because it matches specification.
const SHA256_W = /* @__PURE__ */ new Uint32Array(64);
class SHA256 extends SHA2 {
constructor() {
super(64, 32, 8, false);
// We cannot use array here since array allows indexing by variable
// which means optimizer/compiler cannot use registers.
this.A = IV[0] | 0;
this.B = IV[1] | 0;
this.C = IV[2] | 0;
this.D = IV[3] | 0;
this.E = IV[4] | 0;
this.F = IV[5] | 0;
this.G = IV[6] | 0;
this.H = IV[7] | 0;
}
get() {
const { A, B, C, D, E, F, G, H } = this;
return [A, B, C, D, E, F, G, H];
}
// prettier-ignore
set(A, B, C, D, E, F, G, H) {
this.A = A | 0;
this.B = B | 0;
this.C = C | 0;
this.D = D | 0;
this.E = E | 0;
this.F = F | 0;
this.G = G | 0;
this.H = H | 0;
}
process(view, offset) {
// Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array
for (let i = 0; i < 16; i++, offset += 4)
SHA256_W[i] = view.getUint32(offset, false);
for (let i = 16; i < 64; i++) {
const W15 = SHA256_W[i - 15];
const W2 = SHA256_W[i - 2];
const s0 = rotr(W15, 7) ^ rotr(W15, 18) ^ (W15 >>> 3);
const s1 = rotr(W2, 17) ^ rotr(W2, 19) ^ (W2 >>> 10);
SHA256_W[i] = (s1 + SHA256_W[i - 7] + s0 + SHA256_W[i - 16]) | 0;
}
// Compression function main loop, 64 rounds
let { A, B, C, D, E, F, G, H } = this;
for (let i = 0; i < 64; i++) {
const sigma1 = rotr(E, 6) ^ rotr(E, 11) ^ rotr(E, 25);
const T1 = (H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i]) | 0;
const sigma0 = rotr(A, 2) ^ rotr(A, 13) ^ rotr(A, 22);
const T2 = (sigma0 + Maj(A, B, C)) | 0;
H = G;
G = F;
F = E;
E = (D + T1) | 0;
D = C;
C = B;
B = A;
A = (T1 + T2) | 0;
}
// Add the compressed chunk to the current hash value
A = (A + this.A) | 0;
B = (B + this.B) | 0;
C = (C + this.C) | 0;
D = (D + this.D) | 0;
E = (E + this.E) | 0;
F = (F + this.F) | 0;
G = (G + this.G) | 0;
H = (H + this.H) | 0;
this.set(A, B, C, D, E, F, G, H);
}
roundClean() {
SHA256_W.fill(0);
}
destroy() {
this.set(0, 0, 0, 0, 0, 0, 0, 0);
this.buffer.fill(0);
}
}
/**
* SHA2-256 hash function
* @param message - data that would be hashed
*/
const sha256$1 = /* @__PURE__ */ wrapConstructor(() => new SHA256());
function sha256(value, to_) {
const to = to_ || 'hex';
const bytes = sha256$1(isHex(value, { strict: false }) ? toBytes$1(value) : value);
if (to === 'bytes')
return bytes;
return toHex(bytes);
}
/**
* Transform a commitment to it's versioned hash.
*
* @example
* ```ts
* import {
* blobsToCommitments,
* commitmentToVersionedHash,
* toBlobs
* } from 'viem'
* import { kzg } from './kzg'
*
* const blobs = toBlobs({ data: '0x1234' })
* const [commitment] = blobsToCommitments({ blobs, kzg })
* const versionedHash = commitmentToVersionedHash({ commitment })
* ```
*/
function commitmentToVersionedHash(parameters) {
const { commitment, version = 1 } = parameters;
const to = parameters.to ?? (typeof commitment === 'string' ? 'hex' : 'bytes');
const versionedHash = sha256(commitment, 'bytes');
versionedHash.set([version], 0);
return (to === 'bytes' ? versionedHash : bytesToHex(versionedHash));
}
/**
* Transform a list of commitments to their versioned hashes.
*
* @example
* ```ts
* import {
* blobsToCommitments,
* commitmentsToVersionedHashes,
* toBlobs
* } from 'viem'
* import { kzg } from './kzg'
*
* const blobs = toBlobs({ data: '0x1234' })
* const commitments = blobsToCommitments({ blobs, kzg })
* const versionedHashes = commitmentsToVersionedHashes({ commitments })
* ```
*/
function commitmentsToVersionedHashes(parameters) {
const { commitments, version } = parameters;
const to = parameters.to ?? (typeof commitments[0] === 'string' ? 'hex' : 'bytes');
const hashes = [];
for (const commitment of commitments) {
hashes.push(commitmentToVersionedHash({
commitment,
to,
version,
}));
}
return hashes;
}
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-4844.md#parameters
/** Blob limit per transaction. */
const blobsPerTransaction = 6;
/** The number of bytes in a BLS scalar field element. */
const bytesPerFieldElement = 32;
/** The number of field elements in a blob. */
const fieldElementsPerBlob = 4096;
/** The number of bytes in a blob. */
const bytesPerBlob = bytesPerFieldElement * fieldElementsPerBlob;
/** Blob bytes limit per transaction. */
const maxBytesPerTransaction = bytesPerBlob * blobsPerTransaction -
// terminator byte (0x80).
1 -
// zero byte (0x00) appended to each field element.
1 * fieldElementsPerBlob * blobsPerTransaction;
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-4844.md#parameters
const versionedHashVersionKzg = 1;
class BlobSizeTooLargeError extends BaseError {
constructor({ maxSize, size }) {
super('Blob size is too large.', {
metaMessages: [`Max: ${maxSize} bytes`, `Given: ${size} bytes`],
});
Object.defineProperty(this, "name", {
enumerable: true,
configurable: true,
writable: true,
value: 'BlobSizeTooLargeError'
});
}
}
class EmptyBlobError extends BaseError {
constructor() {
super('Blob data must not be empty.');
Object.defineProperty(this, "name", {
enumerable: true,
configurable: true,
writable: true,
value: 'EmptyBlobError'
});
}
}
class InvalidVersionedHashSizeError extends BaseError {
constructor({ hash, size, }) {
super(`Versioned hash "${hash}" size is invalid.`, {
metaMessages: ['Expected: 32', `Received: ${size}`],
});
Object.defineProperty(this, "name", {
enumerable: true,
configurable: true,
writable: true,
value: 'InvalidVersionedHashSizeError'
});
}
}
class InvalidVersionedHashVersionError extends BaseError {
constructor({ hash, version, }) {
super(`Versioned hash "${hash}" version is invalid.`, {
metaMessages: [
`Expected: ${versionedHashVersionKzg}`,
`Received: ${version}`,
],
});
Object.defineProperty(this, "name", {
enumerable: true,
configurable: true,
writable: true,
value: 'InvalidVersionedHashVersionError'
});
}
}
/**
* Transforms arbitrary data to blobs.
*
* @example
* ```ts
* import { toBlobs, stringToHex } from 'viem'
*
* const blobs = toBlobs({ data: stringToHex('hello world') })
* ```
*/
function toBlobs(parameters) {
const to = parameters.to ?? (typeof parameters.data === 'string' ? 'hex' : 'bytes');
const data = (typeof parameters.data === 'string'
? hexToBytes(parameters.data)
: parameters.data);
const size_ = size(data);
if (!size_)
throw new EmptyBlobError();
if (size_ > maxBytesPerTransaction)
throw new BlobSizeTooLargeError({
maxSize: maxBytesPerTransaction,
size: size_,
});
const blobs = [];
let active = true;
let position = 0;
while (active) {
const blob = createCursor(new Uint8Array(bytesPerBlob));
let size = 0;
while (size < fieldElementsPerBlob) {
const bytes = data.slice(position, position + (bytesPerFieldElement - 1));
// Push a zero byte so the field element doesn't overflow the BLS modulus.
blob.pushByte(0x00);
// Push the current segment of data bytes.
blob.pushBytes(bytes);
// If we detect that the current segment of data bytes is less than 31 bytes,
// we can stop processing and push a terminator byte to indicate the end of the blob.
if (bytes.length < 31) {
blob.pushByte(0x80);
active = false;
break;
}
size++;
position += 31;
}
blobs.push(blob);
}
return (to === 'bytes'
? blobs.map((x) => x.bytes)
: blobs.map((x) => bytesToHex(x.bytes)));
}
/**
* Transforms arbitrary data (or blobs, commitments, & proofs) into a sidecar array.
*
* @example
* ```ts
* import { toBlobSidecars, stringToHex } from 'viem'
*
* const sidecars = toBlobSidecars({ data: stringToHex('hello world') })
* ```
*
* @example
* ```ts
* import {
* blobsToCommitments,
* toBlobs,
* blobsToProofs,
* toBlobSidecars,
* stringToHex
* } from 'viem'
*
* const blobs = toBlobs({ data: stringToHex('hello world') })
* const commitments = blobsToCommitments({ blobs, kzg })
* const proofs = blobsToProofs({ blobs, commitments, kzg })
*
* const sidecars = toBlobSidecars({ blobs, commitments, proofs })
* ```
*/
function toBlobSidecars(parameters) {
const { data, kzg, to } = parameters;
const blobs = parameters.blobs ?? toBlobs({ data: data, to });
const commitments = parameters.commitments ?? blobsToCommitments({ blobs, kzg: kzg, to });
const proofs = parameters.proofs ?? blobsToProofs({ blobs, commitments, kzg: kzg, to });
const sidecars = [];
for (let i = 0; i < blobs.length; i++)
sidecars.push({
blob: blobs[i],
commitment: commitments[i],
proof: proofs[i],
});
return sidecars;
}
function assertTransactionEIP4844(transaction) {
const { blobVersionedHashes } = transaction;
if (blobVersionedHashes) {
if (blobVersionedHashes.length === 0)
throw new EmptyBlobError();
for (const hash of blobVersionedHashes) {
const size_ = size(hash);
const version = hexToNumber(slice(hash, 0, 1));
if (size_ !== 32)
throw new InvalidVersionedHashSizeError({ hash, size: size_ });
if (version !== versionedHashVersionKzg)
throw new InvalidVersionedHashVersionError({
hash,
version,
});
}
}
assertTransactionEIP1559(transaction);
}
function assertTransactionEIP1559(transaction) {
const { chainId, maxPriorityFeePerGas, maxFeePerGas, to } = transaction;
if (chainId <= 0)
throw new InvalidChainIdError({ chainId });
if (to && !isAddress(to))
throw new InvalidAddressError({ address: to });
if (maxFeePerGas && maxFeePerGas > 2n ** 256n - 1n)
throw new FeeCapTooHighError({ maxFeePerGas });
if (maxPriorityFeePerGas &&
maxFeePerGas &&
maxPriorityFeePerGas > maxFeePerGas)
throw new TipAboveFeeCapError({ maxFeePerGas, maxPriorityFeePerGas });
}
function assertTransactionEIP2930(transaction) {
const { chainId, maxPriorityFeePerGas, gasPrice, maxFeePerGas, to } = transaction;
if (chainId <= 0)
throw new InvalidChainIdError({ chainId });
if (to && !isAddress(to))
throw new InvalidAddressError({ address: to });
if (maxPriorityFeePerGas || maxFeePerGas)
throw new BaseError('`maxFeePerGas`/`maxPriorityFeePerGas` is not a valid EIP-2930 Transaction attribute.');
if (gasPrice && gasPrice > 2n ** 256n - 1n)
throw new FeeCapTooHighError({ maxFeePerGas: gasPrice });
}
function assertTransactionLegacy(transaction) {
const { chainId, maxPriorityFeePerGas, gasPrice, maxFeePerGas, to, accessList, } = transaction;
if (to && !isAddress(to))
throw new InvalidAddressError({ address: to });
if (typeof chainId !== 'undefined' && chainId <= 0)
throw new InvalidChainIdError({ chainId });
if (maxPriorityFeePerGas || maxFeePerGas)
throw new BaseError('`maxFeePerGas`/`maxPriorityFeePerGas` is not a valid Legacy Transaction attribute.');
if (gasPrice && gasPrice > 2n ** 256n - 1n)
throw new FeeCapTooHighError({ maxFeePerGas: gasPrice });
if (accessList)
throw new BaseError('`accessList` is not a valid Legacy Transaction attribute.');
}
function getTransactionType(transaction) {
if (transaction.type)
return transaction.type;
if (typeof transaction.blobs !== 'undefined' ||
typeof transaction.blobVersionedHashes !== 'undefined' ||
typeof transaction.maxFeePerBlobGas !== 'undefined' ||
typeof transaction.sidecars !== 'undefined')
return 'eip4844';
if (typeof transaction.maxFeePerGas !== 'undefined' ||
typeof transaction.maxPriorityFeePerGas !== 'undefined') {
return 'eip1559';
}
if (typeof transaction.gasPrice !== 'undefined') {
if (typeof transaction.accessList !== 'undefined')
return 'eip2930';
return 'legacy';
}
throw new InvalidSerializableTransactionError({ transaction });
}
/*
* Serialize an EIP-2930 access list
* @remarks
* Use to create a transaction serializer with support for EIP-2930 access lists
*
* @param accessList - Array of objects of address and arrays of Storage Keys
* @throws InvalidAddressError, InvalidStorageKeySizeError
* @returns Array of hex strings
*/
function serializeAccessList(accessList) {
if (!accessList || accessList.length === 0)
return [];
const serializedAccessList = [];
for (let i = 0; i < accessList.length; i++) {
const { address, storageKeys } = accessList[i];
for (let j = 0; j < storageKeys.length; j++) {
if (storageKeys[j].length - 2 !== 64) {
throw new InvalidStorageKeySizeError({ storageKey: storageKeys[j] });
}
}
if (!isAddress(address, { strict: false })) {
throw new InvalidAddressError({ address });
}
serializedAccessList.push([address, storageKeys]);
}
return serializedAccessList;
}
function serializeTransaction$3(transaction, signature) {
const type = getTransactionType(transaction);
if (type === 'eip1559')
return serializeTransactionEIP1559(transaction, signature);
if (type === 'eip2930')
return serializeTransactionEIP2930(transaction, signature);
if (type === 'eip4844')
return serializeTransactionEIP4844(transaction, signature);
return serializeTransactionLegacy(transaction, signature);
}
function serializeTransactionEIP4844(transaction, signature) {
const { chainId, gas, nonce, to, value, maxFeePerBlobGas, maxFeePerGas, maxPriorityFeePerGas, accessList, data, } = transaction;
assertTransactionEIP4844(transaction);
let blobVersionedHashes = transaction.blobVersionedHashes;
let sidecars = transaction.sidecars;
// If `blobs` are passed, we will need to compute the KZG commitments & proofs.
if (transaction.blobs &&
(typeof blobVersionedHashes === 'undefined' ||
typeof sidecars === 'undefined')) {
const blobs = (typeof transaction.blobs[0] === 'string'
? transaction.blobs
: transaction.blobs.map((x) => bytesToHex(x)));
const kzg = transaction.kzg;
const commitments = blobsToCommitments({
blobs,
kzg,
});
if (typeof blobVersionedHashes === 'undefined')
blobVersionedHashes = commitmentsToVersionedHashes({
commitments,
});
if (typeof sidecars === 'undefined') {
const proofs = blobsToProofs({ blobs, commitments, kzg });
sidecars = toBlobSidecars({ blobs, commitments, proofs });
}
}
const serializedAccessList = serializeAccessList(accessList);
const serializedTransaction = [
toHex(chainId),
nonce ? toHex(nonce) : '0x',
maxPriorityFeePerGas ? toHex(maxPriorityFeePerGas) : '0x',
maxFeePerGas ? toHex(maxFeePerGas) : '0x',
gas ? toHex(gas) : '0x',
to ?? '0x',
value ? toHex(value) : '0x',
data ?? '0x',
serializedAccessList,
maxFeePerBlobGas ? toHex(maxFeePerBlobGas) : '0x',
blobVersionedHashes ?? [],
...toYParitySignatureArray(transaction, signature),
];
const blobs = [];
const commitments = [];
const proofs = [];
if (sidecars)
for (let i = 0; i < sidecars.length; i++) {
const { blob, commitment, proof } = sidecars[i];
blobs.push(blob);
commitments.push(commitment);
proofs.push(proof);
}
return concatHex([
'0x03',
sidecars
? // If sidecars are enabled, envelope turns into a "wrapper":
toRlp([serializedTransaction, blobs, commitments, proofs])
: // If sidecars are disabled, standard envelope is used:
toRlp(serializedTransaction),
]);
}
function serializeTransactionEIP1559(transaction, signature) {
const { chainId, gas, nonce, to, value, maxFeePerGas, maxPriorityFeePerGas, accessList, data, } = transaction;
assertTransactionEIP1559(transaction);
const serializedAccessList = serializeAccessList(accessList);
const serializedTransaction = [
toHex(chainId),
nonce ? toHex(nonce) : '0x',
maxPriorityFeePerGas ? toHex(maxPriorityFeePerGas) : '0x',
maxFeePerGas ? toHex(maxFeePerGas) : '0x',
gas ? toHex(gas) : '0x',
to ?? '0x',
value ? toHex(value) : '0x',
data ?? '0x',
serializedAccessList,
...toYParitySignatureArray(transaction, signature),
];
return concatHex([
'0x02',
toRlp(serializedTransaction),
]);
}
function serializeTransactionEIP2930(transaction, signature) {
const { chainId, gas, data, nonce, to, value, accessList, gasPrice } = transaction;
assertTransactionEIP2930(transaction);
const serializedAccessList = serializeAccessList(accessList);
const serializedTransaction = [
toHex(chainId),
nonce ? toHex(nonce) : '0x',
gasPrice ? toHex(gasPrice) : '0x',
gas ? toHex(gas) : '0x',
to ?? '0x',
value ? toHex(value) : '0x',
data ?? '0x',
serializedAccessList,
...toYParitySignatureArray(transaction, signature),
];
return concatHex([
'0x01',
toRlp(serializedTransaction),
]);
}
function serializeTransactionLegacy(transaction, signature) {
const { chainId = 0, gas, data, nonce, to, value, gasPrice } = transaction;
assertTransactionLegacy(transaction);
let serializedTransaction = [
nonce ? toHex(nonce) : '0x',
gasPrice ? toHex(gasPrice) : '0x',
gas ? toHex(gas) : '0x',
to ?? '0x',
value ? toHex(value) : '0x',
data ?? '0x',
];
if (signature) {
const v = (() => {
// EIP-155 (inferred chainId)
if (signature.v >= 35n) {
const inferredChainId = (signature.v - 35n) / 2n;
if (inferredChainId > 0)
return signature.v;
return 27n + (signature.v === 35n ? 0n : 1n);
}
// EIP-155 (explicit chainId)
if (chainId > 0)
return BigInt(chainId * 2) + BigInt(35n + signature.v - 27n);
// Pre-EIP-155 (no chainId)
const v = 27n + (signature.v === 27n ? 0n : 1n);
if (signature.v !== v)
throw new InvalidLegacyVError({ v: signature.v });
return v;
})();
serializedTransaction = [
...serializedTransaction,
toHex(v),
signature.r,
signature.s,
];
}
else if (chainId > 0) {
serializedTransaction = [
...serializedTransaction,
toHex(chainId),
'0x',
'0x',
];
}
return toRlp(serializedTransaction);
}
function toYParitySignatureArray(transaction, signature) {
const { r, s, v, yParity } = signature ?? transaction;
if (typeof r === 'undefined')
return [];
if (typeof s === 'undefined')
return [];
if (typeof v === 'undefined' && typeof yParity === 'undefined')
return [];
const yParity_ = (() => {
if (typeof yParity === 'number')
return yParity ? toHex(1) : '0x';
if (v === 0n)
return '0x';
if (v === 1n)
return toHex(1);
return v === 27n ? '0x' : toHex(1);
})();
return [yParity_, trim(r), trim(s)];
}
function serializeTransaction$2(transaction, signature) {
if (isDeposit(transaction))
return serializeTransactionDeposit(transaction);
return serializeTransaction$3(transaction, signature);
}
const serializers$2 = {
transaction: serializeTransaction$2,
};
function serializeTransactionDeposit(transaction) {
assertTransactionDeposit(transaction);
const { sourceHash, data, from, gas, isSystemTx, mint, to, value } = transaction;
const serializedTransaction = [
sourceHash,
from,
to ?? '0x',
mint ? toHex(mint) : '0x',
value ? toHex(value) : '0x',
gas ? toHex(gas) : '0x',
isSystemTx ? '0x1' : '0x',
data ?? '0x',
];
return concatHex([
'0x7e',
toRlp(serializedTransaction),
]);
}
function isDeposit(transaction) {
if (transaction.type === 'deposit')
return true;
if (typeof transaction.sourceHash !== 'undefined')
return true;
return false;
}
function assertTransactionDeposit(transaction) {
const { from, to } = transaction;
if (from && !isAddress(from))
throw new InvalidAddressError({ address: from });
if (to && !isAddress(to))
throw new InvalidAddressError({ address: to });
}
const chainConfig$2 = {
contracts,
formatters: formatters$2,
serializers: serializers$2,
};
const sourceId$o = 1; // mainnet
const ancient8 = /*#__PURE__*/ defineChain({
...chainConfig$2,
id: 888888888,
name: 'Ancient8',
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
rpcUrls: {
default: {
http: ['https://rpc.ancient8.gg'],
},
},
blockExplorers: {
default: {
name: 'Ancient8 explorer',
url: 'https://scan.ancient8.gg',
apiUrl: 'https://scan.ancient8.gg/api',
},
},
contracts: {
...chainConfig$2.contracts,
l2OutputOracle: {
[sourceId$o]: {
address: '0xB09DC08428C8b4EFB4ff9C0827386CDF34277996',
},
},
portal: {
[sourceId$o]: {
address: '0x639F2AECE398Aa76b07e59eF6abe2cFe32bacb68',
blockCreated: 19070571,
},
},
l1StandardBridge: {
[sourceId$o]: {
address: '0xd5e3eDf5b68135D559D572E26bF863FBC1950033',
blockCreated: 19070571,
},
},
},
sourceId: sourceId$o,
});
const sourceId$n = 11_155_111; // sepolia
const ancient8Sepolia = /*#__PURE__*/ defineChain({
...chainConfig$2,
id: 28122024,
name: 'Ancient8 Testnet',
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
rpcUrls: {
default: {
http: ['https://rpcv2-testnet.ancient8.gg'],
},
},
blockExplorers: {
default: {
name: 'Ancient8 Celestia Testnet explorer',
url: 'https://scanv2-testnet.ancient8.gg',
apiUrl: 'https://scanv2-testnet.ancient8.gg/api',
},
},
contracts: {
...chainConfig$2.contracts,
l2OutputOracle: {
[sourceId$n]: {
address: '0x942fD5017c0F60575930D8574Eaca13BEcD6e1bB',
},
},
portal: {
[sourceId$n]: {
address: '0xfa1d9E26A6aCD7b22115D27572c1221B9803c960',
blockCreated: 4972908,
},
},
l1StandardBridge: {
[sourceId$n]: {
address: '0xF6Bc0146d3c74D48306e79Ae134A260E418C9335',
blockCreated: 4972908,
},
},
},
sourceId: sourceId$n,
});
const anvil = /*#__PURE__*/ defineChain({
id: 31_337,
name: 'Anvil',
nativeCurrency: {
decimals: 18,
name: 'Ether',
symbol: 'ETH',
},
rpcUrls: {
default: {
http: ['http://127.0.0.1:8545'],
webSocket: ['ws://127.0.0.1:8545'],
},
},
});
const apexTestnet = /*#__PURE__*/ defineChain({
id: 3993,
name: 'APEX Testnet',
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
rpcUrls: {
default: {
http: ['https://rpc-testnet.apexlayer.xyz'],
},
},
blockExplorers: {
default: {
name: 'Blockscout',
url: 'https://exp-testnet.apexlayer.xyz',
apiUrl: 'https://exp-testnet.apexlayer.xyz/api',
},
},
contracts: {
multicall3: {
address: '0xf7642be33a6b18D16a995657adb5a68CD0438aE2',
blockCreated: 283775,
},
},
testnet: true,
});
const arbitrum = /*#__PURE__*/ defineChain({
id: 42_161,
name: 'Arbitrum One',
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
rpcUrls: {
default: {
http: ['https://arb1.arbitrum.io/rpc'],
},
},
blockExplorers: {
default: {
name: 'Arbiscan',
url: 'https://arbiscan.io',
apiUrl: 'https://api.arbiscan.io/api',
},
},
contracts: {
multicall3: {
address: '0xca11bde05977b3631167028862be2a173976ca11',
blockCreated: 7654707,
},
},
});
const arbitrumGoerli = /*#__PURE__*/ defineChain({
id: 421_613,
name: 'Arbitrum Goerli',
nativeCurrency: {
name: 'Arbitrum Goerli Ether',
symbol: 'ETH',
decimals: 18,
},
rpcUrls: {
default: {
http: ['https://goerli-rollup.arbitrum.io/rpc'],
},
},
blockExplorers: {
default: {
name: 'Arbiscan',
url: 'https://goerli.arbiscan.io',
},
},
contracts: {
multicall3: {
address: '0xca11bde05977b3631167028862be2a173976ca11',
blockCreated: 88114,
},
},
testnet: true,
});
const arbitrumNova = /*#__PURE__*/ defineChain({
id: 42_170,
name: 'Arbitrum Nova',
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
rpcUrls: {
default: {
http: ['https://nova.arbitrum.io/rpc'],
},
},
blockExplorers: {
default: {
name: 'Arbiscan',
url: 'https://nova.arbiscan.io',
apiUrl: 'https://api-nova.arbiscan.io/api',
},
},
contracts: {
multicall3: {
address: '0xca11bde05977b3631167028862be2a173976ca11',
blockCreated: 1746963,
},
},
});
const astar = /*#__PURE__*/ defineChain({
id: 592,
name: 'Astar',
network: 'astar-mainnet',
nativeCurrency: {
name: 'Astar',
symbol: 'ASTR',
decimals: 18,
},
rpcUrls: {
default: { http: ['https://astar.api.onfinality.io/public'] },
},
blockExplorers: {
default: {
name: 'Astar Subscan',
url: 'https://astar.subscan.io',
},
},
contracts: {
multicall3: {
address: '0xca11bde05977b3631167028862be2a173976ca11',
blockCreated: 761794,
},
},
testnet: false,
});
const astarZkEVM = /*#__PURE__*/ defineChain({
id: 3_776,
name: 'Astar zkEVM',
network: 'AstarZkEVM',
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
rpcUrls: {
default: {
http: ['https://rpc.startale.com/astar-zkevm'],
},
},
blockExplorers: {
default: {
name: 'Astar zkEVM Explorer',
url: 'https://astar-zkevm.explorer.startale.com',
},
},
contracts: {
multicall3: {
address: '0xca11bde05977b3631167028862be2a173976ca11',
blockCreated: 93528,
},
},
testnet: fals