Skip to content
Open
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
84 changes: 84 additions & 0 deletions src.ts/_tests/test-providers-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,90 @@ describe("Test Provider Transaction operations", function() {
});
});

describe("Test getBlockReceipts", function() {
forEach("test getBlockReceipts(block)", testBlock, (providerName, test) => {
// Skip blocks without transactions
if (test.transactions.length === 0) { return null; }

// Skip providers that may not support eth_getBlockReceipts
// Etherscan doesn't support many RPC methods
if (providerName === "EtherscanProvider") { return null; }

return async (provider) => {
// Only test with JsonRpcProvider instances
if (!(provider instanceof JsonRpcProvider)) { return; }

const block = await provider.getBlock(test.number);
assert.ok(block != null, "block != null");
if (block == null) { return; }

const receipts = await provider.getBlockReceipts(block.number);

// Verify receipt count matches transaction count
assert.equal(receipts.length, block.transactions.length,
"receipt count matches transaction count");

// Verify each receipt matches its transaction
for (let i = 0; i < receipts.length; i++) {
const receipt = receipts[i];
const txHash = block.transactions[i];

assert.equal(receipt.hash, txHash, `receipt[${ i }].hash matches`);
assert.equal(receipt.blockNumber, block.number, `receipt[${ i }].blockNumber`);
assert.equal(receipt.blockHash, block.hash, `receipt[${ i }].blockHash`);
}

// If we have testReceipt data for any of these transactions,
// validate against it
for (const receipt of receipts) {
// Find matching test receipt data
const networkName = (await provider.getNetwork()).name;
const testReceiptData = testReceipt[networkName as TestBlockchainNetwork]?.find(
(tr) => tr.hash === receipt.hash
);

if (testReceiptData) {
// Handle provider-specific quirks
let adjustedTest = testReceiptData;
if (providerName === "CloudflareProvider" ||
providerName === "AnkrProvider" ||
providerName === "PocketProvider" ||
providerName === "BlockscoutProvider") {
adjustedTest = Object.assign({ }, adjustedTest, { root: undefined });
}

assertReceipt(receipt, adjustedTest);
}
}
};
});

// Test with a specific block that has known receipts
it("test getBlockReceipts with known receipts", async function() {
this.timeout(60000);

const provider = getProvider("JsonRpcProvider", "mainnet");
if (provider == null || !(provider instanceof JsonRpcProvider)) { this.skip(); }
assert.ok(provider != null, "provider != null");
if (provider == null) { return; }

// Use a block number from testReceipt data
const testReceiptData = testReceipt.mainnet[0]; // legacy receipt
const block = await provider.getBlock(testReceiptData.blockNumber);
assert.ok(block != null, "block != null");
if (block == null) { return; }

const receipts = await provider.getBlockReceipts(testReceiptData.blockNumber);

// Find the specific receipt we're testing
const receipt = receipts.find((r) => r.hash === testReceiptData.hash);
assert.ok(receipt != null, "found expected receipt");

// Validate using existing assertReceipt
assertReceipt(receipt, testReceiptData);
});
});

describe("Test Networks", function() {
const networks = [
"mainnet", "sepolia", "holesky",
Expand Down
31 changes: 31 additions & 0 deletions src.ts/providers/abstract-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,9 @@ export type PerformActionRequest = {
} | {
method: "getTransactionReceipt",
hash: string
} | {
method: "getBlockReceipts",
blockTag: BlockTag
} | {
method: "getTransactionResult",
hash: string
Expand Down Expand Up @@ -1160,6 +1163,34 @@ export class AbstractProvider implements Provider {
return this._wrapTransactionReceipt(params, network);
}

/**
* Resolves to all transaction receipts for %%blockTag%%.
*/
async getBlockReceipts(blockTag: BlockTag | string): Promise<Array<TransactionReceipt>> {
let blockTagStr: string;
if (isHexString(blockTag, 32)) {
// If it's a block hash, we need to get the block number first
const block = await this.getBlock(blockTag);
if (block == null) { return [ ]; }
blockTagStr = toQuantity(block.number);
} else {
const tag = this._getBlockTag(blockTag);
blockTagStr = typeof(tag) === "string" ? tag : await tag;
}

const { network, params } = await resolveProperties({
network: this.getNetwork(),
params: this.#perform({ method: "getBlockReceipts", blockTag: blockTagStr })
});

if (params == null || !Array.isArray(params)) { return [ ]; }

// Wrap raw receipts into proper TransactionReceipt objects
return params.map((r: any) => {
return this._wrapTransactionReceipt(formatTransactionReceipt(r), network);
});
}

async getTransactionResult(hash: string): Promise<null | string> {
const { result } = await resolveProperties({
network: this.getNetwork(),
Expand Down
8 changes: 7 additions & 1 deletion src.ts/providers/provider-jsonrpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -958,6 +958,12 @@ export abstract class JsonRpcApiProvider extends AbstractProvider {
args: [ req.hash ]
};

case "getBlockReceipts":
return {
method: "eth_getBlockReceipts",
args: [ req.blockTag ]
};

case "call":
return {
method: "eth_call",
Expand Down Expand Up @@ -1332,4 +1338,4 @@ function spelunkMessage(value: any): Array<string> {
const result: Array<string> = [ ];
_spelunkMessage(value, result);
return result;
}
}
5 changes: 5 additions & 0 deletions src.ts/providers/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2084,6 +2084,11 @@ export interface Provider extends ContractRunner, EventEmitterable<ProviderEvent
*/
getTransactionReceipt(hash: string): Promise<null | TransactionReceipt>;

/**
* Resolves to all transaction receipts for %%blockTag%%.
*/
getBlockReceipts(blockTag: BlockTag | string): Promise<Array<TransactionReceipt>>;

/**
* Resolves to the result returned by the executions of %%hash%%.
*
Expand Down