Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: initial changes to show base token - SQUASHED #260

Closed
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
7 changes: 7 additions & 0 deletions packages/api/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,10 @@ DISABLE_EXTERNAL_API=false
DATABASE_STATEMENT_TIMEOUT_MS=90000
CONTRACT_VERIFICATION_API_URL=http://127.0.0.1:3070
NETWORK_NAME=testnet-sepolia
BASE_TOKEN_SYMBOL=ETH
BASE_TOKEN_DECIMALS=18
BASE_TOKEN_L1_ADDRESS=0x8E9C82509488eD471A83824d20Dd474b8F534a0b
BASE_TOKEN_ICON_URL=https://assets.coingecko.com/coins/images/279/large/ethereum.png?1698873266
BASE_TOKEN_NAME=Ether
BASE_TOKEN_LIQUIDITY=220000000000
BASE_TOKEN_USDPRICE=1800
6 changes: 3 additions & 3 deletions packages/api/src/api/account/account.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Test } from "@nestjs/testing";
import { mock } from "jest-mock-extended";
import { BadRequestException, Logger } from "@nestjs/common";
import { L2_ETH_TOKEN_ADDRESS } from "../../common/constants";
import { BASE_TOKEN_L2_ADDRESS } from "../../common/constants";
import { BlockService } from "../../block/block.service";
import { BlockDetails } from "../../block/blockDetails.entity";
import { TransactionService } from "../../transaction/transaction.service";
Expand Down Expand Up @@ -557,7 +557,7 @@ describe("AccountController", () => {
describe("getAccountEtherBalance", () => {
it("calls balanceService.getBalance and returns account ether balance", async () => {
const response = await controller.getAccountEtherBalance(address);
expect(balanceServiceMock.getBalance).toBeCalledWith(address, L2_ETH_TOKEN_ADDRESS);
expect(balanceServiceMock.getBalance).toBeCalledWith(address, BASE_TOKEN_L2_ADDRESS);
expect(response).toEqual({
status: ResponseStatus.OK,
message: ResponseMessage.OK,
Expand Down Expand Up @@ -588,7 +588,7 @@ describe("AccountController", () => {

it("calls balanceService.getBalancesByAddresses and returns accounts ether balances", async () => {
const response = await controller.getAccountsEtherBalances([address, "address2"]);
expect(balanceServiceMock.getBalancesByAddresses).toBeCalledWith([address, "address2"], L2_ETH_TOKEN_ADDRESS);
expect(balanceServiceMock.getBalancesByAddresses).toBeCalledWith([address, "address2"], BASE_TOKEN_L2_ADDRESS);
expect(response).toEqual({
status: ResponseStatus.OK,
message: ResponseMessage.OK,
Expand Down
6 changes: 3 additions & 3 deletions packages/api/src/api/account/account.controller.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Controller, Get, Query, Logger, UseFilters, ParseArrayPipe, BadRequestException } from "@nestjs/common";
import { ApiTags, ApiExcludeController } from "@nestjs/swagger";
import { L2_ETH_TOKEN_ADDRESS } from "../../common/constants";
import { BASE_TOKEN_L2_ADDRESS } from "../../common/constants";
import { TokenType } from "../../token/token.entity";
import { dateToTimestamp } from "../../common/utils";
import { BlockService } from "../../block/block.service";
Expand Down Expand Up @@ -176,7 +176,7 @@ export class AccountController {
public async getAccountEtherBalance(
@Query("address", new ParseAddressPipe()) address: string
): Promise<AccountEtherBalanceResponseDto> {
const balance = await this.balanceService.getBalance(address, L2_ETH_TOKEN_ADDRESS);
const balance = await this.balanceService.getBalance(address, BASE_TOKEN_L2_ADDRESS);
return {
status: ResponseStatus.OK,
message: ResponseMessage.OK,
Expand All @@ -201,7 +201,7 @@ export class AccountController {
if (uniqueAddresses.length > 20) {
throw new BadRequestException("Maximum 20 addresses per request");
}
const balances = await this.balanceService.getBalancesByAddresses(addresses, L2_ETH_TOKEN_ADDRESS);
const balances = await this.balanceService.getBalancesByAddresses(addresses, BASE_TOKEN_L2_ADDRESS);
const result = addresses.map((address) => ({
account: address,
balance: balances.find((balance) => balance.address.toLowerCase() === address.toLowerCase())?.balance || "0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Transfer } from "../../transfer/transfer.entity";
import { TransactionStatus } from "../../transaction/entities/transaction.entity";
import { L2_ETH_TOKEN_ADDRESS } from "../../common/constants";
import { BASE_TOKEN_L2_ADDRESS } from "../../common/constants";
import { mapInternalTransactionListItem } from "./internalTransactionMapper";

describe("internalTransactionMapper", () => {
Expand All @@ -11,7 +11,7 @@ describe("internalTransactionMapper", () => {
from: "0xc7e0220d02d549c4846A6EC31D89C3B670Ebe35C",
to: "0xc7e0220d02d549c4846A6EC31D89C3B670Ebe35D",
amount: "1000000",
tokenAddress: L2_ETH_TOKEN_ADDRESS,
tokenAddress: BASE_TOKEN_L2_ADDRESS,
transaction: {
blockNumber: 20,
receivedAt: new Date("2023-01-01"),
Expand Down
7 changes: 4 additions & 3 deletions packages/api/src/api/stats/stats.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import { Test } from "@nestjs/testing";
import { mock } from "jest-mock-extended";
import { Logger } from "@nestjs/common";
import { TokenService } from "../../token/token.service";
import { Token, ETH_TOKEN } from "../../token/token.entity";
import { Token } from "../../token/token.entity";
import { StatsController } from "./stats.controller";
import { baseTokenData } from "../../config";

describe("StatsController", () => {
let controller: StatsController;
Expand Down Expand Up @@ -31,7 +32,7 @@ describe("StatsController", () => {
describe("ethPrice", () => {
it("returns ok response and ETH price when ETH token is found", async () => {
jest.spyOn(tokenServiceMock, "findOne").mockResolvedValueOnce({
usdPrice: ETH_TOKEN.usdPrice,
usdPrice: baseTokenData.usdPrice,
offChainDataUpdatedAt: new Date("2023-03-03"),
} as Token);

Expand All @@ -40,7 +41,7 @@ describe("StatsController", () => {
status: "1",
message: "OK",
result: {
ethusd: ETH_TOKEN.usdPrice.toString(),
ethusd: baseTokenData.usdPrice.toString(),
ethusd_timestamp: Math.floor(new Date("2023-03-03").getTime() / 1000).toString(),
},
});
Expand Down
7 changes: 5 additions & 2 deletions packages/api/src/api/stats/stats.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { ResponseStatus, ResponseMessage } from "../dtos/common/responseBase.dto
import { ApiExceptionFilter } from "../exceptionFilter";
import { EthPriceResponseDto } from "../dtos/stats/ethPrice.dto";
import { TokenService } from "../../token/token.service";
import { ETH_TOKEN } from "../../token/token.entity";
import { dateToTimestamp } from "../../common/utils";
import { baseTokenData } from "../../config";

const entityName = "stats";

Expand All @@ -18,7 +18,10 @@ export class StatsController {

@Get("/ethprice")
public async ethPrice(): Promise<EthPriceResponseDto> {
const token = await this.tokenService.findOne(ETH_TOKEN.l2Address, { usdPrice: true, offChainDataUpdatedAt: true });
const token = await this.tokenService.findOne(baseTokenData.l2Address, {
usdPrice: true,
offChainDataUpdatedAt: true,
});
return {
status: token ? ResponseStatus.OK : ResponseStatus.NOTOK,
message: token ? ResponseMessage.OK : ResponseMessage.NO_DATA_FOUND,
Expand Down
25 changes: 13 additions & 12 deletions packages/api/src/api/token/token.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import { Test } from "@nestjs/testing";
import { mock } from "jest-mock-extended";
import { Logger } from "@nestjs/common";
import { TokenService } from "../../token/token.service";
import { Token, ETH_TOKEN } from "../../token/token.entity";
import { Token } from "../../token/token.entity";
import { TokenController } from "./token.controller";

import config from "../../config/index";
const { baseTokenData } = config();
describe("TokenController", () => {
let controller: TokenController;
let tokenServiceMock: TokenService;
Expand Down Expand Up @@ -32,22 +33,22 @@ describe("TokenController", () => {

describe("tokenInfo", () => {
it("returns ok response and token info when token is found", async () => {
jest.spyOn(tokenServiceMock, "findOne").mockResolvedValueOnce(ETH_TOKEN);

const baseToken = baseTokenData as Token;
jest.spyOn(tokenServiceMock, "findOne").mockResolvedValueOnce(baseToken);
const response = await controller.tokenInfo(contractAddress);
expect(response).toEqual({
status: "1",
message: "OK",
result: [
{
contractAddress: ETH_TOKEN.l2Address,
iconURL: ETH_TOKEN.iconURL,
l1Address: ETH_TOKEN.l1Address,
liquidity: ETH_TOKEN.liquidity.toString(),
symbol: ETH_TOKEN.symbol,
tokenDecimal: ETH_TOKEN.decimals.toString(),
tokenName: ETH_TOKEN.name,
tokenPriceUSD: ETH_TOKEN.usdPrice.toString(),
contractAddress: baseToken.l2Address,
iconURL: baseToken.iconURL,
l1Address: baseToken.l1Address,
liquidity: baseToken.liquidity.toString(),
symbol: baseToken.symbol,
tokenDecimal: baseToken.decimals.toString(),
tokenName: baseToken.name,
tokenPriceUSD: baseToken.usdPrice.toString(),
},
],
});
Expand Down
1 change: 0 additions & 1 deletion packages/api/src/api/token/token.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { ResponseStatus, ResponseMessage } from "../dtos/common/responseBase.dto
import { ApiExceptionFilter } from "../exceptionFilter";
import { TokenInfoResponseDto } from "../dtos/token/tokenInfo.dto";
import { TokenService } from "../../token/token.service";

const entityName = "token";

@ApiExcludeController()
Expand Down
13 changes: 8 additions & 5 deletions packages/api/src/balance/balance.entity.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Entity, Column, PrimaryColumn, Index, ManyToOne, JoinColumn, AfterLoad } from "typeorm";
import { BaseEntity } from "../common/entities/base.entity";
import { Token, ETH_TOKEN } from "../token/token.entity";
import { Token } from "../token/token.entity";
import { normalizeAddressTransformer } from "../common/transformers/normalizeAddress.transformer";
import { bigIntNumberTransformer } from "../common/transformers/bigIntNumber.transformer";

import { baseTokenData } from "../config/index";
@Entity({ name: "balances" })
export class Balance extends BaseEntity {
@PrimaryColumn({ type: "bytea", transformer: normalizeAddressTransformer })
Expand All @@ -24,9 +24,12 @@ export class Balance extends BaseEntity {
public readonly balance: string;

@AfterLoad()
populateEthToken() {
if (this.tokenAddress === ETH_TOKEN.l2Address && !this.token) {
this.token = ETH_TOKEN;
populateBaseToken() {
if (
!this.token &&
(this.tokenAddress === undefined || this.tokenAddress.toLowerCase() === baseTokenData.l2Address.toLowerCase())
) {
this.token = baseTokenData as Token;
}
}
}
3 changes: 2 additions & 1 deletion packages/api/src/common/constants.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export const L2_ETH_TOKEN_ADDRESS = "0x000000000000000000000000000000000000800a";
export const BASE_TOKEN_L2_ADDRESS = "0x000000000000000000000000000000000000800A";
export const BASE_TOKEN_L1_ADDRESS = "0x0000000000000000000000000000000000000000";
68 changes: 66 additions & 2 deletions packages/api/src/config/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import config from "../config";

jest.mock("./featureFlags", () => ({
feature1Enabled: true,
feature2Enabled: false,
Expand All @@ -20,6 +19,71 @@ describe("config", () => {

it("sets default values", () => {
expect(config()).toEqual({
baseTokenData: {
l2Address: "0x000000000000000000000000000000000000800A",
l1Address: "0x0000000000000000000000000000000000000000",
symbol: "ETH",
name: "Ether",
decimals: 18,
// Fallback data in case ETH token is not in the DB
iconURL: "https://assets.coingecko.com/coins/images/279/large/ethereum.png?1698873266",
liquidity: 220000000000,
usdPrice: 1800,
},
NODE_ENV: "test",
port: 3020,
metrics: {
port: 3005,
collectDbConnectionPoolMetricsInterval: 10000,
},
typeORM: {
type: "postgres",
url: "postgres://postgres:[email protected]:5432/block-explorer",
poolSize: 300,
extra: {
idleTimeoutMillis: 60000,
statement_timeout: 90000,
},
synchronize: true,
logging: false,
autoLoadEntities: true,
retryAttempts: 10,
retryDelay: 3000,
applicationName: "block-explorer-api",
},
contractVerificationApiUrl: "http://127.0.0.1:3070",
featureFlags: {
feature1Enabled: true,
feature2Enabled: false,
},
gracefulShutdownTimeoutMs: 0,
});
});

it("sets default values with base ERC20", () => {
process.env = {
BASE_TOKEN_SYMBOL: "MTTL",
BASE_TOKEN_DECIMALS: "18",
BASE_TOKEN_L1_ADDRESS: "0xSomeAddress",
BASE_TOKEN_ICON_URL: "https://matter-labs.io",
BASE_TOKEN_NAME: "MatterLabs",
BASE_TOKEN_LIQUIDITY: "999999999999",
BASE_TOKEN_USDPRICE: "19",
NODE_ENV: "test",
};

expect(config()).toEqual({
baseTokenData: {
l2Address: "0x000000000000000000000000000000000000800A",
l1Address: "0xSomeAddress",
symbol: "MTTL",
name: "MatterLabs",
decimals: 18,
// Fallback data in case ETH token is not in the DB
iconURL: "https://matter-labs.io",
liquidity: 999999999999,
usdPrice: 19,
},
NODE_ENV: "test",
port: 3020,
metrics: {
Expand All @@ -28,7 +92,7 @@ describe("config", () => {
},
typeORM: {
type: "postgres",
url: "postgres://postgres:postgres@localhost:5432/block-explorer",
url: "postgres://postgres:postgres@127.0.0.1:5432/block-explorer",
poolSize: 300,
extra: {
idleTimeoutMillis: 60000,
Expand Down
57 changes: 56 additions & 1 deletion packages/api/src/config/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,55 @@
import { TypeOrmModuleOptions } from "@nestjs/typeorm";
import * as featureFlags from "./featureFlags";
import { BASE_TOKEN_L1_ADDRESS, BASE_TOKEN_L2_ADDRESS } from "../common/constants";
type BaseToken = {
symbol: string;
decimals: number;
l1Address: string;
l2Address: string;
liquidity: number;
iconURL: string;
name: string;
usdPrice: number;
};
const defaultEthBaseToken: BaseToken = {
l2Address: BASE_TOKEN_L2_ADDRESS,
l1Address: BASE_TOKEN_L1_ADDRESS,
symbol: "ETH",
name: "Ether",
decimals: 18,
// Fallback data in case ETH token is not in the DB
iconURL: "https://assets.coingecko.com/coins/images/279/large/ethereum.png?1698873266",
liquidity: 220000000000,
usdPrice: 1800,
};
const baseTokenFromEnv = (): BaseToken => {
const {
BASE_TOKEN_SYMBOL,
BASE_TOKEN_DECIMALS,
BASE_TOKEN_L1_ADDRESS,
BASE_TOKEN_ICON_URL,
BASE_TOKEN_NAME,
BASE_TOKEN_LIQUIDITY,
BASE_TOKEN_USDPRICE,
} = process.env;
const decimals = parseFloat(BASE_TOKEN_DECIMALS);
const liquidity = parseFloat(BASE_TOKEN_LIQUIDITY);
const usdPrice = parseFloat(BASE_TOKEN_USDPRICE);
if (BASE_TOKEN_L1_ADDRESS && BASE_TOKEN_SYMBOL) {
return {
symbol: BASE_TOKEN_SYMBOL,
decimals,
l1Address: BASE_TOKEN_L1_ADDRESS,
l2Address: BASE_TOKEN_L2_ADDRESS,
liquidity,
iconURL: BASE_TOKEN_ICON_URL,
usdPrice,
name: BASE_TOKEN_NAME,
};
} else {
return defaultEthBaseToken;
}
};

export default () => {
const {
Expand All @@ -15,6 +65,8 @@ export default () => {
GRACEFUL_SHUTDOWN_TIMEOUT_MS,
} = process.env;

const baseTokenData: BaseToken = baseTokenFromEnv();

const MAX_NUMBER_OF_REPLICA = 100;

const getDatabaseReplicaSet = () => {
Expand All @@ -32,7 +84,7 @@ export default () => {
};

const getTypeOrmModuleOptions = (): TypeOrmModuleOptions => {
const master = { url: DATABASE_URL || "postgres://postgres:postgres@localhost:5432/block-explorer" };
const master = { url: DATABASE_URL || "postgres://postgres:postgres@127.0.0.1:5432/block-explorer" };
const replicaSet = getDatabaseReplicaSet();

return {
Expand Down Expand Up @@ -75,6 +127,9 @@ export default () => {
typeORM: getTypeOrmModuleOptions(),
contractVerificationApiUrl: CONTRACT_VERIFICATION_API_URL || "http://127.0.0.1:3070",
featureFlags,
baseTokenData,
gracefulShutdownTimeoutMs: parseInt(GRACEFUL_SHUTDOWN_TIMEOUT_MS, 10) || 0,
};
};

export const baseTokenData = baseTokenFromEnv();
Loading
Loading