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

[Job Launcher] Added testnet and mainnet setup #763

Merged
merged 13 commits into from
Aug 15, 2023
1 change: 1 addition & 0 deletions packages/apps/job-launcher/server/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ POSTGRES_SYNC=false
POSTGRES_PORT=5432

#Web3
WEB3_ENV=testnet
WEB3_PRIVATE_KEY=
JOB_LAUNCHER_FEE=1
RECORDING_ORACLE_FEE=1
Expand Down
2 changes: 2 additions & 0 deletions packages/apps/job-launcher/server/src/common/config/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export const ConfigNames = {
POSTGRES_DB: 'POSTGRES_DB',
POSTGRES_PORT: 'POSTGRES_PORT',
POSTGRES_SYNC: 'POSTGRES_SYNC',
WEB3_ENV: 'WEB3_ENV',
WEB3_PRIVATE_KEY: 'WEB3_PRIVATE_KEY',
JOB_LAUNCHER_FEE: 'JOB_LAUNCHER_FEE',
RECORDING_ORACLE_FEE: 'RECORDING_ORACLE_FEE',
Expand Down Expand Up @@ -58,6 +59,7 @@ export const envValidator = Joi.object({
POSTGRES_PORT: Joi.string().default('5432'),
POSTGRES_SYNC: Joi.string().default(false),
// Web3
WEB3_ENV: Joi.string().default('testnet'),
WEB3_PRIVATE_KEY: Joi.string().required(),
JOB_LAUNCHER_FEE: Joi.string().default(10),
RECORDING_ORACLE_FEE: Joi.string().default(10),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,11 @@ export enum ErrorBucket {
NotPublic = 'Bucket is not public',
UnableSaveFile = 'Unable to save file',
}

/**
* Represents error messages related to web3.
*/
export enum ErrorWeb3 {
InvalidTestnetChainId = 'Invalid chain id provided for the testnet environment',
InvalidMainnetChainId = 'Invalid chain id provided for the mainnet environment',
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { ChainId } from "@human-protocol/sdk";

export const NS = 'hmt';
export const COINGECKO_API_URL =
'https://api.coingecko.com/api/v3/simple/price';
export const JOB_RETRIES_COUNT_THRESHOLD = 3;
export const TX_CONFIRMATION_TRESHOLD = 1;

export const JWT_PREFIX = 'bearer ';
export const TESTNET_CHAIN_IDS = [ChainId.BSC_TESTNET, ChainId.POLYGON_MUMBAI, ChainId.GOERLI];
export const MAINNET_CHAIN_IDS = [ChainId.BSC_MAINNET, ChainId.POLYGON, ChainId.MOONBEAM];
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { Module } from '@nestjs/common';
import { Logger, Module } from '@nestjs/common';
import { Web3Service } from './web3.service';
import { ConfigModule } from '@nestjs/config';

@Module({
imports: [ConfigModule],
providers: [Web3Service],
providers: [Web3Service, Logger],
exports: [Web3Service],
})
export class Web3Module {}
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,36 @@ import { ConfigService } from '@nestjs/config';
import { Test } from '@nestjs/testing';
import { networkMap } from '../../common/config';
import { Web3Service } from './web3.service';
import { ChainId } from '@human-protocol/sdk';
import { ErrorWeb3 } from '../../common/constants/errors';
import { Web3Env } from '../../common/enums/web3';

describe('Web3Service', () => {
let mockConfigService: Partial<ConfigService>;
let web3Service: Web3Service;
const privateKey =
'5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a';

beforeAll(async () => {
const configServiceMock = {
get: jest.fn().mockReturnValue(privateKey),
mockConfigService = {
get: jest.fn((key: string, defaultValue?: any) => {
switch (key) {
case 'WEB3_PRIVATE_KEY':
return privateKey;
case 'WEB3_ENV':
return 'testnet';
default:
return defaultValue;
}
}),
};

const moduleRef = await Test.createTestingModule({
providers: [
Web3Service,
{
provide: ConfigService,
useValue: configServiceMock,
useValue: mockConfigService,
},
],
}).compile();
Expand All @@ -27,23 +40,36 @@ describe('Web3Service', () => {
});

describe('getSigner', () => {
it('should return the signer for the specified chainId', async () => {
for (const networkKey of Object.keys(networkMap)) {
// Iterate through the networkMap to test each chainId
const network = networkMap[networkKey];

const signer = web3Service.getSigner(network.chainId);
expect(signer).toBeDefined();
expect((signer.provider as any).connection.url).toBe(network.rpcUrl);
}
it('should return a signer for a valid chainId on MAINNET', () => {
mockConfigService.get = jest.fn().mockReturnValue(Web3Env.MAINNET);

const validChainId = ChainId.POLYGON;

const signer = web3Service.getSigner(validChainId);
expect(signer).toBeDefined();
});

it('should throw invalid chain id provided for the mainnet environment', () => {
mockConfigService.get = jest.fn().mockReturnValue(Web3Env.MAINNET);
const invalidChainId = ChainId.LOCALHOST;

expect(() => web3Service.getSigner(invalidChainId)).toThrow(ErrorWeb3.InvalidMainnetChainId);
});

it('should return undefined if chainId is not configured', () => {
const chainId = 1;
it('should return a signer for a valid chainId on TESTNET', () => {
mockConfigService.get = jest.fn().mockReturnValue(Web3Env.TESTNET);

const validChainId = ChainId.POLYGON_MUMBAI;

const signer = web3Service.getSigner(validChainId);
expect(signer).toBeDefined();
});

const signer = web3Service.getSigner(chainId);
it('should throw invalid chain id provided for the testnet environment', () => {
mockConfigService.get = jest.fn().mockReturnValue(Web3Env.TESTNET);
const invalidChainId = ChainId.POLYGON;

expect(signer).toBeUndefined();
expect(() => web3Service.getSigner(invalidChainId)).toThrow(ErrorWeb3.InvalidTestnetChainId);
});
});
});
16 changes: 14 additions & 2 deletions packages/apps/job-launcher/server/src/modules/web3/web3.service.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import { Injectable } from '@nestjs/common';
import { BadRequestException, Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Wallet, providers } from 'ethers';
import { ConfigNames, networkMap } from '../../common/config';
import { Web3Env } from '../../common/enums/web3';
import { MAINNET_CHAIN_IDS, TESTNET_CHAIN_IDS } from '../../common/constants';
import { ErrorWeb3 } from '../../common/constants/errors';

@Injectable()
export class Web3Service {
private signers: { [key: number]: Wallet } = {};
public readonly logger = new Logger(Web3Service.name);

constructor(private readonly configService: ConfigService) {
const privateKey = this.configService.get(ConfigNames.WEB3_PRIVATE_KEY);
Expand All @@ -17,6 +21,14 @@ export class Web3Service {
}

getSigner(chainId: number): Wallet {
if (Web3Env.MAINNET === this.configService.get(ConfigNames.WEB3_ENV) && !MAINNET_CHAIN_IDS.includes(chainId)) {
this.logger.log(ErrorWeb3.InvalidMainnetChainId, Web3Service.name);
throw new BadRequestException(ErrorWeb3.InvalidMainnetChainId);
} else if (Web3Env.TESTNET === this.configService.get(ConfigNames.WEB3_ENV) && !TESTNET_CHAIN_IDS.includes(chainId)) {
this.logger.log(ErrorWeb3.InvalidTestnetChainId, Web3Service.name);
throw new BadRequestException(ErrorWeb3.InvalidTestnetChainId);
}

return this.signers[chainId];
}
}
}