diff --git a/apps/api/src/console.ts b/apps/api/src/console.ts index 1fab5d9d0..6f915f2bd 100644 --- a/apps/api/src/console.ts +++ b/apps/api/src/console.ts @@ -11,7 +11,6 @@ import { container } from "tsyringe"; import { WalletController } from "@src/billing/controllers/wallet/wallet.controller"; import { chainDb } from "@src/db/dbConnection"; import { TopUpDeploymentsController } from "@src/deployment/controllers/deployment/deployment.controller"; -import { TopUpManagedDeploymentsService } from "@src/deployment/services/top-up-managed-deployments/top-up-managed-deployments.service"; const program = new Command(); @@ -29,11 +28,11 @@ program program .command("top-up-deployments") + .option("-d, --dry-run", "Dry run the top up deployments", false) .description("Refill deployments with auto top up enabled") .action(async (options, command) => { await executeCliHandler(command.name(), async () => { - await container.resolve(TopUpManagedDeploymentsService).topUpDeployments(); - await container.resolve(TopUpDeploymentsController).topUpDeployments(); + await container.resolve(TopUpDeploymentsController).topUpDeployments({ dryRun: options.dryRun }); }); }); diff --git a/apps/api/src/deployment/controllers/deployment/deployment.controller.ts b/apps/api/src/deployment/controllers/deployment/deployment.controller.ts index 1602b6111..f63b1f09c 100644 --- a/apps/api/src/deployment/controllers/deployment/deployment.controller.ts +++ b/apps/api/src/deployment/controllers/deployment/deployment.controller.ts @@ -2,16 +2,20 @@ import { singleton } from "tsyringe"; import { StaleManagedDeploymentsCleanerService } from "@src/deployment/services/stale-managed-deployments-cleaner/stale-managed-deployments-cleaner.service"; import { TopUpCustodialDeploymentsService } from "@src/deployment/services/top-up-custodial-deployments/top-up-custodial-deployments.service"; +import { TopUpManagedDeploymentsService } from "@src/deployment/services/top-up-managed-deployments/top-up-managed-deployments.service"; +import { TopUpDeploymentsOptions } from "@src/deployment/types/deployments-refiller"; @singleton() export class TopUpDeploymentsController { constructor( private readonly topUpDeploymentsService: TopUpCustodialDeploymentsService, + private readonly topUpManagedDeploymentsService: TopUpManagedDeploymentsService, private readonly staleDeploymentsCleanerService: StaleManagedDeploymentsCleanerService ) {} - async topUpDeployments() { - await this.topUpDeploymentsService.topUpDeployments(); + async topUpDeployments(options: TopUpDeploymentsOptions) { + await this.topUpDeploymentsService.topUpDeployments(options); + await this.topUpManagedDeploymentsService.topUpDeployments(options); } async cleanUpStaleDeployment() { diff --git a/apps/api/src/deployment/services/top-up-custodial-deployments/top-up-custodial-deployments.service.spec.ts b/apps/api/src/deployment/services/top-up-custodial-deployments/top-up-custodial-deployments.service.spec.ts index eaf6925ad..b94ed28c6 100644 --- a/apps/api/src/deployment/services/top-up-custodial-deployments/top-up-custodial-deployments.service.spec.ts +++ b/apps/api/src/deployment/services/top-up-custodial-deployments/top-up-custodial-deployments.service.spec.ts @@ -154,7 +154,7 @@ describe(TopUpCustodialDeploymentsService.name, () => { jest.spyOn(drainingDeploymentService, "calculateTopUpAmount").mockImplementation(async () => faker.number.int({ min: 3500000, max: 4000000 })); it("should top up draining deployment given owners have sufficient grants and balances", async () => { - await topUpDeploymentsService.topUpDeployments(); + await topUpDeploymentsService.topUpDeployments({ dryRun: false }); expect(uaktMasterSigningClientService.executeTx).toHaveBeenCalledTimes(3); expect(usdtMasterSigningClientService.executeTx).toHaveBeenCalledTimes(2); diff --git a/apps/api/src/deployment/services/top-up-custodial-deployments/top-up-custodial-deployments.service.ts b/apps/api/src/deployment/services/top-up-custodial-deployments/top-up-custodial-deployments.service.ts index dddc43956..10c2988b6 100644 --- a/apps/api/src/deployment/services/top-up-custodial-deployments/top-up-custodial-deployments.service.ts +++ b/apps/api/src/deployment/services/top-up-custodial-deployments/top-up-custodial-deployments.service.ts @@ -6,6 +6,7 @@ import { ExecDepositDeploymentMsgOptions, MasterSigningClientService, RpcMessage import { ErrorService } from "@src/core/services/error/error.service"; import { DrainingDeploymentService } from "@src/deployment/services/draining-deployment/draining-deployment.service"; import { TopUpToolsService } from "@src/deployment/services/top-up-tools/top-up-tools.service"; +import { DeploymentsRefiller, TopUpDeploymentsOptions } from "@src/deployment/types/deployments-refiller"; interface Balances { denom: string; @@ -15,7 +16,7 @@ interface Balances { } @singleton() -export class TopUpCustodialDeploymentsService { +export class TopUpCustodialDeploymentsService implements DeploymentsRefiller { private readonly CONCURRENCY = 10; private readonly MIN_FEES_AVAILABLE = 5000; @@ -31,13 +32,13 @@ export class TopUpCustodialDeploymentsService { private readonly errorService: ErrorService ) {} - async topUpDeployments() { + async topUpDeployments(options: TopUpDeploymentsOptions) { const topUpAllCustodialDeployments = this.topUpToolsService.pairs.map(async ({ wallet, client }) => { const address = await wallet.getFirstAddress(); await this.allowanceHttpService.paginateDeploymentGrants({ grantee: address, limit: this.CONCURRENCY }, async grants => { await Promise.all( grants.map(async grant => { - await this.errorService.execWithErrorHandler({ grant, event: "TOP_UP_ERROR" }, () => this.topUpForGrant(grant, client)); + await this.errorService.execWithErrorHandler({ grant, event: "TOP_UP_ERROR" }, () => this.topUpForGrant(grant, client, options)); }) ); }); @@ -45,7 +46,7 @@ export class TopUpCustodialDeploymentsService { await Promise.all(topUpAllCustodialDeployments); } - private async topUpForGrant(grant: DeploymentAllowance, client: MasterSigningClientService) { + private async topUpForGrant(grant: DeploymentAllowance, client: MasterSigningClientService, options: TopUpDeploymentsOptions) { const owner = grant.granter; const { grantee } = grant; @@ -72,7 +73,8 @@ export class TopUpCustodialDeploymentsService { owner, grantee }, - client + client, + options ); } } @@ -104,12 +106,13 @@ export class TopUpCustodialDeploymentsService { return balances.deploymentLimit > amount && balances.feesLimit > this.MIN_FEES_AVAILABLE && balances.balance > amount + this.MIN_FEES_AVAILABLE; } - async topUpDeployment({ grantee, ...messageInput }: ExecDepositDeploymentMsgOptions, client: MasterSigningClientService) { + async topUpDeployment({ grantee, ...messageInput }: ExecDepositDeploymentMsgOptions, client: MasterSigningClientService, options: TopUpDeploymentsOptions) { const message = this.rpcClientService.getExecDepositDeploymentMsg({ grantee, ...messageInput }); - this.logger.info({ event: "TOP_UP_DEPLOYMENT", params: { ...messageInput, masterWallet: grantee } }); + this.logger.info({ event: "TOP_UP_DEPLOYMENT", params: { ...messageInput, masterWallet: grantee }, dryRun: options.dryRun }); - await client.executeTx([message]); - - this.logger.info({ event: "TOP_UP_DEPLOYMENT_SUCCESS" }); + if (!options.dryRun) { + await client.executeTx([message]); + this.logger.info({ event: "TOP_UP_DEPLOYMENT_SUCCESS" }); + } } } diff --git a/apps/api/src/deployment/services/top-up-managed-deployments/top-up-managed-deployments.service.spec.ts b/apps/api/src/deployment/services/top-up-managed-deployments/top-up-managed-deployments.service.spec.ts index 362d3f086..7bb8da8c1 100644 --- a/apps/api/src/deployment/services/top-up-managed-deployments/top-up-managed-deployments.service.spec.ts +++ b/apps/api/src/deployment/services/top-up-managed-deployments/top-up-managed-deployments.service.spec.ts @@ -98,7 +98,7 @@ describe(TopUpManagedDeploymentsService.name, () => { }); it("should top up draining deployment given owners have sufficient balances", async () => { - await topUpDeploymentsService.topUpDeployments(); + await topUpDeploymentsService.topUpDeployments({ dryRun: false }); let count = 0; diff --git a/apps/api/src/deployment/services/top-up-managed-deployments/top-up-managed-deployments.service.ts b/apps/api/src/deployment/services/top-up-managed-deployments/top-up-managed-deployments.service.ts index 92d51250f..2371ff16b 100644 --- a/apps/api/src/deployment/services/top-up-managed-deployments/top-up-managed-deployments.service.ts +++ b/apps/api/src/deployment/services/top-up-managed-deployments/top-up-managed-deployments.service.ts @@ -9,9 +9,10 @@ import { BalancesService } from "@src/billing/services/balances/balances.service import { TxSignerService } from "@src/billing/services/tx-signer/tx-signer.service"; import { ErrorService } from "@src/core/services/error/error.service"; import { DrainingDeploymentService } from "@src/deployment/services/draining-deployment/draining-deployment.service"; +import { DeploymentsRefiller, TopUpDeploymentsOptions } from "@src/deployment/types/deployments-refiller"; @singleton() -export class TopUpManagedDeploymentsService { +export class TopUpManagedDeploymentsService implements DeploymentsRefiller { private readonly CONCURRENCY = 10; private readonly logger = new LoggerService({ context: TopUpManagedDeploymentsService.name }); @@ -27,17 +28,17 @@ export class TopUpManagedDeploymentsService { private readonly errorService: ErrorService ) {} - async topUpDeployments() { + async topUpDeployments(options: TopUpDeploymentsOptions) { await this.userWalletRepository.paginate({ limit: this.CONCURRENCY }, async wallets => { await Promise.all( wallets.map(async wallet => { - await this.errorService.execWithErrorHandler({ wallet, event: "TOP_UP_ERROR" }, () => this.topUpForWallet(wallet)); + await this.errorService.execWithErrorHandler({ wallet, event: "TOP_UP_ERROR" }, () => this.topUpForWallet(wallet, options)); }) ); }); } - private async topUpForWallet(wallet: UserWalletOutput) { + private async topUpForWallet(wallet: UserWalletOutput, options: TopUpDeploymentsOptions) { const owner = wallet.address; const denom = this.billingConfig.DEPLOYMENT_GRANT_DENOM; const drainingDeployments = await this.drainingDeploymentService.findDeployments(owner, denom); @@ -55,11 +56,12 @@ export class TopUpManagedDeploymentsService { balance -= amount; const messageInput = { dseq: deployment.dseq, amount, denom, owner, depositor }; const message = this.rpcClientService.getDepositDeploymentMsg(messageInput); - this.logger.info({ event: "TOP_UP_DEPLOYMENT", params: messageInput }); + this.logger.info({ event: "TOP_UP_DEPLOYMENT", params: messageInput, dryRun: options.dryRun }); - await signer.signAndBroadcast([message]); - - this.logger.info({ event: "TOP_UP_SUCCESS" }); + if (!options.dryRun) { + await signer.signAndBroadcast([message]); + this.logger.info({ event: "TOP_UP_SUCCESS" }); + } } } } diff --git a/apps/api/src/deployment/types/deployments-refiller.ts b/apps/api/src/deployment/types/deployments-refiller.ts new file mode 100644 index 000000000..cddc764dd --- /dev/null +++ b/apps/api/src/deployment/types/deployments-refiller.ts @@ -0,0 +1,7 @@ +export interface TopUpDeploymentsOptions { + dryRun: boolean; +} + +export interface DeploymentsRefiller { + topUpDeployments(options: TopUpDeploymentsOptions): Promise; +}