Skip to content

Commit

Permalink
feat: initial changes to show base token - SQUASHED
Browse files Browse the repository at this point in the history
  • Loading branch information
SantiagoPittella committed May 29, 2024
1 parent 146e2d9 commit d8af700
Show file tree
Hide file tree
Showing 73 changed files with 507 additions and 318 deletions.
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=wETH
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;
}
}
}
2 changes: 1 addition & 1 deletion packages/api/src/common/constants.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export const L2_ETH_TOKEN_ADDRESS = "0x000000000000000000000000000000000000800a";
export const BASE_TOKEN_L2_ADDRESS = "0x000000000000000000000000000000000000800A";
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: "0x0000000000000000000000000000000000000001",
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_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: "0x0000000000000000000000000000000000000001",
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

0 comments on commit d8af700

Please sign in to comment.