mirror of
https://github.com/tornadocash/tornado-pool-relayer
synced 2024-02-02 15:04:09 +01:00
refactor: spread across services & added docker
This commit is contained in:
parent
268dd097b7
commit
79af9c7ce0
3
.dockerignore
Normal file
3
.dockerignore
Normal file
@ -0,0 +1,3 @@
|
||||
node_modules
|
||||
.env
|
||||
.git
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -9,6 +9,7 @@ npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.env
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
@ -31,4 +32,4 @@ lerna-debug.log*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
!.vscode/extensions.json
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all",
|
||||
"printWidth": 140
|
||||
}
|
||||
|
9
Dockerfile
Normal file
9
Dockerfile
Normal file
@ -0,0 +1,9 @@
|
||||
FROM node:12
|
||||
WORKDIR /app
|
||||
|
||||
COPY package.json yarn.lock ./
|
||||
RUN yarn && yarn cache clean --force
|
||||
COPY . .
|
||||
|
||||
EXPOSE 8000
|
||||
ENTRYPOINT ["yarn"]
|
63
docker-compose.yml
Normal file
63
docker-compose.yml
Normal file
@ -0,0 +1,63 @@
|
||||
version: '2'
|
||||
|
||||
services:
|
||||
server:
|
||||
image: tornadocash/relayer
|
||||
restart: always
|
||||
command: start:prod
|
||||
env_file: .env
|
||||
environment:
|
||||
REDIS_URL: redis://redis/0
|
||||
nginx_proxy_read_timeout: 600
|
||||
depends_on: [redis]
|
||||
links:
|
||||
- redis
|
||||
|
||||
redis:
|
||||
image: redis
|
||||
restart: always
|
||||
command: [redis-server, --appendonly, 'yes']
|
||||
volumes:
|
||||
- redis:/data
|
||||
|
||||
nginx:
|
||||
image: nginx:alpine
|
||||
container_name: nginx
|
||||
restart: always
|
||||
ports:
|
||||
- 80:80
|
||||
- 443:443
|
||||
volumes:
|
||||
- conf:/etc/nginx/conf.d
|
||||
- vhost:/etc/nginx/vhost.d
|
||||
- html:/usr/share/nginx/html
|
||||
- certs:/etc/nginx/certs
|
||||
logging:
|
||||
driver: none
|
||||
|
||||
dockergen:
|
||||
image: poma/docker-gen
|
||||
container_name: dockergen
|
||||
restart: always
|
||||
command: -notify-sighup nginx -watch /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf
|
||||
volumes_from:
|
||||
- nginx
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
|
||||
letsencrypt:
|
||||
image: jrcs/letsencrypt-nginx-proxy-companion
|
||||
container_name: letsencrypt
|
||||
restart: always
|
||||
environment:
|
||||
NGINX_DOCKER_GEN_CONTAINER: dockergen
|
||||
volumes_from:
|
||||
- nginx
|
||||
- dockergen
|
||||
|
||||
volumes:
|
||||
conf:
|
||||
vhost:
|
||||
html:
|
||||
certs:
|
||||
redis:
|
18
example.env
18
example.env
@ -1,3 +1,17 @@
|
||||
RPC_URL=
|
||||
PRIVATE_KEY=
|
||||
# DNS settings
|
||||
VIRTUAL_HOST=
|
||||
LETSENCRYPT_HOST=
|
||||
|
||||
# server settings
|
||||
PORT=8000
|
||||
|
||||
#commision for service
|
||||
SERVICE_FEE=0.05
|
||||
|
||||
# bull settings
|
||||
REDIS_URL=redis://127.0.0.1:6379
|
||||
|
||||
# tx-manager settings
|
||||
RPC_URL=
|
||||
CHAIN_ID=1
|
||||
PRIVATE_KEY=
|
||||
|
@ -13,7 +13,7 @@
|
||||
"start": "nest start",
|
||||
"start:dev": "nest start --watch",
|
||||
"start:debug": "nest start --debug --watch",
|
||||
"start:prod": "node dist/main",
|
||||
"start:prod": "yarn prebuild; yarn build; node dist/main",
|
||||
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch",
|
||||
|
@ -2,7 +2,7 @@ import { Module } from '@nestjs/common';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
|
||||
import { baseConfig } from '@/config';
|
||||
import { QueueModule, StatusModule } from '@/modules';
|
||||
import { QueueModule, ApiModule } from '@/modules';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@ -10,8 +10,8 @@ import { QueueModule, StatusModule } from '@/modules';
|
||||
load: [baseConfig],
|
||||
isGlobal: true,
|
||||
}),
|
||||
ApiModule,
|
||||
QueueModule,
|
||||
StatusModule,
|
||||
],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { registerAs } from '@nestjs/config';
|
||||
|
||||
export default registerAs('bull', () => ({
|
||||
name: 'withdrawal',
|
||||
redis: {
|
||||
host: 'localhost',
|
||||
port: 6379,
|
||||
|
@ -1,5 +1,6 @@
|
||||
export const baseConfig = () => ({
|
||||
gasLimit: 600000,
|
||||
serviceFee: process.env.SERVICE_FEE,
|
||||
chainId: process.env.CHAIN_ID,
|
||||
port: parseInt(process.env.PORT, 10) || 8080,
|
||||
gasLimit: 400000,
|
||||
fee: process.env.SERVICE_FEE,
|
||||
});
|
||||
|
@ -3,7 +3,7 @@ import { ChainId } from '@/types';
|
||||
export const CONTRACT_NETWORKS: { [chainId in ChainId]: string } = {
|
||||
[ChainId.MAINNET]: '0x8Bfac9EF3d73cE08C7CEC339C0fE3B2e57814c1E',
|
||||
[ChainId.GOERLI]: '0x20a2D506cf52453D681F9E8E814A3437c6242B9e',
|
||||
[ChainId.OPTIMISM]: '0xc436071dE853A4421c57ddD0CDDC116C735aa8b5',
|
||||
[ChainId.OPTIMISM]: '0x1Ed4dcDB4b78985008199f451E88C6448C4EDd94',
|
||||
};
|
||||
|
||||
export const RPC_LIST: { [chainId in ChainId]: string } = {
|
||||
|
@ -1,10 +0,0 @@
|
||||
import { ChainId } from '@/types';
|
||||
import { CONTRACT_NETWORKS } from '@/constants';
|
||||
import { getProviderWithSigner } from '@/services';
|
||||
|
||||
import { TornadoPool__factory as TornadoPoolFactory } from '@/artifacts';
|
||||
|
||||
export function getTornadoPool(chainId: ChainId) {
|
||||
const provider = getProviderWithSigner(chainId);
|
||||
return TornadoPoolFactory.connect(CONTRACT_NETWORKS[chainId], provider);
|
||||
}
|
31
src/modules/api/api.controller.ts
Normal file
31
src/modules/api/api.controller.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import { Controller, Body, Param, Get, Post } from '@nestjs/common';
|
||||
import { Job } from 'bull';
|
||||
|
||||
import { ApiService } from './api.service';
|
||||
|
||||
@Controller()
|
||||
export class ApiController {
|
||||
constructor(private readonly service: ApiService) {}
|
||||
|
||||
@Get('/api')
|
||||
async status(): Promise<Health> {
|
||||
return await this.service.status();
|
||||
}
|
||||
|
||||
@Get('/')
|
||||
async main(): Promise<string> {
|
||||
return this.service.main();
|
||||
}
|
||||
|
||||
@Get('/job/:jobId')
|
||||
async getJob(@Param('jobId') jobId: string): Promise<Job> {
|
||||
return await this.service.getJob(jobId);
|
||||
}
|
||||
|
||||
@Post('/withdrawal')
|
||||
async withdrawal(_, @Body() { body }: any): Promise<string> {
|
||||
console.log('body', body);
|
||||
|
||||
return await this.service.withdrawal(JSON.parse(body));
|
||||
}
|
||||
}
|
@ -1,15 +1,15 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
|
||||
import { StatusService } from './stat.service';
|
||||
import { StatusController } from './stat.controller';
|
||||
import { ApiService } from './api.service';
|
||||
import { ApiController } from './api.controller';
|
||||
|
||||
import { QueueModule } from '@/modules';
|
||||
|
||||
@Module({
|
||||
imports: [ConfigModule, QueueModule],
|
||||
providers: [StatusService],
|
||||
controllers: [StatusController],
|
||||
providers: [ApiService],
|
||||
controllers: [ApiController],
|
||||
exports: [],
|
||||
})
|
||||
export class StatusModule {}
|
||||
export class ApiModule {}
|
@ -1,9 +1,9 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Queue } from 'bull';
|
||||
import { Queue, Job } from 'bull';
|
||||
import { InjectQueue } from '@nestjs/bull';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
class StatusService {
|
||||
class ApiService {
|
||||
constructor(@InjectQueue('withdrawal') private withdrawalQueue: Queue) {}
|
||||
|
||||
async status(): Promise<Health> {
|
||||
@ -17,11 +17,15 @@ class StatusService {
|
||||
return `This is <a href=https://tornado.cash>tornado.cash</a> Relayer service. Check the <a href=/status>/status</a> for settings`;
|
||||
}
|
||||
|
||||
async withdrawal(data): Promise<string> {
|
||||
const job = await this.withdrawalQueue.add(data)
|
||||
async withdrawal(data: any): Promise<string> {
|
||||
const job = await this.withdrawalQueue.add(data);
|
||||
|
||||
return String(job.id);
|
||||
}
|
||||
|
||||
async getJob(id: string): Promise<Job> {
|
||||
return await this.withdrawalQueue.getJob(id);
|
||||
}
|
||||
}
|
||||
|
||||
export { StatusService };
|
||||
export { ApiService };
|
@ -1,4 +1,4 @@
|
||||
export class CreateStatusDto {
|
||||
export class CreateApiDto {
|
||||
error: boolean;
|
||||
status: string;
|
||||
}
|
1
src/modules/api/index.ts
Normal file
1
src/modules/api/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { ApiModule } from './api.module';
|
@ -1,2 +1,2 @@
|
||||
export * from './queue';
|
||||
export * from './status';
|
||||
export * from './api';
|
||||
|
@ -10,7 +10,6 @@ import {
|
||||
} from '@nestjs/bull';
|
||||
import { Injectable, OnModuleDestroy } from '@nestjs/common';
|
||||
import { Job, Queue } from 'bull';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
@Injectable()
|
||||
@Processor()
|
||||
@ -54,17 +53,11 @@ export class BaseProcessor<T = object> implements OnModuleDestroy {
|
||||
return this.updateTask(job);
|
||||
}
|
||||
|
||||
async updateTask(job: Job<T>) {
|
||||
private async updateTask(job: Job<T>) {
|
||||
const currentJob = await this.queue.getJob(job.id);
|
||||
await currentJob.update(job.data);
|
||||
}
|
||||
|
||||
private async createTask({ request }) {
|
||||
const id = uuid();
|
||||
await this.queue.add({ ...request, id });
|
||||
return id;
|
||||
}
|
||||
|
||||
async onModuleDestroy() {
|
||||
if (this.queue) {
|
||||
await this.queue.close();
|
||||
|
@ -1,18 +1,15 @@
|
||||
import { BullModule } from '@nestjs/bull';
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { GasPriceService, ProviderService } from '@/services';
|
||||
|
||||
import { WithdrawalProcessor } from './withdrawal.processor';
|
||||
|
||||
import bullConfig from '@/config/bull.config';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
BullModule.registerQueue({
|
||||
...bullConfig(),
|
||||
name: 'withdrawal',
|
||||
}),
|
||||
],
|
||||
providers: [WithdrawalProcessor],
|
||||
imports: [BullModule.registerQueue(bullConfig())],
|
||||
providers: [GasPriceService, ProviderService, WithdrawalProcessor],
|
||||
exports: [BullModule],
|
||||
})
|
||||
export class QueueModule {}
|
||||
|
@ -7,11 +7,11 @@ import { ConfigService } from '@nestjs/config';
|
||||
import { InjectQueue, Process, Processor } from '@nestjs/bull';
|
||||
|
||||
import { toWei } from '@/utilities';
|
||||
import { getGasPrice } from '@/services';
|
||||
import { getTornadoPool } from '@/contracts';
|
||||
import { GasPriceService, ProviderService } from '@/services';
|
||||
import txMangerConfig from '@/config/txManager.config';
|
||||
|
||||
import { BaseProcessor } from './base.processor';
|
||||
import { ChainId } from '@/types';
|
||||
|
||||
export interface Withdrawal {
|
||||
args: string[];
|
||||
@ -27,6 +27,8 @@ export interface Withdrawal {
|
||||
export class WithdrawalProcessor extends BaseProcessor<Withdrawal> {
|
||||
constructor(
|
||||
@InjectQueue('withdrawal') public withdrawalQueue: Queue,
|
||||
private gasPriceService: GasPriceService,
|
||||
private providerService: ProviderService,
|
||||
private configService: ConfigService,
|
||||
) {
|
||||
super();
|
||||
@ -49,12 +51,12 @@ export class WithdrawalProcessor extends BaseProcessor<Withdrawal> {
|
||||
}
|
||||
|
||||
async submitTx(job: Job<Withdrawal>) {
|
||||
const txManager = new TxManager(txMangerConfig());
|
||||
|
||||
const prepareTx = await this.prepareTransaction(job.data);
|
||||
const tx = await txManager.createTx(prepareTx);
|
||||
|
||||
try {
|
||||
const txManager = new TxManager(txMangerConfig());
|
||||
|
||||
const prepareTx = await this.prepareTransaction(job.data);
|
||||
const tx = await txManager.createTx(prepareTx);
|
||||
|
||||
const receipt = await tx
|
||||
.send()
|
||||
.on('transactionHash', async (txHash: string) => {
|
||||
@ -88,44 +90,52 @@ export class WithdrawalProcessor extends BaseProcessor<Withdrawal> {
|
||||
}
|
||||
}
|
||||
|
||||
async prepareTransaction({ proof, args, amount }) {
|
||||
const contract = getTornadoPool(5);
|
||||
async prepareTransaction({ proof, args }) {
|
||||
const chainId = this.configService.get<number>('chainId');
|
||||
|
||||
const contract = this.providerService.getTornadoPool();
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
const data = contract.interface.encodeFunctionData('transaction', [
|
||||
proof,
|
||||
...args,
|
||||
]);
|
||||
const data = contract.interface.encodeFunctionData('transaction', [proof, ...args]);
|
||||
|
||||
const gasLimit = this.configService.get<number>('gasLimit');
|
||||
let gasLimit = this.configService.get<BigNumber>('gasLimit');
|
||||
|
||||
// need because optimism has dynamic gas limit
|
||||
if (chainId === ChainId.OPTIMISM) {
|
||||
// @ts-ignore
|
||||
gasLimit = await contract.estimateGas.transaction(proof, ...args, {
|
||||
value: BigNumber.from(0)._hex,
|
||||
from: '0x1a5245ea5210C3B57B7Cfdf965990e63534A7b52',
|
||||
gasPrice: toWei('0.015', 'gwei'),
|
||||
});
|
||||
}
|
||||
|
||||
const { fast } = await this.gasPriceService.getGasPrice();
|
||||
|
||||
return {
|
||||
data,
|
||||
gasLimit,
|
||||
value: BigNumber.from(0)._hex,
|
||||
to: contract.address,
|
||||
gasPrice: fast.toString(),
|
||||
value: BigNumber.from(0)._hex,
|
||||
};
|
||||
}
|
||||
|
||||
async checkFee({ fee, amount }) {
|
||||
const gasLimit = this.configService.get<number>('gasLimit');
|
||||
const { gasLimit, serviceFee } = this.configService.get('');
|
||||
|
||||
const { fast } = await getGasPrice(5);
|
||||
const { fast } = await this.gasPriceService.getGasPrice();
|
||||
|
||||
const expense = BigNumber.from(toWei(fast.toString(), 'gwei')).mul(
|
||||
gasLimit,
|
||||
);
|
||||
const expense = BigNumber.from(toWei(fast.toString(), 'gwei')).mul(gasLimit);
|
||||
|
||||
const serviceFee = this.configService.get<number>('fee');
|
||||
const feePercent = BigNumber.from(amount).mul(serviceFee * 1e10).div(100 * 1e10)
|
||||
const feePercent = BigNumber.from(amount)
|
||||
.mul(serviceFee * 1e10)
|
||||
.div(100 * 1e10);
|
||||
|
||||
const desiredFee = expense.add(feePercent);
|
||||
|
||||
if (BigNumber.from(fee).lt(desiredFee)) {
|
||||
throw new Error(
|
||||
'Provided fee is not enough. Probably it is a Gas Price spike, try to resubmit.',
|
||||
);
|
||||
throw new Error('Provided fee is not enough. Probably it is a Gas Price spike, try to resubmit.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1 +0,0 @@
|
||||
export { StatusModule } from './stat.module';
|
@ -1,25 +0,0 @@
|
||||
import { Controller, Body, Get, Post } from '@nestjs/common';
|
||||
|
||||
import { StatusService } from './stat.service';
|
||||
|
||||
@Controller()
|
||||
export class StatusController {
|
||||
constructor(private readonly service: StatusService) {}
|
||||
|
||||
@Get('/status')
|
||||
async status(): Promise<Health> {
|
||||
return await this.service.status();
|
||||
}
|
||||
|
||||
@Get('/')
|
||||
async main(): Promise<string> {
|
||||
return this.service.main();
|
||||
}
|
||||
|
||||
@Post('/withdrawal')
|
||||
async withdrawal(_, @Body() { body }: any): Promise<string> {
|
||||
console.log('body', body)
|
||||
|
||||
return await this.service.withdrawal(JSON.parse(body))
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
import { ethers } from 'ethers';
|
||||
|
||||
import { ChainId } from '@/types';
|
||||
import { RPC_LIST } from '@/constants';
|
||||
|
||||
interface Options {
|
||||
url: string;
|
||||
}
|
||||
|
||||
export class Provider {
|
||||
public provider: ethers.providers.JsonRpcProvider;
|
||||
|
||||
constructor(options: Options) {
|
||||
this.provider = new ethers.providers.JsonRpcProvider(options.url);
|
||||
}
|
||||
}
|
||||
|
||||
export function getProvider(chainId: ChainId): Provider {
|
||||
return new Provider({ url: RPC_LIST[chainId] });
|
||||
}
|
||||
|
||||
export function getProviderWithSigner(
|
||||
chainId: ChainId,
|
||||
): ethers.providers.BaseProvider {
|
||||
return ethers.providers.getDefaultProvider(RPC_LIST[chainId]);
|
||||
}
|
@ -1,91 +0,0 @@
|
||||
import { Wallet, PopulatedTransaction } from 'ethers';
|
||||
import {
|
||||
FlashbotsBundleProvider,
|
||||
FlashbotsBundleResolution,
|
||||
} from '@flashbots/ethers-provider-bundle';
|
||||
|
||||
import { ChainId } from '@/types';
|
||||
import { numbers } from '@/constants';
|
||||
import { getProviderWithSigner } from '@/services';
|
||||
|
||||
const authSigner = Wallet.createRandom();
|
||||
|
||||
const FLASH_BOT_RPC: { [key in ChainId]: { name: string; url: string } } = {
|
||||
[ChainId.GOERLI]: {
|
||||
url: 'https://relay-goerli.flashbots.net/',
|
||||
name: 'goerli',
|
||||
},
|
||||
[ChainId.MAINNET]: {
|
||||
url: '',
|
||||
name: '',
|
||||
},
|
||||
};
|
||||
|
||||
async function sendFlashBotTransaction(
|
||||
transaction: PopulatedTransaction,
|
||||
chainId: ChainId,
|
||||
) {
|
||||
const provider = getProviderWithSigner(chainId);
|
||||
|
||||
const { url, name } = FLASH_BOT_RPC[chainId];
|
||||
|
||||
const flashBotsProvider = await FlashbotsBundleProvider.create(
|
||||
provider,
|
||||
authSigner,
|
||||
url,
|
||||
name,
|
||||
);
|
||||
|
||||
const nonce = await provider.getTransactionCount(authSigner.address);
|
||||
|
||||
const mergedTx = {
|
||||
...transaction,
|
||||
nonce,
|
||||
from: authSigner.address,
|
||||
};
|
||||
|
||||
const signedTransaction = await authSigner.signTransaction(mergedTx);
|
||||
|
||||
const TIME_10_BLOCK = 130;
|
||||
|
||||
const blockNumber = await provider.getBlockNumber();
|
||||
const minTimestamp = (await provider.getBlock(blockNumber)).timestamp;
|
||||
|
||||
const maxTimestamp = minTimestamp + TIME_10_BLOCK;
|
||||
const targetBlockNumber = blockNumber + numbers.TWO;
|
||||
|
||||
const simulation = await flashBotsProvider.simulate(
|
||||
[signedTransaction],
|
||||
targetBlockNumber,
|
||||
);
|
||||
|
||||
if ('error' in simulation) {
|
||||
console.log(`Simulation Error: ${simulation.error.message}`);
|
||||
} else {
|
||||
console.log(
|
||||
`Simulation Success: ${JSON.stringify(simulation, null, numbers.TWO)}`,
|
||||
);
|
||||
}
|
||||
|
||||
const bundleSubmission = await flashBotsProvider.sendBundle(
|
||||
[{ signedTransaction }],
|
||||
targetBlockNumber,
|
||||
{
|
||||
minTimestamp,
|
||||
maxTimestamp,
|
||||
},
|
||||
);
|
||||
|
||||
if ('error' in bundleSubmission) {
|
||||
throw new Error(bundleSubmission.error.message);
|
||||
}
|
||||
|
||||
const waitResponse = await bundleSubmission.wait();
|
||||
const bundleSubmissionSimulation = await bundleSubmission.simulate();
|
||||
console.log({
|
||||
bundleSubmissionSimulation,
|
||||
waitResponse: FlashbotsBundleResolution[waitResponse],
|
||||
});
|
||||
}
|
||||
|
||||
export { sendFlashBotTransaction };
|
@ -1,3 +1,2 @@
|
||||
export * from './ether';
|
||||
export * from './oracle';
|
||||
export * from './flashbot';
|
||||
export * from './oracle.service';
|
||||
export * from './provider.service';
|
||||
|
45
src/services/oracle.service.ts
Normal file
45
src/services/oracle.service.ts
Normal file
@ -0,0 +1,45 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
|
||||
import { GasPriceOracle } from 'gas-price-oracle';
|
||||
import { GasPrice } from 'gas-price-oracle/lib/types';
|
||||
|
||||
import { ChainId } from '@/types';
|
||||
import { toWei } from '@/utilities';
|
||||
import { RPC_LIST, numbers } from '@/constants';
|
||||
|
||||
@Injectable()
|
||||
export class GasPriceService {
|
||||
private readonly chainId: number;
|
||||
|
||||
constructor(private configService: ConfigService) {
|
||||
this.chainId = this.configService.get<number>('chainId');
|
||||
}
|
||||
|
||||
async getGasPrice(): Promise<GasPrice> {
|
||||
if (this.chainId === ChainId.OPTIMISM) {
|
||||
return GasPriceService.getOptimismPrice();
|
||||
}
|
||||
|
||||
const TIMER = 10;
|
||||
const INTERVAL = TIMER * numbers.SECOND;
|
||||
|
||||
const instance = new GasPriceOracle({
|
||||
timeout: INTERVAL,
|
||||
defaultRpc: RPC_LIST[ChainId.MAINNET],
|
||||
});
|
||||
|
||||
return await instance.gasPrices();
|
||||
}
|
||||
|
||||
private static getOptimismPrice() {
|
||||
const OPTIMISM_GAS = toWei('0.015', 'gwei').toNumber();
|
||||
|
||||
return {
|
||||
fast: OPTIMISM_GAS,
|
||||
low: OPTIMISM_GAS,
|
||||
instant: OPTIMISM_GAS,
|
||||
standard: OPTIMISM_GAS,
|
||||
};
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
import { GasPriceOracle } from 'gas-price-oracle';
|
||||
import { GasPrice } from 'gas-price-oracle/lib/types';
|
||||
|
||||
import { ChainId } from '@/types';
|
||||
import { RPC_LIST, numbers } from '@/constants';
|
||||
|
||||
const SECONDS = 10;
|
||||
const TEN_SECOND = SECONDS * numbers.SECOND;
|
||||
|
||||
const OPTIMISM_GAS_PRICE = {
|
||||
fast: 0.015,
|
||||
low: 0.015,
|
||||
instant: 0.015,
|
||||
standard: 0.015,
|
||||
};
|
||||
|
||||
const getGasPrice = async (chainId: ChainId): Promise<GasPrice> => {
|
||||
if (chainId === ChainId.OPTIMISM) {
|
||||
return OPTIMISM_GAS_PRICE;
|
||||
}
|
||||
|
||||
const instance = new GasPriceOracle({
|
||||
timeout: TEN_SECOND,
|
||||
defaultRpc: RPC_LIST[ChainId.MAINNET],
|
||||
});
|
||||
return await instance.gasPrices();
|
||||
};
|
||||
|
||||
export { getGasPrice };
|
27
src/services/provider.service.ts
Normal file
27
src/services/provider.service.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import { ethers } from 'ethers';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
|
||||
import { CONTRACT_NETWORKS, RPC_LIST } from '@/constants';
|
||||
import { TornadoPool__factory as TornadoPoolFactory } from '@/artifacts';
|
||||
|
||||
@Injectable()
|
||||
export class ProviderService {
|
||||
private readonly chainId: number;
|
||||
|
||||
constructor(private configService: ConfigService) {
|
||||
this.chainId = this.configService.get<number>('chainId');
|
||||
}
|
||||
|
||||
getProvider() {
|
||||
return new ethers.providers.JsonRpcProvider(RPC_LIST[this.chainId]);
|
||||
}
|
||||
|
||||
getProviderWithSigner() {
|
||||
return ethers.providers.getDefaultProvider(RPC_LIST[this.chainId]);
|
||||
}
|
||||
|
||||
getTornadoPool() {
|
||||
return TornadoPoolFactory.connect(CONTRACT_NETWORKS[this.chainId], this.getProviderWithSigner());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user