-
Notifications
You must be signed in to change notification settings - Fork 57
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 deployments top up data collec…
…tion refs #39
- Loading branch information
1 parent
8876b5c
commit 9b4a461
Showing
39 changed files
with
574 additions
and
169 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
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,25 @@ | ||
AKASH_SANDBOX_DATABASE_CS=postgres://postgres:password@localhost:5432/console-akash-sandbox | ||
USER_DATABASE_CS=postgres://postgres:password@localhost:5432/console-users | ||
POSTGRES_DB_URI=postgres://postgres:password@localhost:5432/console-users | ||
MASTER_WALLET_MNEMONIC="motion isolate mother convince snack twenty tumble boost elbow bundle modify balcony" | ||
UAKT_TOP_UP_MASTER_WALLET_MNEMONIC="since bread kind field rookie stairs elephant tent horror rice gain tongue collect goose rural garment cover client biology toe ability boat afford mind" | ||
USDC_TOP_UP_MASTER_WALLET_MNEMONIC="leaf brush weapon puppy depart hockey walnut hospital orphan require unfair hunt ribbon toe cereal eagle hour door awesome dress mouse when phone return" | ||
NETWORK=sandbox | ||
RPC_NODE_ENDPOINT=https://rpc.sandbox-01.aksh.pw:443 | ||
TRIAL_DEPLOYMENT_ALLOWANCE_AMOUNT=20000000 | ||
DEPLOYMENT_ALLOWANCE_REFILL_AMOUNT=20000000 | ||
DEPLOYMENT_ALLOWANCE_REFILL_THRESHOLD=2000000 | ||
TRIAL_FEES_ALLOWANCE_AMOUNT=5000000 | ||
FEE_ALLOWANCE_REFILL_AMOUNT=5000000 | ||
FEE_ALLOWANCE_REFILL_THRESHOLD=500000 | ||
DEPLOYMENT_GRANT_DENOM=uakt | ||
LOG_LEVEL=debug | ||
BILLING_ENABLED=true | ||
ANONYMOUS_USER_TOKEN_SECRET=ANONYMOUS_USER_TOKEN_SECRET | ||
STRIPE_SECRET_KEY=STRIPE_SECRET_KEY | ||
STRIPE_PRICE_ID=STRIPE_PRICE_ID | ||
STRIPE_WEBHOOK_SECRET=STRIPE_WEBHOOK_SECRET | ||
ALLOWED_CHECKOUT_REFERRERS=["http://localhost:3000"] | ||
STRIPE_CHECKOUT_REDIRECT_URL=http://localhost:3000 | ||
STD_OUT_LOG_FORMAT=pretty | ||
SQL_LOG_FORMAT=pretty |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,4 @@ | ||
import "./config.provider"; | ||
import "./http-sdk.provider"; | ||
import "./wallet.provider"; | ||
|
||
export * from "./config.provider"; |
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
4 changes: 2 additions & 2 deletions
4
...rc/billing/providers/http-sdk.provider.ts → ...i/src/core/providers/http-sdk.provider.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,8 +1,8 @@ | ||
import { AllowanceHttpService, BalanceHttpService } from "@akashnetwork/http-sdk"; | ||
import { AllowanceHttpService, BalanceHttpService, BlockHttpService } from "@akashnetwork/http-sdk"; | ||
import { container } from "tsyringe"; | ||
|
||
import { apiNodeUrl } from "@src/utils/constants"; | ||
|
||
const SERVICES = [BalanceHttpService, AllowanceHttpService]; | ||
const SERVICES = [BalanceHttpService, AllowanceHttpService, BlockHttpService]; | ||
|
||
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
export * from "./postgres.provider"; | ||
export * from "./config.provider"; | ||
import "./http-sdk.provider"; |
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,11 @@ | ||
import { container, inject } from "tsyringe"; | ||
|
||
import { config } from "@src/deployment/config"; | ||
|
||
export const DEPLOYMENT_CONFIG = "DEPLOYMENT_CONFIG"; | ||
|
||
container.register(DEPLOYMENT_CONFIG, { useValue: config }); | ||
|
||
export type DeploymentConfig = typeof config; | ||
|
||
export const InjectDeploymentConfig = () => inject(DEPLOYMENT_CONFIG); |
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,8 @@ | ||
import { z } from "zod"; | ||
|
||
const envSchema = z.object({ | ||
AUTO_TOP_UP_JOB_INTERVAL_IN_H: z.number({ coerce: true }).optional().default(1), | ||
AUTO_TOP_UP_DEPLOYMENT_INTERVAL_IN_DAYS: z.number({ coerce: true }).optional().default(7) | ||
}); | ||
|
||
export const envConfig = envSchema.parse(process.env); |
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 @@ | ||
export { envConfig as config } from "./env.config"; |
4 changes: 2 additions & 2 deletions
4
apps/api/src/deployment/controllers/deployment/deployment.controller.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
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
174 changes: 174 additions & 0 deletions
174
...oyment/services/top-up-custodial-deployments/top-up-custodial-deployments.service.spec.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,174 @@ | ||
import { AllowanceHttpService, BalanceHttpService, BlockHttpService, Denom } from "@akashnetwork/http-sdk"; | ||
import { container } from "tsyringe"; | ||
|
||
import { MasterSigningClientService, MasterWalletService } from "@src/billing/services"; | ||
import type { Sentry } from "@src/core/providers/sentry.provider"; | ||
import { SentryEventService } from "@src/core/services/sentry-event/sentry-event.service"; | ||
import { config } from "@src/deployment/config"; | ||
import { DrainingLeasesOptions, LeaseRepository } from "@src/deployment/repositories/lease/lease.repository"; | ||
import { TopUpCustodialDeploymentsService } from "./top-up-custodial-deployments.service"; | ||
|
||
import { AkashAddressSeeder } from "@test/seeders/akash-address.seeder"; | ||
import { BalanceSeeder } from "@test/seeders/balance.seeder"; | ||
import { DeploymentGrantSeeder } from "@test/seeders/deployment-grant.seeder"; | ||
import { DrainingDeploymentSeeder } from "@test/seeders/draining-deployment.seeder"; | ||
import { FeesAuthorizationSeeder } from "@test/seeders/fees-authorization.seeder"; | ||
|
||
describe(TopUpCustodialDeploymentsService.name, () => { | ||
const CURRENT_BLOCK_HEIGHT = 7481457; | ||
const UAKT_TOP_UP_MASTER_WALLET_ADDRESS = AkashAddressSeeder.create(); | ||
const USDT_TOP_UP_MASTER_WALLET_ADDRESS = AkashAddressSeeder.create(); | ||
const mockManagedWalletService = (address: string) => { | ||
return { | ||
getFirstAddress: async () => address | ||
} as unknown as MasterWalletService; | ||
}; | ||
const mockMasterSigningClientService = () => { | ||
return { | ||
execTx: jest.fn() | ||
} as unknown as MasterSigningClientService; | ||
}; | ||
|
||
const allowanceHttpService = new AllowanceHttpService(); | ||
const balanceHttpService = new BalanceHttpService(); | ||
const blockHttpService = new BlockHttpService(); | ||
const uaktMasterWalletService = mockManagedWalletService(UAKT_TOP_UP_MASTER_WALLET_ADDRESS); | ||
const usdtMasterWalletService = mockManagedWalletService(USDT_TOP_UP_MASTER_WALLET_ADDRESS); | ||
const uaktMasterSigningClientService = mockMasterSigningClientService(); | ||
const usdtMasterSigningClientService = mockMasterSigningClientService(); | ||
|
||
jest.spyOn(blockHttpService, "getCurrentHeight").mockResolvedValue(CURRENT_BLOCK_HEIGHT); | ||
|
||
const leaseRepository = container.resolve(LeaseRepository); | ||
jest.spyOn(leaseRepository, "findDrainingLeases").mockResolvedValue([]); | ||
const sentryEventService = container.resolve(SentryEventService); | ||
const sentry = { | ||
captureEvent: jest.fn() | ||
} as unknown as Sentry; | ||
const topUpDeploymentsService = new TopUpCustodialDeploymentsService( | ||
allowanceHttpService, | ||
balanceHttpService, | ||
blockHttpService, | ||
uaktMasterWalletService, | ||
usdtMasterWalletService, | ||
uaktMasterSigningClientService, | ||
usdtMasterSigningClientService, | ||
leaseRepository, | ||
config, | ||
sentry, | ||
sentryEventService | ||
); | ||
|
||
type SeedParams = { | ||
denom: Denom; | ||
balance?: string; | ||
grantee: string; | ||
expectedDeploymentsTopUpCount?: 0 | 1 | 2; | ||
hasDeployments?: boolean; | ||
client: MasterSigningClientService; | ||
}; | ||
|
||
const seedFor = ({ denom, balance = "100000000", grantee, expectedDeploymentsTopUpCount = 2, hasDeployments = true, client }: SeedParams) => { | ||
const owner = AkashAddressSeeder.create(); | ||
|
||
return { | ||
balance: BalanceSeeder.create({ denom, amount: balance }), | ||
grant: DeploymentGrantSeeder.create({ | ||
granter: owner, | ||
grantee: grantee, | ||
authorization: { spend_limit: { denom, amount: "100000000" } } | ||
}), | ||
feeAllowance: FeesAuthorizationSeeder.create({ | ||
granter: owner, | ||
grantee: grantee, | ||
allowance: { spend_limit: { denom } } | ||
}), | ||
drainingDeployments: hasDeployments | ||
? [ | ||
{ | ||
deployment: DrainingDeploymentSeeder.create({ denom, blockRate: 50, predictedClosedHeight: CURRENT_BLOCK_HEIGHT + 1500 }), | ||
expectedTopUpAmount: expectedDeploymentsTopUpCount ? 4897959 : undefined | ||
}, | ||
{ | ||
deployment: DrainingDeploymentSeeder.create({ denom, blockRate: 45, predictedClosedHeight: CURRENT_BLOCK_HEIGHT + 1700 }), | ||
expectedTopUpAmount: expectedDeploymentsTopUpCount > 1 ? 4408163 : undefined | ||
} | ||
] | ||
: [], | ||
client: client | ||
}; | ||
}; | ||
|
||
const data = [ | ||
seedFor({ | ||
denom: "uakt", | ||
grantee: UAKT_TOP_UP_MASTER_WALLET_ADDRESS, | ||
client: uaktMasterSigningClientService | ||
}), | ||
seedFor({ | ||
denom: "ibc/170C677610AC31DF0904FFE09CD3B5C657492170E7E52372E48756B71E56F2F1", | ||
grantee: USDT_TOP_UP_MASTER_WALLET_ADDRESS, | ||
client: usdtMasterSigningClientService | ||
}), | ||
seedFor({ | ||
denom: "uakt", | ||
balance: "5500000", | ||
grantee: UAKT_TOP_UP_MASTER_WALLET_ADDRESS, | ||
expectedDeploymentsTopUpCount: 1, | ||
client: uaktMasterSigningClientService | ||
}), | ||
seedFor({ | ||
denom: "uakt", | ||
balance: "5500000", | ||
grantee: UAKT_TOP_UP_MASTER_WALLET_ADDRESS, | ||
hasDeployments: false, | ||
client: uaktMasterSigningClientService | ||
}), | ||
seedFor({ | ||
denom: "uakt", | ||
balance: "0", | ||
grantee: UAKT_TOP_UP_MASTER_WALLET_ADDRESS, | ||
expectedDeploymentsTopUpCount: 0, | ||
client: uaktMasterSigningClientService | ||
}) | ||
]; | ||
|
||
jest.spyOn(allowanceHttpService, "paginateDeploymentGrants").mockImplementation(async (params, cb) => { | ||
return await cb(data.filter(({ grant }) => "grantee" in params && grant.grantee === params.grantee).map(({ grant }) => grant)); | ||
}); | ||
jest.spyOn(allowanceHttpService, "getFeeAllowanceForGranterAndGrantee").mockImplementation(async (granter: string, grantee: string) => { | ||
return data.find(({ grant }) => grant.granter === granter && grant.grantee === grantee)?.feeAllowance; | ||
}); | ||
jest.spyOn(balanceHttpService, "getBalance").mockImplementation(async (address: string, denom: Denom) => { | ||
return ( | ||
data.find(({ grant }) => grant.granter === address)?.balance || { | ||
amount: "0", | ||
denom | ||
} | ||
); | ||
}); | ||
jest.spyOn(leaseRepository, "findDrainingLeases").mockImplementation(async ({ owner, denom }: DrainingLeasesOptions) => { | ||
return ( | ||
data | ||
.find(({ grant }) => grant.granter === owner && grant.authorization.spend_limit.denom === denom) | ||
?.drainingDeployments?.map(({ deployment }) => deployment) || [] | ||
); | ||
}); | ||
jest.spyOn(topUpDeploymentsService, "topUpDeployment"); | ||
|
||
console.log("DEBUG data", JSON.stringify(data, null, 2)); | ||
|
||
it("should top up draining deployment given owners have sufficient grants and balances", async () => { | ||
await topUpDeploymentsService.topUpDeployments(); | ||
|
||
expect(topUpDeploymentsService.topUpDeployment).toHaveBeenCalledTimes(5); | ||
|
||
data.forEach(({ drainingDeployments, client }) => { | ||
drainingDeployments.forEach(({ expectedTopUpAmount, deployment }) => { | ||
if (expectedTopUpAmount) { | ||
expect(topUpDeploymentsService.topUpDeployment).toHaveBeenCalledWith(expectedTopUpAmount, deployment, client); | ||
} | ||
}); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.