Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion src/transactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,31 @@ export function buildTransaction(options: BuildTransactionOptions): ShellTransac
if (options.maxFeePerBlobGas !== undefined && options.maxFeePerBlobGas !== null) {
validateNonNegativeInteger(options.maxFeePerBlobGas, "maxFeePerBlobGas");
}
const txType = options.txType ?? DEFAULT_TX_TYPE;
const hasBlobFee = options.maxFeePerBlobGas !== undefined && options.maxFeePerBlobGas !== null;
const hasBlobHashes =
options.blobVersionedHashes !== undefined && options.blobVersionedHashes !== null;
if (txType === 3) {
if (!hasBlobFee) {
throw new RangeError("type-3 transactions require maxFeePerBlobGas");
}
if (!hasBlobHashes || options.blobVersionedHashes?.length === 0) {
throw new RangeError("type-3 transactions require at least one blobVersionedHash");
}
if ((options.blobVersionedHashes?.length ?? 0) > 6) {
throw new RangeError("type-3 transactions support at most 6 blobVersionedHashes");
}
if (options.to === null) {
throw new RangeError("type-3 transactions cannot create contracts");
}
} else {
if (hasBlobFee) {
throw new RangeError("non-blob transactions cannot set maxFeePerBlobGas");
}
if (hasBlobHashes) {
throw new RangeError("non-blob transactions cannot set blobVersionedHashes");
}
}

return {
chain_id: options.chainId,
Expand All @@ -248,7 +273,7 @@ export function buildTransaction(options: BuildTransactionOptions): ShellTransac
max_fee_per_gas: maxFeePerGas,
max_priority_fee_per_gas: maxPriorityFeePerGas,
access_list: options.accessList ?? null,
tx_type: options.txType ?? DEFAULT_TX_TYPE,
tx_type: txType,
max_fee_per_blob_gas: options.maxFeePerBlobGas ?? null,
blob_versioned_hashes: options.blobVersionedHashes ?? null,
};
Expand Down
75 changes: 75 additions & 0 deletions tests/transactions.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import assert from 'node:assert/strict';
import test from 'node:test';

import { buildTransaction } from '../dist/transactions.js';

const recipient = `0x${'11'.repeat(32)}`;
const blobHash = `0x${'22'.repeat(32)}`;

function baseOptions(overrides = {}) {
return {
chainId: 1337,
nonce: 0,
to: recipient,
...overrides,
};
}

test('buildTransaction rejects blob-only fields on non-blob transactions', () => {
assert.throws(
() => buildTransaction(baseOptions({ maxFeePerBlobGas: 1 })),
/non-blob transactions cannot set maxFeePerBlobGas/,
);
assert.throws(
() => buildTransaction(baseOptions({ blobVersionedHashes: [blobHash] })),
/non-blob transactions cannot set blobVersionedHashes/,
);
});

test('buildTransaction requires complete type-3 blob fields', () => {
assert.throws(
() => buildTransaction(baseOptions({ txType: 3, blobVersionedHashes: [blobHash] })),
/require maxFeePerBlobGas/,
);
assert.throws(
() => buildTransaction(baseOptions({ txType: 3, maxFeePerBlobGas: 1 })),
/require at least one blobVersionedHash/,
);
assert.throws(
() =>
buildTransaction(
baseOptions({
txType: 3,
maxFeePerBlobGas: 1,
blobVersionedHashes: Array(7).fill(blobHash),
}),
),
/at most 6 blobVersionedHashes/,
);
assert.throws(
() =>
buildTransaction(
baseOptions({
txType: 3,
to: null,
maxFeePerBlobGas: 1,
blobVersionedHashes: [blobHash],
}),
),
/cannot create contracts/,
);
});

test('buildTransaction accepts valid type-3 blob fields', () => {
const tx = buildTransaction(
baseOptions({
txType: 3,
maxFeePerBlobGas: 1,
blobVersionedHashes: [blobHash],
}),
);

assert.equal(tx.tx_type, 3);
assert.equal(tx.max_fee_per_blob_gas, 1);
assert.deepEqual(tx.blob_versioned_hashes, [blobHash]);
});