Skip to content

Commit 3e5a4fa

Browse files
committed
refactor: improves encapsulation of app
1 parent fc9c42c commit 3e5a4fa

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+276
-231
lines changed

apps/api/env/.env.mainnet

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
RPC_NODE_ENDPOINT=https://consolerpc.akashnet.net
2+
REST_API_NODE_URL=https://consoleapi.akashnet.net
23
DEPLOYMENT_GRANT_DENOM=ibc/170C677610AC31DF0904FFE09CD3B5C657492170E7E52372E48756B71E56F2F1

apps/api/env/.env.sandbox

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
RPC_NODE_ENDPOINT=https://rpc.sandbox-2.aksh.pw:443
2-
DEPLOYMENT_GRANT_DENOM=uakt
2+
REST_API_NODE_URL=https://api.sandbox-2.aksh.pw:443
3+
DEPLOYMENT_GRANT_DENOM=uakt

apps/api/src/billing/lib/wallet/wallet.ts

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,63 +4,60 @@ import type { DirectSignResponse, OfflineDirectSigner } from "@cosmjs/proto-sign
44
import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing";
55
import type { DirectSecp256k1HdWalletOptions } from "@cosmjs/proto-signing/build/directsecp256k1hdwallet";
66

7+
export const WALLET_ADDRESS_PREFIX = "akash";
8+
79
export class Wallet implements OfflineDirectSigner {
810
static create(mnemonic?: string, index?: number): Wallet {
911
return new Wallet(mnemonic, index);
1012
}
1113

12-
private readonly PREFIX = "akash";
13-
14-
private readonly instanceAsPromised: Promise<DirectSecp256k1HdWallet>;
15-
private aminoSignerPromise?: Promise<OfflineAminoSigner>;
16-
private readonly walletIndex?: number;
14+
readonly #instanceAsPromised: Promise<DirectSecp256k1HdWallet>;
15+
#aminoSignerPromise?: Promise<OfflineAminoSigner>;
16+
#walletIndex?: number;
1717

1818
constructor(mnemonic?: string, index?: number) {
19-
this.walletIndex = index;
19+
this.#walletIndex = index;
2020
if (typeof mnemonic === "undefined") {
21-
this.instanceAsPromised = DirectSecp256k1HdWallet.generate(24, this.getInstanceOptions(index));
21+
this.#instanceAsPromised = DirectSecp256k1HdWallet.generate(24, this.getInstanceOptions(index));
2222
} else {
23-
this.instanceAsPromised = DirectSecp256k1HdWallet.fromMnemonic(mnemonic, this.getInstanceOptions(index));
23+
this.#instanceAsPromised = DirectSecp256k1HdWallet.fromMnemonic(mnemonic, this.getInstanceOptions(index));
2424
}
2525
}
2626

2727
private getInstanceOptions(index?: number): Partial<DirectSecp256k1HdWalletOptions> {
2828
if (typeof index === "undefined") {
29-
return { prefix: this.PREFIX };
29+
return { prefix: WALLET_ADDRESS_PREFIX };
3030
}
3131

3232
return {
33-
prefix: this.PREFIX,
33+
prefix: WALLET_ADDRESS_PREFIX,
3434
hdPaths: [makeCosmoshubPath(index)]
3535
};
3636
}
3737

3838
async getAccounts() {
39-
return (await this.instanceAsPromised).getAccounts();
39+
return (await this.#instanceAsPromised).getAccounts();
4040
}
4141

4242
async signDirect(...args: Parameters<DirectSecp256k1HdWallet["signDirect"]>): Promise<DirectSignResponse> {
43-
return (await this.instanceAsPromised).signDirect(...args);
43+
return (await this.#instanceAsPromised).signDirect(...args);
4444
}
4545

4646
async signAmino(address: string, data: StdSignDoc): Promise<AminoSignResponse> {
47-
const wallet = await this.instanceAsPromised;
48-
this.aminoSignerPromise ??= Secp256k1HdWallet.fromMnemonic(wallet.mnemonic, this.getInstanceOptions(this.walletIndex));
49-
const aminoSigner = await this.aminoSignerPromise;
47+
const wallet = await this.#instanceAsPromised;
48+
this.#aminoSignerPromise ??= Secp256k1HdWallet.fromMnemonic(wallet.mnemonic, this.getInstanceOptions(this.#walletIndex));
49+
const aminoSigner = await this.#aminoSignerPromise;
5050
return aminoSigner.signAmino(address, data);
5151
}
5252

53-
async getFirstAddress() {
53+
async getFirstAddress(): Promise<string> {
5454
const accounts = await this.getAccounts();
5555
return accounts[0].address;
5656
}
5757

58-
async getMnemonic() {
59-
return (await this.instanceAsPromised).mnemonic;
60-
}
61-
62-
async getInstance() {
63-
return await this.instanceAsPromised;
58+
async createDerivedWallet(index: number): Promise<Wallet> {
59+
const instance = await this.#instanceAsPromised;
60+
return new Wallet(instance.mnemonic, index);
6461
}
6562
}
6663

apps/api/src/billing/services/financial-stats/financial-stats.service.ts

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,29 @@
11
import { Provider } from "@akashnetwork/database/dbSchemas/akash";
2-
import { CosmosDistributionCommunityPoolResponse, CosmosHttpService } from "@akashnetwork/http-sdk";
3-
import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing";
4-
import axios from "axios";
2+
import { CosmosHttpService } from "@akashnetwork/http-sdk";
53
import { Op, QueryTypes } from "sequelize";
6-
import { singleton } from "tsyringe";
4+
import { inject, singleton } from "tsyringe";
75

86
import { USDC_IBC_DENOMS } from "@src/billing/config/network.config";
9-
import { type BillingConfig, InjectBillingConfig } from "@src/billing/providers";
7+
import type { Wallet } from "@src/billing/lib/wallet/wallet";
8+
import { MANAGED_MASTER_WALLET } from "@src/billing/providers/wallet.provider";
109
import { UserWalletRepository } from "@src/billing/repositories";
1110
import { chainDb } from "@src/db/dbConnection";
12-
import { apiNodeUrl } from "@src/utils/constants";
1311

1412
@singleton()
1513
export class FinancialStatsService {
1614
constructor(
17-
@InjectBillingConfig() private readonly config: BillingConfig,
1815
private readonly userWalletRepository: UserWalletRepository,
19-
private readonly cosmosHttpService: CosmosHttpService
16+
private readonly cosmosHttpService: CosmosHttpService,
17+
@inject(MANAGED_MASTER_WALLET) private readonly managedMasterWallet: Wallet
2018
) {}
2119

2220
async getPayingUserCount() {
2321
return this.userWalletRepository.payingUserCount();
2422
}
2523

26-
async getMasterWalletBalanceUsdc() {
27-
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(this.config.MASTER_WALLET_MNEMONIC, { prefix: "akash" });
28-
const [account] = await wallet.getAccounts();
29-
30-
return this.getWalletBalances(account.address, USDC_IBC_DENOMS.mainnetId);
24+
async getMasterWalletBalanceUsdc(): Promise<number> {
25+
const address = await this.managedMasterWallet.getFirstAddress();
26+
return this.getWalletBalances(address, USDC_IBC_DENOMS.mainnetId);
3127
}
3228

3329
private async getWalletBalances(address: string, denom: string) {
@@ -52,9 +48,9 @@ export class FinancialStatsService {
5248
return balances;
5349
}
5450

55-
async getCommunityPoolUsdc() {
56-
const communityPoolData = await axios.get<CosmosDistributionCommunityPoolResponse>(`${apiNodeUrl}/cosmos/distribution/v1beta1/community_pool`);
57-
return parseFloat(communityPoolData.data.pool.find(x => x.denom === USDC_IBC_DENOMS.mainnetId)?.amount || "0");
51+
async getCommunityPoolUsdc(): Promise<number> {
52+
const pool = await this.cosmosHttpService.getCommunityPool();
53+
return parseFloat(pool.find(x => x.denom === USDC_IBC_DENOMS.mainnetId)?.amount || "0");
5854
}
5955

6056
async getProviderRevenues() {

apps/api/src/billing/services/managed-user-wallet/managed-user-wallet.service.ts

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { AuthzHttpService } from "@akashnetwork/http-sdk";
22
import { LoggerService } from "@akashnetwork/logging";
3-
import { stringToPath } from "@cosmjs/crypto";
4-
import { DirectSecp256k1HdWallet, EncodeObject } from "@cosmjs/proto-signing";
3+
import { EncodeObject } from "@cosmjs/proto-signing";
54
import add from "date-fns/add";
65
import { singleton } from "tsyringe";
76

@@ -27,10 +26,6 @@ interface SpendingAuthorizationOptions {
2726

2827
@singleton()
2928
export class ManagedUserWalletService {
30-
private readonly PREFIX = "akash";
31-
32-
private readonly HD_PATH = "m/44'/118'/0'/0";
33-
3429
private readonly logger = LoggerService.forContext(ManagedUserWalletService.name);
3530

3631
constructor(
@@ -56,16 +51,12 @@ export class ManagedUserWalletService {
5651
return { address, limits };
5752
}
5853

59-
async createWallet({ addressIndex }: { addressIndex: number }) {
60-
const hdPath = stringToPath(`${this.HD_PATH}/${addressIndex}`);
61-
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(this.config.MASTER_WALLET_MNEMONIC, {
62-
prefix: this.PREFIX,
63-
hdPaths: [hdPath]
64-
});
65-
const [account] = await wallet.getAccounts();
66-
this.logger.debug({ event: "WALLET_CREATED", address: account.address });
54+
async createWallet(input: { addressIndex: number }): Promise<{ address: string }> {
55+
const wallet = await this.masterWallet.createDerivedWallet(input.addressIndex);
56+
const address = await wallet.getFirstAddress();
57+
this.logger.debug({ event: "WALLET_CREATED", address });
6758

68-
return { address: account.address };
59+
return { address };
6960
}
7061

7162
async authorizeSpending(options: SpendingAuthorizationOptions) {
Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
11
import "@test/mocks/logger-service.mock";
22

3-
import { BlockHttpService as BlockHttpServiceCommon } from "@akashnetwork/http-sdk";
3+
import type { BlockHttpService as BlockHttpServiceCommon } from "@akashnetwork/http-sdk";
44
import { faker } from "@faker-js/faker";
5+
import { mock } from "jest-mock-extended";
56

67
import { BlockHttpService } from "./block-http.service";
78

89
describe(BlockHttpService.name, () => {
9-
let service: BlockHttpService;
10-
let blockHttpService: BlockHttpServiceCommon;
11-
12-
beforeEach(() => {
13-
blockHttpService = new BlockHttpServiceCommon();
14-
service = new BlockHttpService(blockHttpService);
15-
});
16-
1710
it("should get current height", async () => {
11+
const { blockHttpService, service } = setup();
1812
const height = faker.number.int({ min: 1000000, max: 10000000 });
1913
jest.spyOn(blockHttpService, "getCurrentHeight").mockResolvedValue(height);
2014
const result = await service.getCurrentHeight();
2115

2216
expect(result).toBe(height);
2317
});
18+
19+
function setup() {
20+
const blockHttpService = mock<BlockHttpServiceCommon>();
21+
const service = new BlockHttpService(blockHttpService);
22+
return { blockHttpService, service };
23+
}
2424
});

apps/api/src/console.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,10 +119,7 @@ async function executeCliHandler(name: string, handler: () => Promise<unknown>,
119119
}
120120

121121
const shutdown = once(async () => {
122-
// eslint-disable-next-line @typescript-eslint/no-var-requires
123-
const { closeConnections } = require("./core/providers/postgres.provider");
124-
125-
await Promise.all([closeConnections(), chainDb.close(), container.dispose()]);
122+
await container.dispose();
126123
});
127124
process.on("SIGTERM", shutdown);
128125
process.on("SIGINT", shutdown);

apps/api/src/core/config/env.config.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { netConfig } from "@akashnetwork/net";
12
import { z } from "zod";
23

34
export const envSchema = z
@@ -23,7 +24,11 @@ export const envSchema = z
2324
FEATURE_FLAGS_ENABLE_ALL: z
2425
.string()
2526
.default("false")
26-
.transform(value => value === "true")
27+
.transform(value => value === "true"),
28+
REST_API_NODE_URL: z
29+
.string()
30+
.url()
31+
.default(() => netConfig.getBaseAPIUrl(process.env.NETWORK || "mainnet"))
2732
})
2833
.superRefine((value, ctx) => {
2934
if (!value.FEATURE_FLAGS_ENABLE_ALL && (!value.UNLEASH_SERVER_API_URL || !value.UNLEASH_SERVER_API_TOKEN)) {
@@ -34,4 +39,4 @@ export const envSchema = z
3439
}
3540
});
3641

37-
export const envConfig = envSchema.parse(process.env);
42+
export type CoreConfig = z.infer<typeof envSchema>;

apps/api/src/core/config/index.ts

Lines changed: 0 additions & 5 deletions
This file was deleted.
Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1-
import { container } from "tsyringe";
1+
import type { InjectionToken } from "tsyringe";
2+
import { container, instancePerContainerCachingFactory } from "tsyringe";
23

3-
import { config } from "@src/core/config";
4+
import type { CoreConfig } from "../config/env.config";
5+
import { envSchema } from "../config/env.config";
46

5-
export const CORE_CONFIG = "CORE_CONFIG";
7+
export const CORE_CONFIG: InjectionToken<CoreConfig> = "CORE_CONFIG";
68

7-
container.register(CORE_CONFIG, { useValue: config });
9+
container.register(CORE_CONFIG, {
10+
useFactory: instancePerContainerCachingFactory(() => envSchema.parse(process.env))
11+
});
812

9-
export type CoreConfig = typeof config;
13+
export type { CoreConfig };

0 commit comments

Comments
 (0)