diff --git a/.gitignore b/.gitignore index cf84bf3d88..497bd3e2a4 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ tests/e2e/artifacts/ # Logs logs +!/packages/worker/test/logs/ *.log npm-debug.log* yarn-debug.log* diff --git a/packages/worker/src/migrations/1699955766418-SetTransferTypeForEmptyBlockTransfers.ts b/packages/worker/src/migrations/1699955766418-SetTransferTypeForEmptyBlockTransfers.ts new file mode 100644 index 0000000000..530b535dfe --- /dev/null +++ b/packages/worker/src/migrations/1699955766418-SetTransferTypeForEmptyBlockTransfers.ts @@ -0,0 +1,22 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class SetTransferTypeForEmptyBlockTransfers1699955766418 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + WITH "updatedTransferNumbers" AS + ( + UPDATE transfers + SET "type" = 'transfer', "isFeeOrRefund" = false, "isInternal" = true + WHERE "transactionHash" IS NULL + RETURNING number + ) + UPDATE "addressTransfers" + SET "isFeeOrRefund" = false, "isInternal" = true + FROM "updatedTransferNumbers" + WHERE "transferNumber" = "updatedTransferNumbers"."number" + `); + } + + // eslint-disable-next-line @typescript-eslint/no-empty-function + public async down(): Promise {} +} diff --git a/packages/worker/src/transfer/extractHandlers/transfer/default.handler.spec.ts b/packages/worker/src/transfer/extractHandlers/transfer/default.handler.spec.ts index 20c96ec742..579e25ef74 100644 --- a/packages/worker/src/transfer/extractHandlers/transfer/default.handler.spec.ts +++ b/packages/worker/src/transfer/extractHandlers/transfer/default.handler.spec.ts @@ -123,18 +123,32 @@ describe("defaultTransferHandler", () => { expect(result.isFeeOrRefund).toBe(true); }); - it("extracts transfer of refund type if from address is a bootloader address", () => { + it("extracts transfer of refund type if from address is a bootloader address and there are transaction details", () => { + const transactionDetails = mock(); log.topics[1] = "0x0000000000000000000000000000000000000000000000000000000000008001"; - const result = defaultTransferHandler.extract(log, blockDetails); + const result = defaultTransferHandler.extract(log, blockDetails, transactionDetails); expect(result.type).toBe(TransferType.Refund); }); - it("adds isFeeOrRefund as true if from address is a bootloader address", () => { + it("extracts transfer of transfer type if from address is a bootloader address and there are no transaction details", () => { log.topics[1] = "0x0000000000000000000000000000000000000000000000000000000000008001"; const result = defaultTransferHandler.extract(log, blockDetails); + expect(result.type).toBe(TransferType.Transfer); + }); + + it("adds isFeeOrRefund as true if from address is a bootloader address and there are transaction details", () => { + const transactionDetails = mock(); + log.topics[1] = "0x0000000000000000000000000000000000000000000000000000000000008001"; + const result = defaultTransferHandler.extract(log, blockDetails, transactionDetails); expect(result.isFeeOrRefund).toBe(true); }); + it("adds isFeeOrRefund as false if from address is a bootloader address and there are no transaction details", () => { + log.topics[1] = "0x0000000000000000000000000000000000000000000000000000000000008001"; + const result = defaultTransferHandler.extract(log, blockDetails); + expect(result.isFeeOrRefund).toBe(false); + }); + it("extracts transfer of transfer type if neither to address nor from address is a bootload address", () => { const result = defaultTransferHandler.extract(log, blockDetails); expect(result.type).toBe(TransferType.Transfer); diff --git a/packages/worker/src/transfer/extractHandlers/transfer/default.handler.ts b/packages/worker/src/transfer/extractHandlers/transfer/default.handler.ts index e14f386a1c..1762bc2df8 100644 --- a/packages/worker/src/transfer/extractHandlers/transfer/default.handler.ts +++ b/packages/worker/src/transfer/extractHandlers/transfer/default.handler.ts @@ -19,7 +19,10 @@ export const defaultTransferHandler: ExtractTransferHandler = { let transferType: TransferType = TransferType.Transfer; if (parsedLog.args.to === utils.BOOTLOADER_FORMAL_ADDRESS) { transferType = TransferType.Fee; - } else if (parsedLog.args.from === utils.BOOTLOADER_FORMAL_ADDRESS) { + } + // if transactionDetails is null it means that this transfer comes from a block with + // no transactions and it is a reward to an operator address, so it's a transfer and not a refund + else if (parsedLog.args.from === utils.BOOTLOADER_FORMAL_ADDRESS && transactionDetails) { transferType = TransferType.Refund; } diff --git a/packages/worker/src/transfer/transfer.service.spec.ts b/packages/worker/src/transfer/transfer.service.spec.ts index 682e70ad48..fe5bc10411 100644 --- a/packages/worker/src/transfer/transfer.service.spec.ts +++ b/packages/worker/src/transfer/transfer.service.spec.ts @@ -53,6 +53,7 @@ import * as addressOutOfRange from "../../test/transactionReceipts/address-out-o import * as logParsingError from "../../test/transactionReceipts/log-parsing-error.json"; import * as noDepositAfterFee from "../../test/transactionReceipts/no-deposit-after-fee.json"; import * as feeWithNoDeposits from "../../test/transactionReceipts/fee-with-no-deposits.json"; +import * as blockWithNoTxsLogs from "../../test/logs/block-with-no-txs-logs.json"; jest.mock("../logger", () => ({ default: { @@ -2285,5 +2286,35 @@ describe("TransferService", () => { expect(result).toStrictEqual(expectedTransfers); }); }); + + describe("block with no transactions", () => { + it("properly saves transfers", async () => { + const blockDate = new Date(); + blockDetails.timestamp = blockDate.getTime() / 1000; + + const expectedTransfers = [ + { + amount: BigNumber.from("0xf22ec29c9c4980"), + blockNumber: 6711853, + from: "0x0000000000000000000000000000000000008001", + isFeeOrRefund: false, + isInternal: true, + logIndex: 0, + timestamp: blockDate, + to: "0xa9232040bf0e0aea2578a5b2243f2916dbfc0a69", + tokenAddress: "0x000000000000000000000000000000000000800a", + tokenType: "ETH", + transactionHash: "0x0000000000000000000000000000000000000000000000000000000000000000", + transactionIndex: 0, + type: "transfer", + }, + ]; + + const result = await transferService.saveTransfers(blockWithNoTxsLogs, blockDetails); + expect(transferRepositoryMock.addMany).toHaveBeenCalledTimes(1); + expect(transferRepositoryMock.addMany).toHaveBeenCalledWith(expectedTransfers); + expect(result).toStrictEqual(expectedTransfers); + }); + }); }); }); diff --git a/packages/worker/test/logs/block-with-no-txs-logs.json b/packages/worker/test/logs/block-with-no-txs-logs.json new file mode 100644 index 0000000000..fd00518101 --- /dev/null +++ b/packages/worker/test/logs/block-with-no-txs-logs.json @@ -0,0 +1,18 @@ +[ + { + "blockNumber": 6711853, + "blockHash": "0xe9b0940095e6e708deebd75fa0320cad868ec5029f92cf8cc8fb464314f5141e", + "transactionIndex": 0, + "removed": false, + "address": "0x000000000000000000000000000000000000800A", + "data": "0x00000000000000000000000000000000000000000000000000f22ec29c9c4980", + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x0000000000000000000000000000000000000000000000000000000000008001", + "0x000000000000000000000000a9232040bf0e0aea2578a5b2243f2916dbfc0a69" + ], + "transactionHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "logIndex": 0, + "l1BatchNumber": 76725 + } +] \ No newline at end of file