Skip to content

Commit

Permalink
Merge branch 'main' into show-gas-fields-on-ui
Browse files Browse the repository at this point in the history
  • Loading branch information
vasyl-ivanchuk committed Dec 6, 2023
2 parents dfe087f + 65d0a8d commit 7a66571
Show file tree
Hide file tree
Showing 48 changed files with 808 additions and 146 deletions.
24 changes: 13 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ flowchart
## 🛠 Installation

```bash
$ npm install
npm install
```

## ⚙️ Setting up env variables
Expand All @@ -63,7 +63,7 @@ Make sure you have [zksync-era](https://github.com/matter-labs/zksync-era) repo

The following script sets `.env` files for [Worker](./packages/worker) and [API](./packages/api) packages as well as environment configuration file for [App](./packages/app) package based on your local [zksync-era](https://github.com/matter-labs/zksync-era) repo setup.
```bash
$ npm run hyperchain:configure
npm run hyperchain:configure
```
You can review and edit generated files if you need to change any settings.

Expand All @@ -72,18 +72,18 @@ You can review and edit generated files if you need to change any settings.
Before running the solution, make sure you have a database server up and running, you have created a database and set up all the required environment variables.
To create a database run the following command:
```bash
$ npm run db:create
npm run db:create
```

To run all the packages (`Worker`, `API` and front-end `App`) in `development` mode run the following command from the root directory.
```bash
$ npm run dev
npm run dev
```

For `production` mode run:
```bash
$ npm run build
$ npm run start
npm run build
npm run start
```

Each component can also be started individually. Follow individual packages `README` for details.
Expand All @@ -105,15 +105,15 @@ To verify front-end `App` is running open http://localhost:3010 in your browser.
## 🕵️‍♂️ Testing
Run unit tests for all packages:
```bash
$ npm run test
npm run test
```
Run e2e tests for all packages:
```bash
$ npm run test:e2e
npm run test:e2e
```
Run tests for a specific package:
```bash
$ npm run test -w {package}
npm run test -w {package}
```
For more details on testing please check individual packages `README`.

Expand All @@ -129,7 +129,9 @@ zkSync Era Block Explorer is distributed under the terms of either
at your option.

## 🔗 Production links
- Testnet API: https://block-explorer-api.testnets.zksync.dev
- Testnet Goerli API: https://block-explorer-api.testnets.zksync.dev
- Testnet Sepolia API: https://block-explorer-api.sepolia.zksync.dev
- Mainnet API: https://block-explorer-api.mainnet.zksync.io
- Testnet App: https://goerli.explorer.zksync.io
- Testnet Goerli App: https://goerli.explorer.zksync.io
- Testnet Sepolia App: https://sepolia.explorer.zksync.io
- Mainnet App: https://explorer.zksync.io
4 changes: 2 additions & 2 deletions packages/api/src/api/account/account.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { mock } from "jest-mock-extended";
import { BadRequestException, Logger } from "@nestjs/common";
import { L2_ETH_TOKEN_ADDRESS } from "../../common/constants";
import { BlockService } from "../../block/block.service";
import { BlockDetail } from "../../block/blockDetail.entity";
import { BlockDetails } from "../../block/blockDetails.entity";
import { TransactionService } from "../../transaction/transaction.service";
import { BalanceService } from "../../balance/balance.service";
import { TransactionStatus } from "../../transaction/entities/transaction.entity";
Expand Down Expand Up @@ -629,7 +629,7 @@ describe("AccountController", () => {
it("returns blocks list response when block by miner are found", async () => {
jest
.spyOn(blockServiceMock, "findMany")
.mockResolvedValue([{ number: 1, timestamp: new Date("2023-03-03") } as BlockDetail]);
.mockResolvedValue([{ number: 1, timestamp: new Date("2023-03-03") } as BlockDetails]);
const response = await controller.getAccountMinedBlocks(address, {
page: 1,
offset: 10,
Expand Down
57 changes: 54 additions & 3 deletions packages/api/src/api/transaction/transaction.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ 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, Transaction } from "../../transaction/entities/transaction.entity";
import { TransactionStatus } from "../../transaction/entities/transaction.entity";
import { TransactionDetails } from "../../transaction/entities/transactionDetails.entity";
import { TransactionReceipt } from "../../transaction/entities/transactionReceipt.entity";
import { ResponseStatus, ResponseMessage } from "../dtos/common/responseBase.dto";
import { TransactionController } from "./transaction.controller";
Expand Down Expand Up @@ -56,7 +57,7 @@ describe("TransactionController", () => {
it("returns isError as 0 when transaction is successful", async () => {
jest
.spyOn(transactionServiceMock, "findOne")
.mockResolvedValue({ status: TransactionStatus.Included } as Transaction);
.mockResolvedValue({ status: TransactionStatus.Included } as TransactionDetails);

const response = await controller.getTransactionStatus(transactionHash);
expect(response).toEqual({
Expand All @@ -72,7 +73,57 @@ describe("TransactionController", () => {
it("returns isError as 1 when transaction is failed", async () => {
jest
.spyOn(transactionServiceMock, "findOne")
.mockResolvedValue({ status: TransactionStatus.Failed } as Transaction);
.mockResolvedValue({ status: TransactionStatus.Failed } as TransactionDetails);

const response = await controller.getTransactionStatus(transactionHash);
expect(response).toEqual({
status: ResponseStatus.OK,
message: ResponseMessage.OK,
result: {
isError: "1",
errDescription: "",
},
});
});

it("returns transaction error in errDescription when transaction is failed and transaction error is present", async () => {
jest.spyOn(transactionServiceMock, "findOne").mockResolvedValue({
status: TransactionStatus.Failed,
error: "Error",
revertReason: "Reverted",
} as TransactionDetails);

const response = await controller.getTransactionStatus(transactionHash);
expect(response).toEqual({
status: ResponseStatus.OK,
message: ResponseMessage.OK,
result: {
isError: "1",
errDescription: "Error",
},
});
});

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);

const response = await controller.getTransactionStatus(transactionHash);
expect(response).toEqual({
status: ResponseStatus.OK,
message: ResponseMessage.OK,
result: {
isError: "1",
errDescription: "Reverted",
},
});
});

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);

const response = await controller.getTransactionStatus(transactionHash);
expect(response).toEqual({
Expand Down
2 changes: 1 addition & 1 deletion packages/api/src/api/transaction/transaction.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export class TransactionController {
message: ResponseMessage.OK,
result: {
isError: hasError ? ResponseStatus.OK : ResponseStatus.NOTOK,
errDescription: "",
errDescription: transaction?.error || transaction?.revertReason || "",
},
};
}
Expand Down
6 changes: 3 additions & 3 deletions packages/api/src/block/block.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { PagingOptionsDto, ListFiltersDto } from "../common/dtos";
import { ApiListPageOkResponse } from "../common/decorators/apiListPageOkResponse";
import { BlockService } from "./block.service";
import { BlockDto } from "./block.dto";
import { BlockDetailDto } from "./blockDetail.dto";
import { BlockDetailsDto } from "./blockDetails.dto";
import { swagger } from "../config/featureFlags";

const entityName = "blocks";
Expand Down Expand Up @@ -48,12 +48,12 @@ export class BlockController {
example: "1",
description: "Block number",
})
@ApiOkResponse({ description: "Block was returned successfully", type: BlockDetailDto })
@ApiOkResponse({ description: "Block was returned successfully", type: BlockDetailsDto })
@ApiBadRequestResponse({ description: "Block number is invalid" })
@ApiNotFoundResponse({ description: "Block with the specified number does not exist" })
public async getBlock(
@Param("blockNumber", new ParseLimitedIntPipe({ min: 0 })) blockNumber: number
): Promise<BlockDetailDto> {
): Promise<BlockDetailsDto> {
const block = await this.blockService.findOne(blockNumber);
if (!block) {
throw new NotFoundException();
Expand Down
4 changes: 2 additions & 2 deletions packages/api/src/block/block.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import { TypeOrmModule } from "@nestjs/typeorm";
import { BlockService } from "../block/block.service";
import { BlockController } from "./block.controller";
import { Block } from "./block.entity";
import { BlockDetail } from "./blockDetail.entity";
import { BlockDetails } from "./blockDetails.entity";

@Module({
imports: [TypeOrmModule.forFeature([Block, BlockDetail])],
imports: [TypeOrmModule.forFeature([Block, BlockDetails])],
controllers: [BlockController],
providers: [BlockService],
exports: [BlockService],
Expand Down
10 changes: 5 additions & 5 deletions packages/api/src/block/block.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,19 @@ import { Pagination, IPaginationMeta } from "nestjs-typeorm-paginate";
import * as utils from "../common/utils";
import { BlockService, FindManyOptions } from "./block.service";
import { Block } from "./block.entity";
import { BlockDetail } from "./blockDetail.entity";
import { BlockDetails } from "./blockDetails.entity";

jest.mock("../common/utils");

describe("BlockService", () => {
let blockRecord;
let service: BlockService;
let repositoryMock: Repository<Block>;
let blockDetailRepositoryMock: Repository<BlockDetail>;
let blockDetailRepositoryMock: Repository<BlockDetails>;

beforeEach(async () => {
repositoryMock = mock<Repository<Block>>();
blockDetailRepositoryMock = mock<Repository<BlockDetail>>();
blockDetailRepositoryMock = mock<Repository<BlockDetails>>();

blockRecord = {
number: 123,
Expand All @@ -32,7 +32,7 @@ describe("BlockService", () => {
useValue: repositoryMock,
},
{
provide: getRepositoryToken(BlockDetail),
provide: getRepositoryToken(BlockDetails),
useValue: blockDetailRepositoryMock,
},
],
Expand Down Expand Up @@ -305,7 +305,7 @@ describe("BlockService", () => {
let filterOptions: FindManyOptions;

beforeEach(() => {
queryBuilderMock = mock<SelectQueryBuilder<BlockDetail>>({
queryBuilderMock = mock<SelectQueryBuilder<BlockDetails>>({
getMany: jest.fn().mockResolvedValue([
{
number: 1,
Expand Down
16 changes: 8 additions & 8 deletions packages/api/src/block/block.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,22 @@ import { Pagination } from "nestjs-typeorm-paginate";
import { paginate } from "../common/utils";
import { IPaginationOptions } from "../common/types";
import { Block } from "./block.entity";
import { BlockDetail } from "./blockDetail.entity";
import { BlockDetails } from "./blockDetails.entity";

export interface FindManyOptions {
miner?: string;
page?: number;
offset?: number;
selectFields?: (keyof BlockDetail)[];
selectFields?: (keyof BlockDetails)[];
}

@Injectable()
export class BlockService {
public constructor(
@InjectRepository(Block)
private readonly blocksRepository: Repository<Block>,
@InjectRepository(BlockDetail)
private readonly blockDetailsRepository: Repository<BlockDetail>
@InjectRepository(BlockDetails)
private readonly blockDetailsRepository: Repository<BlockDetails>
) {}

private getBlock(filterOptions: FindOptionsWhere<Block>, orderOptions: FindOptionsOrder<Block>): Promise<Block> {
Expand Down Expand Up @@ -50,9 +50,9 @@ export class BlockService {

public async findOne(
number: number,
selectFields?: (keyof BlockDetail)[],
relations: FindOptionsRelations<BlockDetail> = { batch: true }
): Promise<BlockDetail> {
selectFields?: (keyof BlockDetails)[],
relations: FindOptionsRelations<BlockDetails> = { batch: true }
): Promise<BlockDetails> {
return await this.blockDetailsRepository.findOne({
where: { number },
relations: relations,
Expand Down Expand Up @@ -90,7 +90,7 @@ export class BlockService {
return await paginate<Block>(queryBuilder, paginationOptions, () => this.count(filterOptions));
}

public async findMany({ miner, page = 1, offset = 10, selectFields }: FindManyOptions): Promise<BlockDetail[]> {
public async findMany({ miner, page = 1, offset = 10, selectFields }: FindManyOptions): Promise<BlockDetails[]> {
const queryBuilder = this.blockDetailsRepository.createQueryBuilder("block");
queryBuilder.addSelect(selectFields);
if (miner) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ApiProperty } from "@nestjs/swagger";
import { BlockDto } from "./block.dto";

export class BlockDetailDto extends BlockDto {
export class BlockDetailsDto extends BlockDto {
@ApiProperty({
type: String,
description: "The hash of the previous block",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Block } from "./block.entity";
import { hexTransformer } from "../common/transformers/hex.transformer";

@Entity({ name: "blocks" })
export class BlockDetail extends Block {
export class BlockDetails extends Block {
@Column({ type: "bytea", transformer: hexTransformer, nullable: true })
public readonly parentHash?: string;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { hexToDecimalNumberTransformer } from "./hexToDecimalNumber.transformer";

describe("hexToDecimalNumberTransformer", () => {
describe("to", () => {
it("returns null for null input", () => {
const result = hexToDecimalNumberTransformer.to(null);
expect(result).toBeNull();
});

it("returns hex representation of the decimal number string", () => {
const result = hexToDecimalNumberTransformer.to("800");
expect(result).toBe("0x0320");
});
});

describe("from", () => {
it("returns null for null input", () => {
const result = hexToDecimalNumberTransformer.from(null);
expect(result).toBeNull();
});

it("returns decimal representation of the hex number string", () => {
const result = hexToDecimalNumberTransformer.from("0x320");
expect(result).toBe("800");
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { BigNumber } from "ethers";
import { ValueTransformer } from "typeorm";

export const hexToDecimalNumberTransformer: ValueTransformer = {
to(decimalNumberStr: string | null): string | null {
if (!decimalNumberStr) {
return null;
}
return BigNumber.from(decimalNumberStr).toHexString();
},
from(hexNumberStr: string | null): string | null {
if (!hexNumberStr) {
return null;
}
return BigNumber.from(hexNumberStr).toString();
},
};
Loading

0 comments on commit 7a66571

Please sign in to comment.