-
Notifications
You must be signed in to change notification settings - Fork 55
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(deployment): implements custodial wallet balances collection for…
… top up Also assembles rough service interface for further development refs #395
- Loading branch information
1 parent
77644e7
commit 56fd330
Showing
14 changed files
with
247 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,8 @@ | ||
import { AllowanceHttpService } from "@akashnetwork/http-sdk"; | ||
import { AllowanceHttpService, BalanceHttpService } from "@akashnetwork/http-sdk"; | ||
import { container } from "tsyringe"; | ||
|
||
import { apiNodeUrl } from "@src/utils/constants"; | ||
|
||
container.register(AllowanceHttpService, { useValue: new AllowanceHttpService({ baseURL: apiNodeUrl }) }); | ||
const SERVICES = [BalanceHttpService, AllowanceHttpService]; | ||
|
||
SERVICES.forEach(Service => container.register(Service, { useValue: new Service({ baseURL: apiNodeUrl }) })); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
32 changes: 32 additions & 0 deletions
32
apps/api/src/deployment/repositories/lease/lease.repository.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import { Lease } from "@akashnetwork/database/dbSchemas/akash"; | ||
import { col, fn, Op } from "sequelize"; | ||
import { singleton } from "tsyringe"; | ||
|
||
interface DrainingLeasesOptions { | ||
closureHeight: number; | ||
owner: string; | ||
} | ||
|
||
export interface DrainingDeploymentOutput { | ||
dseq: number; | ||
denom: string; | ||
blockRate: number; | ||
} | ||
|
||
@singleton() | ||
export class LeaseRepository { | ||
async findDrainingLeases(options: DrainingLeasesOptions): Promise<DrainingDeploymentOutput[]> { | ||
return (await Lease.findAll({ | ||
where: { | ||
closedHeight: null, | ||
owner: options.owner, | ||
predictedClosedHeight: { | ||
[Op.lte]: options.closureHeight | ||
} | ||
}, | ||
attributes: ["dseq", "denom", [fn("sum", col("price")), "blockRate"]], | ||
group: ["dseq", "denom"], | ||
plain: true | ||
})) as unknown as DrainingDeploymentOutput[]; | ||
} | ||
} |
125 changes: 124 additions & 1 deletion
125
apps/api/src/deployment/services/top-up-deployments/top-up-deployments.service.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,135 @@ | ||
import { AllowanceHttpService, BalanceHttpService, DeploymentAllowance } from "@akashnetwork/http-sdk"; | ||
import { PromisePool } from "@supercharge/promise-pool"; | ||
import { singleton } from "tsyringe"; | ||
|
||
import { InjectWallet } from "@src/billing/providers/wallet.provider"; | ||
import { UserWalletOutput, UserWalletRepository } from "@src/billing/repositories"; | ||
import { MasterWalletService } from "@src/billing/services"; | ||
import { LoggerService } from "@src/core"; | ||
import { DrainingDeploymentOutput, LeaseRepository } from "@src/deployment/repositories/lease/lease.repository"; | ||
|
||
interface Balances { | ||
denom: string; | ||
feesLimit: number; | ||
deploymentLimit: number; | ||
balance: number; | ||
isManaged: boolean; | ||
} | ||
|
||
@singleton() | ||
export class TopUpDeploymentsService { | ||
private readonly CONCURRENCY = 10; | ||
|
||
private readonly logger = new LoggerService({ context: TopUpDeploymentsService.name }); | ||
|
||
constructor( | ||
private readonly userWalletRepository: UserWalletRepository, | ||
private readonly allowanceHttpService: AllowanceHttpService, | ||
private readonly balanceHttpService: BalanceHttpService, | ||
@InjectWallet("MANAGED") private readonly managedMasterWalletService: MasterWalletService, | ||
@InjectWallet("UAKT_TOP_UP") private readonly uaktMasterWalletService: MasterWalletService, | ||
@InjectWallet("USDC_TOP_UP") private readonly usdtMasterWalletService: MasterWalletService, | ||
private readonly leaseRepository: LeaseRepository | ||
) {} | ||
|
||
async topUpDeployments() { | ||
this.logger.warn("Top up deployments not implemented"); | ||
const wallets = [this.uaktMasterWalletService, this.usdtMasterWalletService]; | ||
|
||
const topUpAllManagedDeployments = wallets.map(async wallet => { | ||
const address = await wallet.getFirstAddress(); | ||
await this.allowanceHttpService.paginateDeploymentGrantsForGrantee(address, async grants => { | ||
await PromisePool.withConcurrency(this.CONCURRENCY) | ||
.for(grants) | ||
.process(async grant => this.topUpForGrant(grant)); | ||
}); | ||
}); | ||
await Promise.all(topUpAllManagedDeployments); | ||
|
||
await this.paginateManagedWallets(async userWallets => { | ||
await Promise.all(userWallets.map(async userWallet => this.topUpForManagedWallet(userWallet))); | ||
}); | ||
} | ||
|
||
private async topUpForGrant(grant: DeploymentAllowance) { | ||
const balances = await this.collectCustodialWalletBalances(grant); | ||
const owner = grant.granter; | ||
this.logger.debug({ event: "BALANCES_COLLECTED", granter: owner, grantee: grant.grantee, balances }); | ||
|
||
const drainingDeployments = await this.retrieveDrainingDeployments(owner); | ||
|
||
drainingDeployments.map(async deployment => { | ||
const topUpAmount = await this.calculateTopUpAmount(deployment); | ||
this.validateTopUpAmount(topUpAmount, balances); | ||
}); | ||
} | ||
|
||
private async collectCustodialWalletBalances(grant: DeploymentAllowance): Promise<Balances> { | ||
const denom = grant.authorization.spend_limit.denom; | ||
const deploymentLimit = parseFloat(grant.authorization.spend_limit.amount); | ||
|
||
const feesAllowance = await this.allowanceHttpService.getFeeAllowanceForGranterAndGrantee(grant.granter, grant.grantee); | ||
const feesSpendLimit = feesAllowance.allowance.spend_limit.find(limit => limit.denom === denom); | ||
const feesLimit = feesSpendLimit ? parseFloat(feesSpendLimit.amount) : 0; | ||
|
||
const { amount } = await this.balanceHttpService.getBalance(grant.granter, "uakt"); | ||
const balance = parseFloat(amount); | ||
|
||
return { | ||
denom, | ||
feesLimit: feesLimit, | ||
deploymentLimit, | ||
balance, | ||
isManaged: false | ||
}; | ||
} | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
private async paginateManagedWallets(cb: (page: UserWalletOutput[]) => Promise<void>) { | ||
this.logger.debug({ event: "PAGINATING_MANAGED_WALLETS", warning: "Not implemented yet" }); | ||
} | ||
|
||
private async topUpForManagedWallet(userWallet: UserWalletOutput) { | ||
const balances = await this.collectManagedWalletBalances(userWallet); | ||
this.logger.debug({ event: "BALANCES_COLLECTED", wallet: userWallet, balances }); | ||
|
||
const drainingDeployments = await this.retrieveDrainingDeployments(userWallet.address); | ||
|
||
drainingDeployments.map(async deployment => { | ||
const topUpAmount = await this.calculateTopUpAmount(deployment); | ||
this.validateTopUpAmount(topUpAmount, balances); | ||
}); | ||
} | ||
|
||
private async collectManagedWalletBalances(userWallet: UserWalletOutput): Promise<Balances> { | ||
this.logger.debug({ event: "CALCULATING_MANAGE_WALLET_BALANCES", userWallet, warning: "Not implemented yet" }); | ||
return { | ||
denom: "usdc", | ||
feesLimit: 0, | ||
deploymentLimit: 0, | ||
balance: 0, | ||
isManaged: true | ||
}; | ||
} | ||
|
||
private async retrieveDrainingDeployments(owner: string): Promise<DrainingDeploymentOutput[]> { | ||
this.logger.debug({ event: "RETRIEVING_DRAINING_DEPLOYMENTS", owner, warning: "Not implemented yet" }); | ||
return []; | ||
} | ||
|
||
private async calculateTopUpAmount(deployment: DrainingDeploymentOutput): Promise<number> { | ||
this.logger.debug({ event: "CALCULATING_TOP_UP_AMOUNT", deployment, warning: "Not implemented yet" }); | ||
return 0; | ||
} | ||
|
||
private validateTopUpAmount(amount: number, balances: Balances) { | ||
this.logger.debug({ event: "VALIDATING_TOP_UP_AMOUNT", amount, balances, warning: "Not implemented yet" }); | ||
} | ||
|
||
private async topUpCustodialDeployment() { | ||
this.logger.debug({ event: "TOPPING_UP_CUSTODIAL_DEPLOYMENT", warning: "Not implemented yet" }); | ||
} | ||
|
||
private async topUpManagedDeployment() { | ||
this.logger.debug({ event: "TOPPING_UP_MANAGED_DEPLOYMENT", warning: "Not implemented yet" }); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.