diff --git a/packages/api/src/api/dtos/account/accountTransaction.dto.ts b/packages/api/src/api/dtos/account/accountTransaction.dto.ts index ed20047581..9687283b37 100644 --- a/packages/api/src/api/dtos/account/accountTransaction.dto.ts +++ b/packages/api/src/api/dtos/account/accountTransaction.dto.ts @@ -12,8 +12,10 @@ export class AccountTransactionDto { type: String, description: "The to address of this transaction", example: "0xc7e0220d02d549c4846A6EC31D89C3B670Ebe35C", + examples: ["0xc7e0220d02d549c4846A6EC31D89C3B670Ebe35C", null], + nullable: true, }) - public readonly to: string; + public readonly to?: string; @ApiProperty({ type: String, diff --git a/packages/api/src/api/transaction/transaction.controller.spec.ts b/packages/api/src/api/transaction/transaction.controller.spec.ts index 049fec6b5e..14d10dbe19 100644 --- a/packages/api/src/api/transaction/transaction.controller.spec.ts +++ b/packages/api/src/api/transaction/transaction.controller.spec.ts @@ -3,8 +3,7 @@ import { mock } from "jest-mock-extended"; import { Logger } from "@nestjs/common"; import { TransactionService } from "../../transaction/transaction.service"; import { TransactionReceiptService } from "../../transaction/transactionReceipt.service"; -import { TransactionStatus } from "../../transaction/entities/transaction.entity"; -import { TransactionDetails } from "../../transaction/entities/transactionDetails.entity"; +import { TransactionStatus, Transaction } from "../../transaction/entities/transaction.entity"; import { TransactionReceipt } from "../../transaction/entities/transactionReceipt.entity"; import { ResponseStatus, ResponseMessage } from "../dtos/common/responseBase.dto"; import { TransactionController } from "./transaction.controller"; @@ -57,7 +56,7 @@ describe("TransactionController", () => { it("returns isError as 0 when transaction is successful", async () => { jest .spyOn(transactionServiceMock, "findOne") - .mockResolvedValue({ status: TransactionStatus.Included } as TransactionDetails); + .mockResolvedValue({ status: TransactionStatus.Included } as Transaction); const response = await controller.getTransactionStatus(transactionHash); expect(response).toEqual({ @@ -73,7 +72,7 @@ describe("TransactionController", () => { it("returns isError as 1 when transaction is failed", async () => { jest .spyOn(transactionServiceMock, "findOne") - .mockResolvedValue({ status: TransactionStatus.Failed } as TransactionDetails); + .mockResolvedValue({ status: TransactionStatus.Failed } as Transaction); const response = await controller.getTransactionStatus(transactionHash); expect(response).toEqual({ @@ -91,7 +90,7 @@ describe("TransactionController", () => { status: TransactionStatus.Failed, error: "Error", revertReason: "Reverted", - } as TransactionDetails); + } as Transaction); const response = await controller.getTransactionStatus(transactionHash); expect(response).toEqual({ @@ -107,7 +106,7 @@ describe("TransactionController", () => { it("returns transaction revert reason in errDescription when transaction is failed and transaction revert reason is present", async () => { jest .spyOn(transactionServiceMock, "findOne") - .mockResolvedValue({ status: TransactionStatus.Failed, revertReason: "Reverted" } as TransactionDetails); + .mockResolvedValue({ status: TransactionStatus.Failed, revertReason: "Reverted" } as Transaction); const response = await controller.getTransactionStatus(transactionHash); expect(response).toEqual({ @@ -123,7 +122,7 @@ describe("TransactionController", () => { it("returns empty errDescription when transaction is failed and transaction error and revert reason are not present", async () => { jest .spyOn(transactionServiceMock, "findOne") - .mockResolvedValue({ status: TransactionStatus.Failed } as TransactionDetails); + .mockResolvedValue({ status: TransactionStatus.Failed } as Transaction); const response = await controller.getTransactionStatus(transactionHash); expect(response).toEqual({ diff --git a/packages/api/src/transaction/dtos/transaction.dto.ts b/packages/api/src/transaction/dtos/transaction.dto.ts index fefc44049a..5909bd6877 100644 --- a/packages/api/src/transaction/dtos/transaction.dto.ts +++ b/packages/api/src/transaction/dtos/transaction.dto.ts @@ -12,7 +12,8 @@ export class TransactionDto { @ApiProperty({ type: String, description: "The address this transaction is to", - example: ["0xc7e0220d02d549c4846A6EC31D89C3B670Ebe35C", null], + example: "0xc7e0220d02d549c4846A6EC31D89C3B670Ebe35C", + examples: ["0xc7e0220d02d549c4846A6EC31D89C3B670Ebe35C", null], nullable: true, }) public readonly to?: string; @@ -207,17 +208,17 @@ export class TransactionDto { public readonly revertReason?: string; @ApiProperty({ - type: Boolean, - description: "Is the transaction EVM-like", - example: true, - nullable: true, + type: String, + description: "Gas used by the transaction", + example: "50000000", }) - public readonly isEvmLike?: boolean; + public readonly gasUsed: string; @ApiProperty({ type: String, - description: "Address of the first deployed EVM contract", - example: ["0xc7e0220d02d549c4846A6EC31D89C3B670Ebe35C", null], + description: "Address of the deployed contract", + example: "50000000", + examples: ["0xc7e0220d02d549c4846A6EC31D89C3B670Ebe35C", null], nullable: true, }) public readonly contractAddress?: string; diff --git a/packages/api/src/transaction/dtos/transactionDetails.dto.ts b/packages/api/src/transaction/dtos/transactionDetails.dto.ts deleted file mode 100644 index 0f18c8b72a..0000000000 --- a/packages/api/src/transaction/dtos/transactionDetails.dto.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { ApiProperty } from "@nestjs/swagger"; -import { TransactionDto } from "./transaction.dto"; - -export class TransactionDetailsDto extends TransactionDto { - @ApiProperty({ - type: String, - description: "Gas used by the transaction", - example: "50000000", - }) - public readonly gasUsed: string; -} diff --git a/packages/api/src/transaction/entities/addressTransaction.entity.ts b/packages/api/src/transaction/entities/addressTransaction.entity.ts index 2a0502aa51..2e426d5eeb 100644 --- a/packages/api/src/transaction/entities/addressTransaction.entity.ts +++ b/packages/api/src/transaction/entities/addressTransaction.entity.ts @@ -19,8 +19,8 @@ export class AddressTransaction extends BaseEntity { @Column({ type: "bytea", transformer: hexTransformer }) public readonly transactionHash: string; - @Column({ type: "bytea", transformer: normalizeAddressTransformer }) - public readonly address: string; + @Column({ type: "bytea", transformer: normalizeAddressTransformer, nullable: true }) + public readonly address?: string; @Index() @Column({ type: "bigint", transformer: bigIntNumberTransformer }) diff --git a/packages/api/src/transaction/entities/transaction.entity.ts b/packages/api/src/transaction/entities/transaction.entity.ts index a0871358d6..0c67a16240 100644 --- a/packages/api/src/transaction/entities/transaction.entity.ts +++ b/packages/api/src/transaction/entities/transaction.entity.ts @@ -107,12 +107,6 @@ export class Transaction extends BaseEntity { @Column({ nullable: true }) public readonly revertReason?: string; - @Column({ type: "boolean", nullable: true }) - public readonly isEvmLike?: boolean; - - @Column({ type: "bytea", transformer: normalizeAddressTransformer, nullable: true }) - public readonly contractAddress?: string; - public get status(): TransactionStatus { if (this.receiptStatus === 0) { return TransactionStatus.Failed; @@ -147,9 +141,17 @@ export class Transaction extends BaseEntity { return !!this.batch; } + public get gasUsed(): string { + return this.transactionReceipt ? this.transactionReceipt.gasUsed : null; + } + + public get contractAddress(): string { + return this.transactionReceipt ? this.transactionReceipt.contractAddress : null; + } + toJSON(): any { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { number, receiptStatus, batch, ...restFields } = this; + const { number, receiptStatus, batch, transactionReceipt, ...restFields } = this; return { ...restFields, status: this.status, @@ -157,6 +159,8 @@ export class Transaction extends BaseEntity { executeTxHash: this.executeTxHash, proveTxHash: this.proveTxHash, isL1BatchSealed: this.isL1BatchSealed, + gasUsed: this.gasUsed, + contractAddress: this.contractAddress, }; } } diff --git a/packages/api/src/transaction/entities/transactionDetails.entity.ts b/packages/api/src/transaction/entities/transactionDetails.entity.ts deleted file mode 100644 index 1b8fdb9f4f..0000000000 --- a/packages/api/src/transaction/entities/transactionDetails.entity.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Entity } from "typeorm"; -import { Transaction } from "./transaction.entity"; - -@Entity({ name: "transactions" }) -export class TransactionDetails extends Transaction { - public get gasUsed(): string { - return this.transactionReceipt ? this.transactionReceipt.gasUsed : null; - } - - toJSON(): any { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { transactionReceipt, ...restFields } = super.toJSON(); - return { - ...restFields, - gasUsed: this.gasUsed, - }; - } -} diff --git a/packages/api/src/transaction/entities/transactionReceipt.entity.ts b/packages/api/src/transaction/entities/transactionReceipt.entity.ts index f896d80392..90a8a56e04 100644 --- a/packages/api/src/transaction/entities/transactionReceipt.entity.ts +++ b/packages/api/src/transaction/entities/transactionReceipt.entity.ts @@ -11,6 +11,9 @@ export class TransactionReceipt extends BaseEntity { @Column({ type: "bytea", transformer: normalizeAddressTransformer }) public readonly from: string; + @Column({ type: "bytea", transformer: hexTransformer, nullable: true }) + public readonly to?: string; + @Index() @Column({ type: "bytea", nullable: true, transformer: normalizeAddressTransformer }) public readonly contractAddress?: string; diff --git a/packages/api/src/transaction/transaction.controller.ts b/packages/api/src/transaction/transaction.controller.ts index 0a358422e3..8803e32f30 100644 --- a/packages/api/src/transaction/transaction.controller.ts +++ b/packages/api/src/transaction/transaction.controller.ts @@ -14,7 +14,6 @@ import { buildDateFilter } from "../common/utils"; import { FilterTransactionsOptionsDto } from "./dtos/filterTransactionsOptions.dto"; import { TransferDto } from "../transfer/transfer.dto"; import { TransactionDto } from "./dtos/transaction.dto"; -import { TransactionDetailsDto } from "./dtos/transactionDetails.dto"; import { TransferService } from "../transfer/transfer.service"; import { LogDto } from "../log/log.dto"; import { LogService } from "../log/log.service"; @@ -74,7 +73,7 @@ export class TransactionController { @ApiNotFoundResponse({ description: "Transaction with the specified hash does not exist" }) public async getTransaction( @Param("transactionHash", new ParseTransactionHashPipe()) transactionHash: string - ): Promise { + ): Promise { const transactionDetail = await this.transactionService.findOne(transactionHash); if (!transactionDetail) { throw new NotFoundException(); diff --git a/packages/api/src/transaction/transaction.module.ts b/packages/api/src/transaction/transaction.module.ts index b121a319bd..299313a968 100644 --- a/packages/api/src/transaction/transaction.module.ts +++ b/packages/api/src/transaction/transaction.module.ts @@ -4,7 +4,6 @@ import { TransactionController } from "./transaction.controller"; import { TransactionService } from "./transaction.service"; import { TransactionReceiptService } from "./transactionReceipt.service"; import { Transaction } from "./entities/transaction.entity"; -import { TransactionDetails } from "./entities/transactionDetails.entity"; import { AddressTransaction } from "./entities/addressTransaction.entity"; import { TransactionReceipt } from "./entities/transactionReceipt.entity"; import { Batch } from "../batch/batch.entity"; @@ -14,7 +13,7 @@ import { LogModule } from "../log/log.module"; @Module({ imports: [ - TypeOrmModule.forFeature([Transaction, TransactionDetails, AddressTransaction, TransactionReceipt, Batch]), + TypeOrmModule.forFeature([Transaction, AddressTransaction, TransactionReceipt, Batch]), TransferModule, LogModule, CounterModule, diff --git a/packages/api/src/transaction/transaction.service.spec.ts b/packages/api/src/transaction/transaction.service.spec.ts index b8c5e7e3cb..d63a3b6166 100644 --- a/packages/api/src/transaction/transaction.service.spec.ts +++ b/packages/api/src/transaction/transaction.service.spec.ts @@ -8,7 +8,6 @@ import { SortingOrder } from "../common/types"; import { CounterService } from "../counter/counter.service"; import { TransactionService, FilterTransactionsOptions } from "./transaction.service"; import { Transaction } from "./entities/transaction.entity"; -import { TransactionDetails } from "./entities/transactionDetails.entity"; import { AddressTransaction } from "./entities/addressTransaction.entity"; import { Batch } from "../batch/batch.entity"; @@ -18,7 +17,6 @@ describe("TransactionService", () => { let transaction; let service: TransactionService; let repositoryMock: typeorm.Repository; - let repositoryDetailMock: typeorm.Repository; let addressTransactionRepositoryMock: typeorm.Repository; let batchRepositoryMock: typeorm.Repository; let counterServiceMock: CounterService; @@ -27,7 +25,6 @@ describe("TransactionService", () => { beforeEach(async () => { counterServiceMock = mock(); repositoryMock = mock>(); - repositoryDetailMock = mock>(); addressTransactionRepositoryMock = mock>(); batchRepositoryMock = mock>(); transaction = { @@ -41,10 +38,6 @@ describe("TransactionService", () => { provide: getRepositoryToken(Transaction), useValue: repositoryMock, }, - { - provide: getRepositoryToken(TransactionDetails), - useValue: repositoryDetailMock, - }, { provide: getRepositoryToken(AddressTransaction), useValue: addressTransactionRepositoryMock, @@ -73,13 +66,13 @@ describe("TransactionService", () => { beforeEach(() => { queryBuilderMock = mock>(); - (repositoryDetailMock.createQueryBuilder as jest.Mock).mockReturnValue(queryBuilderMock); + (repositoryMock.createQueryBuilder as jest.Mock).mockReturnValue(queryBuilderMock); (queryBuilderMock.getOne as jest.Mock).mockResolvedValue(null); }); it("creates query builder with proper params", async () => { await service.findOne(hash); - expect(repositoryDetailMock.createQueryBuilder).toHaveBeenCalledWith("transaction"); + expect(repositoryMock.createQueryBuilder).toHaveBeenCalledWith("transaction"); }); it("filters transactions by the specified hash", async () => { @@ -99,7 +92,10 @@ describe("TransactionService", () => { it("selects only needed transactionReceipt fields", async () => { await service.findOne(hash); - expect(queryBuilderMock.addSelect).toHaveBeenCalledWith(["transactionReceipt.gasUsed"]); + expect(queryBuilderMock.addSelect).toHaveBeenCalledWith([ + "transactionReceipt.gasUsed", + "transactionReceipt.contractAddress", + ]); }); it("returns paginated result", async () => { @@ -172,6 +168,19 @@ describe("TransactionService", () => { expect(queryBuilderMock.where).toHaveBeenCalledWith(filterTransactionsOptions); }); + it("joins transactionReceipt record to get receipt specific fields", async () => { + await service.findAll(filterTransactionsOptions, pagingOptions); + expect(queryBuilderMock.leftJoin).toHaveBeenCalledWith("transaction.transactionReceipt", "transactionReceipt"); + }); + + it("selects only needed transactionReceipt fields", async () => { + await service.findAll(filterTransactionsOptions, pagingOptions); + expect(queryBuilderMock.addSelect).toHaveBeenCalledWith([ + "transactionReceipt.gasUsed", + "transactionReceipt.contractAddress", + ]); + }); + it("joins batch record to get batch specific fields", async () => { await service.findAll(filterTransactionsOptions, pagingOptions); expect(queryBuilderMock.leftJoin).toHaveBeenCalledWith("transaction.batch", "batch"); @@ -243,6 +252,22 @@ describe("TransactionService", () => { ); }); + it("joins transactionReceipt record to get receipt specific fields", async () => { + await service.findAll(filterTransactionsOptions, pagingOptions); + expect(addressTransactionsQueryBuilderMock.leftJoin).toHaveBeenCalledWith( + "transaction.transactionReceipt", + "transactionReceipt" + ); + }); + + it("selects only needed transactionReceipt fields", async () => { + await service.findAll(filterTransactionsOptions, pagingOptions); + expect(addressTransactionsQueryBuilderMock.addSelect).toHaveBeenCalledWith([ + "transactionReceipt.gasUsed", + "transactionReceipt.contractAddress", + ]); + }); + it("joins batch records", async () => { await service.findAll(filterTransactionsOptions, pagingOptions); expect(addressTransactionsQueryBuilderMock.leftJoinAndSelect).toBeCalledTimes(1); diff --git a/packages/api/src/transaction/transaction.service.ts b/packages/api/src/transaction/transaction.service.ts index a79376148f..a7ab5e61b8 100644 --- a/packages/api/src/transaction/transaction.service.ts +++ b/packages/api/src/transaction/transaction.service.ts @@ -5,7 +5,6 @@ import { Pagination } from "nestjs-typeorm-paginate"; import { paginate } from "../common/utils"; import { IPaginationOptions, CounterCriteria, SortingOrder } from "../common/types"; import { Transaction } from "./entities/transaction.entity"; -import { TransactionDetails } from "./entities/transactionDetails.entity"; import { AddressTransaction } from "./entities/addressTransaction.entity"; import { Batch } from "../batch/batch.entity"; import { CounterService } from "../counter/counter.service"; @@ -30,8 +29,6 @@ export class TransactionService { constructor( @InjectRepository(Transaction) private readonly transactionRepository: Repository, - @InjectRepository(TransactionDetails) - private readonly transactionDetailsRepository: Repository, @InjectRepository(AddressTransaction) private readonly addressTransactionRepository: Repository, @InjectRepository(Batch) @@ -39,11 +36,11 @@ export class TransactionService { private readonly counterService: CounterService ) {} - public async findOne(hash: string): Promise { - const queryBuilder = this.transactionDetailsRepository.createQueryBuilder("transaction"); + public async findOne(hash: string): Promise { + const queryBuilder = this.transactionRepository.createQueryBuilder("transaction"); queryBuilder.leftJoinAndSelect("transaction.batch", "batch"); queryBuilder.leftJoin("transaction.transactionReceipt", "transactionReceipt"); - queryBuilder.addSelect(["transactionReceipt.gasUsed"]); + queryBuilder.addSelect(["transactionReceipt.gasUsed", "transactionReceipt.contractAddress"]); queryBuilder.where({ hash }); return await queryBuilder.getOne(); } @@ -60,6 +57,8 @@ export class TransactionService { const queryBuilder = this.addressTransactionRepository.createQueryBuilder("addressTransaction"); queryBuilder.select("addressTransaction.number"); queryBuilder.leftJoinAndSelect("addressTransaction.transaction", "transaction"); + queryBuilder.leftJoin("transaction.transactionReceipt", "transactionReceipt"); + queryBuilder.addSelect(["transactionReceipt.gasUsed", "transactionReceipt.contractAddress"]); queryBuilder.leftJoin("transaction.batch", "batch"); queryBuilder.addSelect(["batch.commitTxHash", "batch.executeTxHash", "batch.proveTxHash"]); queryBuilder.where({ @@ -87,6 +86,8 @@ export class TransactionService { }; } else { const queryBuilder = this.transactionRepository.createQueryBuilder("transaction"); + queryBuilder.leftJoin("transaction.transactionReceipt", "transactionReceipt"); + queryBuilder.addSelect(["transactionReceipt.gasUsed", "transactionReceipt.contractAddress"]); queryBuilder.leftJoin("transaction.batch", "batch"); queryBuilder.addSelect(["batch.commitTxHash", "batch.executeTxHash", "batch.proveTxHash"]); queryBuilder.where(filterOptions); diff --git a/packages/api/test/transaction.e2e-spec.ts b/packages/api/test/transaction.e2e-spec.ts index 6b7f225638..0fb7aa4dd9 100644 --- a/packages/api/test/transaction.e2e-spec.ts +++ b/packages/api/test/transaction.e2e-spec.ts @@ -264,11 +264,11 @@ describe("TransactionController (e2e)", () => { from: "0xc7e0220d02d549c4846A6EC31D89C3B670Ebe35C", gasLimit: "2009", gasPrice: "1009", + gasUsed: "7009", gasPerPubdata: "5009", maxFeePerGas: "3009", maxPriorityFeePerGas: "4009", hash: "0x8a008b8dbbc18035e56370abb820e736b705d68d6ac12b203603db8d9ea87e19", - isEvmLike: null, contractAddress: null, isL1BatchSealed: false, isL1Originated: true, @@ -294,11 +294,11 @@ describe("TransactionController (e2e)", () => { from: "0xc7e0220d02d549c4846A6EC31D89C3B670Ebe35C", gasLimit: "2008", gasPrice: "1008", + gasUsed: "7008", gasPerPubdata: "5008", maxFeePerGas: "3008", maxPriorityFeePerGas: "4008", hash: "0x8a008b8dbbc18035e56370abb820e736b705d68d6ac12b203603db8d9ea87e18", - isEvmLike: null, contractAddress: null, isL1BatchSealed: true, isL1Originated: true, @@ -324,11 +324,11 @@ describe("TransactionController (e2e)", () => { from: "0xc7e0220d02d549c4846A6EC31D89C3B670Ebe35C", gasLimit: "2007", gasPrice: "1007", + gasUsed: "7007", gasPerPubdata: "5007", maxFeePerGas: "3007", maxPriorityFeePerGas: "4007", hash: "0x8a008b8dbbc18035e56370abb820e736b705d68d6ac12b203603db8d9ea87e17", - isEvmLike: null, contractAddress: null, isL1BatchSealed: true, isL1Originated: true, @@ -354,11 +354,11 @@ describe("TransactionController (e2e)", () => { from: "0xc7e0220d02d549c4846A6EC31D89C3B670Ebe35C", gasLimit: "2006", gasPrice: "1006", + gasUsed: "7006", gasPerPubdata: "5006", maxFeePerGas: "3006", maxPriorityFeePerGas: "4006", hash: "0x8a008b8dbbc18035e56370abb820e736b705d68d6ac12b203603db8d9ea87e16", - isEvmLike: null, contractAddress: null, isL1BatchSealed: true, isL1Originated: true, @@ -384,11 +384,11 @@ describe("TransactionController (e2e)", () => { from: "0xc7e0220d02d549c4846A6EC31D89C3B670Ebe35C", gasLimit: "2005", gasPrice: "1005", + gasUsed: "7005", gasPerPubdata: "5005", maxFeePerGas: "3005", maxPriorityFeePerGas: "4005", hash: "0x8a008b8dbbc18035e56370abb820e736b705d68d6ac12b203603db8d9ea87e15", - isEvmLike: null, contractAddress: null, isL1BatchSealed: true, isL1Originated: true, @@ -413,12 +413,12 @@ describe("TransactionController (e2e)", () => { fee: "0x2386f26fc10000", from: "0xc7e0220d02d549c4846A6EC31D89C3B670Ebe35C", gasPrice: "1004", + gasUsed: "7004", gasLimit: "2004", gasPerPubdata: "5004", maxFeePerGas: "3004", maxPriorityFeePerGas: "4004", hash: "0x8a008b8dbbc18035e56370abb820e736b705d68d6ac12b203603db8d9ea87e14", - isEvmLike: null, contractAddress: null, isL1BatchSealed: true, isL1Originated: true, @@ -444,11 +444,11 @@ describe("TransactionController (e2e)", () => { from: "0xc7e0220d02d549c4846A6EC31D89C3B670Ebe35C", gasLimit: "2003", gasPrice: "1003", + gasUsed: "7003", gasPerPubdata: "5003", maxFeePerGas: "3003", maxPriorityFeePerGas: "4003", hash: "0x8a008b8dbbc18035e56370abb820e736b705d68d6ac12b203603db8d9ea87e13", - isEvmLike: null, contractAddress: null, isL1BatchSealed: true, isL1Originated: true, @@ -473,12 +473,12 @@ describe("TransactionController (e2e)", () => { fee: "0x2386f26fc10000", from: "0xc7e0220d02d549c4846A6EC31D89C3B670Ebe35C", gasPrice: "1002", + gasUsed: "7002", gasLimit: "2002", gasPerPubdata: "5002", maxFeePerGas: "3002", maxPriorityFeePerGas: "4002", hash: "0x8a008b8dbbc18035e56370abb820e736b705d68d6ac12b203603db8d9ea87e12", - isEvmLike: null, contractAddress: null, isL1BatchSealed: false, isL1Originated: true, @@ -503,12 +503,12 @@ describe("TransactionController (e2e)", () => { fee: "0x2386f26fc10000", from: "0xc7e0220d02d549c4846A6EC31D89C3B670Ebe35C", gasPrice: "1001", + gasUsed: "7001", gasLimit: "2001", gasPerPubdata: "5001", maxFeePerGas: "3001", maxPriorityFeePerGas: "4001", hash: "0x8a008b8dbbc18035e56370abb820e736b705d68d6ac12b203603db8d9ea87e11", - isEvmLike: null, contractAddress: null, isL1BatchSealed: false, isL1Originated: true, @@ -534,11 +534,11 @@ describe("TransactionController (e2e)", () => { from: "0xc7e0220d02d549c4846A6EC31D89C3B670Ebe35C", gasLimit: "2000", gasPrice: "1000", + gasUsed: "7000", gasPerPubdata: "5000", maxFeePerGas: "3000", maxPriorityFeePerGas: "4000", hash: "0x8a008b8dbbc18035e56370abb820e736b705d68d6ac12b203603db8d9ea87e10", - isEvmLike: null, contractAddress: null, isL1BatchSealed: false, isL1Originated: true, @@ -574,11 +574,11 @@ describe("TransactionController (e2e)", () => { from: "0xc7e0220d02d549c4846A6EC31D89C3B670Ebe35C", gasLimit: "2008", gasPrice: "1008", + gasUsed: "7008", gasPerPubdata: "5008", maxFeePerGas: "3008", maxPriorityFeePerGas: "4008", hash: "0x8a008b8dbbc18035e56370abb820e736b705d68d6ac12b203603db8d9ea87e18", - isEvmLike: null, contractAddress: null, isL1BatchSealed: true, isL1Originated: true, @@ -604,11 +604,11 @@ describe("TransactionController (e2e)", () => { from: "0xc7e0220d02d549c4846A6EC31D89C3B670Ebe35C", gasLimit: "2007", gasPrice: "1007", + gasUsed: "7007", gasPerPubdata: "5007", maxFeePerGas: "3007", maxPriorityFeePerGas: "4007", hash: "0x8a008b8dbbc18035e56370abb820e736b705d68d6ac12b203603db8d9ea87e17", - isEvmLike: null, contractAddress: null, isL1BatchSealed: true, isL1Originated: true, @@ -634,11 +634,11 @@ describe("TransactionController (e2e)", () => { from: "0xc7e0220d02d549c4846A6EC31D89C3B670Ebe35C", gasLimit: "2006", gasPrice: "1006", + gasUsed: "7006", gasPerPubdata: "5006", maxFeePerGas: "3006", maxPriorityFeePerGas: "4006", hash: "0x8a008b8dbbc18035e56370abb820e736b705d68d6ac12b203603db8d9ea87e16", - isEvmLike: null, contractAddress: null, isL1BatchSealed: true, isL1Originated: true, @@ -720,11 +720,11 @@ describe("TransactionController (e2e)", () => { from: "0xc7e0220d02d549c4846A6EC31D89C3B670Ebe35C", gasLimit: "2001", gasPrice: "1001", + gasUsed: "7001", gasPerPubdata: "5001", maxFeePerGas: "3001", maxPriorityFeePerGas: "4001", hash: "0x8a008b8dbbc18035e56370abb820e736b705d68d6ac12b203603db8d9ea87e11", - isEvmLike: null, contractAddress: null, isL1BatchSealed: false, isL1Originated: true, @@ -775,11 +775,11 @@ describe("TransactionController (e2e)", () => { from: "0xc7e0220d02d549c4846A6EC31D89C3B670Ebe35C", gasLimit: "2001", gasPrice: "1001", + gasUsed: "7001", gasPerPubdata: "5001", maxFeePerGas: "3001", maxPriorityFeePerGas: "4001", hash: "0x8a008b8dbbc18035e56370abb820e736b705d68d6ac12b203603db8d9ea87e11", - isEvmLike: null, contractAddress: null, isL1BatchSealed: false, isL1Originated: true, @@ -830,11 +830,11 @@ describe("TransactionController (e2e)", () => { from: "0xc7e0220d02d549c4846A6EC31D89C3B670Ebe35C", gasLimit: "2007", gasPrice: "1007", + gasUsed: "7007", gasPerPubdata: "5007", maxFeePerGas: "3007", maxPriorityFeePerGas: "4007", hash: "0x8a008b8dbbc18035e56370abb820e736b705d68d6ac12b203603db8d9ea87e17", - isEvmLike: null, contractAddress: null, isL1BatchSealed: true, isL1Originated: true, @@ -860,11 +860,11 @@ describe("TransactionController (e2e)", () => { from: "0xc7e0220d02d549c4846A6EC31D89C3B670Ebe35C", gasLimit: "2006", gasPrice: "1006", + gasUsed: "7006", gasPerPubdata: "5006", maxFeePerGas: "3006", maxPriorityFeePerGas: "4006", hash: "0x8a008b8dbbc18035e56370abb820e736b705d68d6ac12b203603db8d9ea87e16", - isEvmLike: null, contractAddress: null, isL1BatchSealed: true, isL1Originated: true, @@ -956,7 +956,6 @@ describe("TransactionController (e2e)", () => { maxFeePerGas: "3008", maxPriorityFeePerGas: "4008", hash: "0x8a008b8dbbc18035e56370abb820e736b705d68d6ac12b203603db8d9ea87e18", - isEvmLike: null, contractAddress: null, isL1BatchSealed: true, isL1Originated: true, @@ -995,7 +994,6 @@ describe("TransactionController (e2e)", () => { maxFeePerGas: "3005", maxPriorityFeePerGas: "4005", hash: "0x8a008b8dbbc18035e56370abb820e736b705d68d6ac12b203603db8d9ea87e15", - isEvmLike: null, contractAddress: null, isL1BatchSealed: true, isL1Originated: true, @@ -1034,7 +1032,6 @@ describe("TransactionController (e2e)", () => { maxFeePerGas: "3003", maxPriorityFeePerGas: "4003", hash: "0x8a008b8dbbc18035e56370abb820e736b705d68d6ac12b203603db8d9ea87e13", - isEvmLike: null, contractAddress: null, isL1BatchSealed: true, isL1Originated: true, @@ -1073,7 +1070,6 @@ describe("TransactionController (e2e)", () => { maxFeePerGas: "3000", maxPriorityFeePerGas: "4000", hash: "0x8a008b8dbbc18035e56370abb820e736b705d68d6ac12b203603db8d9ea87e10", - isEvmLike: null, contractAddress: null, isL1BatchSealed: true, isL1Originated: true, @@ -1112,7 +1108,6 @@ describe("TransactionController (e2e)", () => { maxFeePerGas: "3009", maxPriorityFeePerGas: "4009", hash: "0x8a008b8dbbc18035e56370abb820e736b705d68d6ac12b203603db8d9ea87e19", - isEvmLike: null, contractAddress: null, isL1BatchSealed: true, isL1Originated: true, @@ -1151,7 +1146,6 @@ describe("TransactionController (e2e)", () => { maxFeePerGas: "3000", maxPriorityFeePerGas: "4000", hash: "0x8a008b8dbbc18035e56370abb820e736b705d68d6ac12b203603db8d9ea87e10", - isEvmLike: null, contractAddress: null, isL1BatchSealed: true, isL1Originated: true, diff --git a/packages/app/mock/transactions/Execute.json b/packages/app/mock/transactions/Execute.json index 8799ac49c2..d9f6554996 100644 --- a/packages/app/mock/transactions/Execute.json +++ b/packages/app/mock/transactions/Execute.json @@ -171,5 +171,7 @@ "gasUsed": "3000", "gasPerPubdata": "800", "maxFeePerGas": "7000", - "maxPriorityFeePerGas": "8000" + "maxPriorityFeePerGas": "8000", + "contractAddress": null, + "isEvmLike": false } diff --git a/packages/app/src/components/transactions/Table.vue b/packages/app/src/components/transactions/Table.vue index f7814219d9..aedbdf4ca9 100644 --- a/packages/app/src/components/transactions/Table.vue +++ b/packages/app/src/components/transactions/Table.vue @@ -111,8 +111,17 @@ - - {{ shortenFitText(item.to, "left", 125) }} + + {{ + item.isContractDeploymentTx + ? t("contract.contractCreated") + : shortenFitText(item.displayedTxReceiver, "left", 125) + }} @@ -133,12 +142,17 @@ - {{ shortenFitText(item.to, "left", 125) }} + {{ + item.isContractDeploymentTx + ? t("contract.contractCreated") + : shortenFitText(item.displayedTxReceiver, "left", 125) + }} @@ -225,7 +239,7 @@ import type { Direction } from "@/components/transactions/TransactionDirectionTa import type { AbiFragment } from "@/composables/useAddress"; import type { NetworkOrigin } from "@/types"; -import { utcStringFromISOString } from "@/utils/helpers"; +import { isContractDeployerAddress, utcStringFromISOString } from "@/utils/helpers"; const { currentNetwork } = useContext(); @@ -325,17 +339,24 @@ type TransactionListItemMapped = TransactionListItem & { toNetwork: NetworkOrigin; statusIcon: unknown; statusColor: "danger" | "dark-success"; + isContractDeploymentTx: boolean; + displayedTxReceiver: string | null; }; const transactions = computed(() => { - return data.value?.map((transaction) => ({ - ...transaction, - methodName: getTransactionMethod(transaction, methodNames.value), - fromNetwork: transaction.isL1Originated ? "L1" : "L2", - toNetwork: "L2", // even withdrawals go through L2 addresses (800A or bridge addresses) - statusColor: transaction.status === "failed" ? "danger" : "dark-success", - statusIcon: ["failed", "included"].includes(transaction.status) ? ZkSyncIcon : EthereumIcon, - })); + return data.value?.map((transaction) => { + const isContractDeploymentTx = isContractDeployerAddress(transaction.to) && !!transaction.contractAddress; + return { + ...transaction, + methodName: getTransactionMethod(transaction, methodNames.value), + fromNetwork: transaction.isL1Originated ? "L1" : "L2", + toNetwork: "L2", // even withdrawals go through L2 addresses (800A or bridge addresses) + statusColor: transaction.status === "failed" ? "danger" : "dark-success", + statusIcon: ["failed", "included"].includes(transaction.status) ? ZkSyncIcon : EthereumIcon, + isContractDeploymentTx, + displayedTxReceiver: isContractDeploymentTx ? transaction.contractAddress : transaction.to, + }; + }); }); const isHighRowsSize = computed(() => props.columns.includes("fee")); diff --git a/packages/app/src/components/transactions/infoTable/GeneralInfo.vue b/packages/app/src/components/transactions/infoTable/GeneralInfo.vue index f7a3acbb06..7acc57fa46 100644 --- a/packages/app/src/components/transactions/infoTable/GeneralInfo.vue +++ b/packages/app/src/components/transactions/infoTable/GeneralInfo.vue @@ -112,14 +112,14 @@ -
+
- -

Created

+ +

{{ t("contract.created") }}

- +
@@ -237,8 +237,6 @@ import { computed, type PropType } from "vue"; import { useI18n } from "vue-i18n"; -import { computedAsync } from "@vueuse/core"; - import AddressLink from "@/components/AddressLink.vue"; import FeeData from "@/components/FeeData.vue"; import Badge from "@/components/common/Badge.vue"; @@ -255,11 +253,10 @@ import TransactionStatus from "@/components/transactions/Status.vue"; import TransactionData from "@/components/transactions/infoTable/TransactionData.vue"; import TransferTableCell from "@/components/transactions/infoTable/TransferTableCell.vue"; -import useAddress from "@/composables/useAddress"; - -import type { Contract } from "@/composables/useAddress"; import type { TransactionItem } from "@/composables/useTransaction"; +import { isContractDeployerAddress } from "@/utils/helpers"; + const { t } = useI18n(); const props = defineProps({ @@ -276,7 +273,13 @@ const props = defineProps({ }, }); -const { getByAddress, item } = useAddress(); +const isContractDeploymentTx = computed(() => { + return isContractDeployerAddress(props.transaction?.to) && !!props.transaction?.contractAddress; +}); + +const displayedTxReceiver = computed(() => { + return isContractDeploymentTx.value ? props.transaction?.contractAddress : props.transaction?.to; +}); const tokenTransfers = computed(() => { // exclude transfers with no amount, such as NFT until we fully support them @@ -291,15 +294,6 @@ const gasUsedPercent = computed(() => { } return null; }); - -const isEvmLike = computedAsync(async () => { - if (!props.transaction?.data || !props.transaction?.to) { - return false; - } - await getByAddress(props.transaction?.to); - - return !!(item.value as Contract)?.isEvmLike; -});