Skip to content

Commit

Permalink
feat(wallet): implement multiple master wallets and clients
Browse files Browse the repository at this point in the history
So that those could be used separately for managed wallets, uakt and usdc top up

refs #395
  • Loading branch information
ygrishajev committed Oct 30, 2024
1 parent f29a37d commit 5ea00e4
Show file tree
Hide file tree
Showing 13 changed files with 71 additions and 22 deletions.
2 changes: 2 additions & 0 deletions apps/api/env/.env.functional.test
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ AKASH_SANDBOX_DATABASE_CS=postgres://postgres:password@localhost:5432/console-ak
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="motion isolate mother convince snack twenty tumble boost elbow bundle modify balcony"
USDC_TOP_UP_MASTER_WALLET_MNEMONIC="motion isolate mother convince snack twenty tumble boost elbow bundle modify balcony"
NETWORK=sandbox
RPC_NODE_ENDPOINT=https://rpc.sandbox-01.aksh.pw:443
TRIAL_DEPLOYMENT_ALLOWANCE_AMOUNT=20000000
Expand Down
2 changes: 2 additions & 0 deletions apps/api/src/billing/config/env.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { z } from "zod";

const envSchema = z.object({
MASTER_WALLET_MNEMONIC: z.string(),
UAKT_TOP_UP_MASTER_WALLET_MNEMONIC: z.string(),
USDC_TOP_UP_MASTER_WALLET_MNEMONIC: z.string(),
NETWORK: z.enum(["mainnet", "testnet", "sandbox"]),
RPC_NODE_ENDPOINT: z.string(),
TRIAL_ALLOWANCE_EXPIRATION_DAYS: z.number({ coerce: true }).default(14),
Expand Down
1 change: 1 addition & 0 deletions apps/api/src/billing/providers/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import "./config.provider";
import "./http-sdk.provider";
import "./wallet.provider";

export * from "./config.provider";
24 changes: 24 additions & 0 deletions apps/api/src/billing/providers/signing-client.provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { container, inject } from "tsyringe";

import { config } from "@src/billing/config";
import { TYPE_REGISTRY } from "@src/billing/providers/type-registry.provider";
import { MANAGED_MASTER_WALLET, UAKT_TOP_UP_MASTER_WALLET, USDC_TOP_UP_MASTER_WALLET } from "@src/billing/providers/wallet.provider";
import { MasterSigningClientService } from "@src/billing/services";
import { MasterWalletType } from "@src/billing/types/wallet.type";

export const MANAGED_MASTER_SIGNING_CLIENT = "MANAGED_MASTER_SIGNING_CLIENT";
container.register(MANAGED_MASTER_SIGNING_CLIENT, {
useFactory: c => new MasterSigningClientService(config, c.resolve(MANAGED_MASTER_WALLET), c.resolve(TYPE_REGISTRY), MANAGED_MASTER_SIGNING_CLIENT)
});

export const UAKT_TOP_UP_MASTER_SIGNING_CLIENT = "UAKT_TOP_UP_MASTER_SIGNING_CLIENT";
container.register(UAKT_TOP_UP_MASTER_SIGNING_CLIENT, {
useFactory: c => new MasterSigningClientService(config, c.resolve(UAKT_TOP_UP_MASTER_WALLET), c.resolve(TYPE_REGISTRY), UAKT_TOP_UP_MASTER_SIGNING_CLIENT)
});

export const USDC_TOP_UP_MASTER_SIGNING_CLIENT = "USDC_TOP_UP_MASTER_SIGNING_CLIENT";
container.register(USDC_TOP_UP_MASTER_SIGNING_CLIENT, {
useFactory: c => new MasterSigningClientService(config, c.resolve(USDC_TOP_UP_MASTER_WALLET), c.resolve(TYPE_REGISTRY), USDC_TOP_UP_MASTER_SIGNING_CLIENT)
});

export const InjectSigningClient = (walletType: MasterWalletType) => inject(`${walletType}_MASTER_SIGNING_CLIENT`);
16 changes: 16 additions & 0 deletions apps/api/src/billing/providers/wallet.provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { container, inject } from "tsyringe";

import { config } from "@src/billing/config";
import { MasterWalletService } from "@src/billing/services/master-wallet/master-wallet.service";
import { MasterWalletType } from "@src/billing/types/wallet.type";

export const MANAGED_MASTER_WALLET = "MANAGED_MASTER_WALLET";
container.register(MANAGED_MASTER_WALLET, { useFactory: () => new MasterWalletService(config.MASTER_WALLET_MNEMONIC) });

export const UAKT_TOP_UP_MASTER_WALLET = "TOP_UP_UAKT_MASTER_WALLET";
container.register(UAKT_TOP_UP_MASTER_WALLET, { useFactory: () => new MasterWalletService(config.UAKT_TOP_UP_MASTER_WALLET_MNEMONIC) });

export const USDC_TOP_UP_MASTER_WALLET = "TOP_UP_USDC_MASTER_WALLET";
container.register(USDC_TOP_UP_MASTER_WALLET, { useFactory: () => new MasterWalletService(config.USDC_TOP_UP_MASTER_WALLET_MNEMONIC) });

export const InjectWallet = (walletType: MasterWalletType) => inject(`${walletType}_MASTER_WALLET`);
3 changes: 2 additions & 1 deletion apps/api/src/billing/services/balances/balances.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { AllowanceHttpService } from "@akashnetwork/http-sdk";
import { singleton } from "tsyringe";

import { BillingConfig, InjectBillingConfig } from "@src/billing/providers";
import { InjectWallet } from "@src/billing/providers/wallet.provider";
import { UserWalletInput, UserWalletOutput, UserWalletRepository } from "@src/billing/repositories";
import { MasterWalletService } from "@src/billing/services";

Expand All @@ -10,7 +11,7 @@ export class BalancesService {
constructor(
@InjectBillingConfig() private readonly config: BillingConfig,
private readonly userWalletRepository: UserWalletRepository,
private readonly masterWalletService: MasterWalletService,
@InjectWallet("MANAGED") private readonly masterWalletService: MasterWalletService,
private readonly allowanceHttpService: AllowanceHttpService
) {}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import add from "date-fns/add";
import { singleton } from "tsyringe";

import { BillingConfig, InjectBillingConfig } from "@src/billing/providers";
import { InjectSigningClient } from "@src/billing/providers/signing-client.provider";
import { InjectWallet } from "@src/billing/providers/wallet.provider";
import { MasterSigningClientService } from "@src/billing/services/master-signing-client/master-signing-client.service";
import { MasterWalletService } from "@src/billing/services/master-wallet/master-wallet.service";
import { RpcMessageService, SpendingAuthorizationMsgOptions } from "@src/billing/services/rpc-message-service/rpc-message.service";
Expand Down Expand Up @@ -34,11 +36,13 @@ export class ManagedUserWalletService {

constructor(
@InjectBillingConfig() private readonly config: BillingConfig,
private readonly masterWalletService: MasterWalletService,
private readonly masterSigningClientService: MasterSigningClientService,
@InjectWallet("MANAGED") private readonly masterWalletService: MasterWalletService,
@InjectSigningClient("MANAGED") private readonly masterSigningClientService: MasterSigningClientService,
private readonly rpcMessageService: RpcMessageService,
private readonly allowanceHttpService: AllowanceHttpService
) {}
) {
console.log("DEBUG masterSigningClientService", masterSigningClientService);
}

async createAndAuthorizeTrialSpending({ addressIndex }: { addressIndex: number }) {
const { address } = await this.createWallet({ addressIndex });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,8 @@ import { Sema } from "async-sema";
import { TxRaw } from "cosmjs-types/cosmos/tx/v1beta1/tx";
import DataLoader from "dataloader";
import assert from "http-assert";
import { singleton } from "tsyringe";

import { BillingConfig, InjectBillingConfig } from "@src/billing/providers";
import { InjectTypeRegistry } from "@src/billing/providers/type-registry.provider";
import { BillingConfig } from "@src/billing/providers";
import { BatchSigningStargateClient } from "@src/billing/services/batch-signing-stargate-client/batch-signing-stargate-client";
import { MasterWalletService } from "@src/billing/services/master-wallet/master-wallet.service";
import { LoggerService } from "@src/core";
Expand All @@ -22,8 +20,9 @@ interface ShortAccountInfo {
sequence: number;
}

@singleton()
export class MasterSigningClientService {
private readonly FEES_DENOM = "uakt";

private clientAsPromised: Promise<BatchSigningStargateClient>;

private readonly semaphore = new Sema(1);
Expand All @@ -39,12 +38,13 @@ export class MasterSigningClientService {
{ cache: false, batchScheduleFn: callback => setTimeout(callback, this.config.MASTER_WALLET_BATCHING_INTERVAL_MS) }
);

private readonly logger = new LoggerService({ context: MasterWalletService.name });
private readonly logger = new LoggerService({ context: this.loggerContext });

constructor(
@InjectBillingConfig() private readonly config: BillingConfig,
private readonly config: BillingConfig,
private readonly masterWalletService: MasterWalletService,
@InjectTypeRegistry() private readonly registry: Registry
private readonly registry: Registry,
private readonly loggerContext = MasterSigningClientService.name
) {
this.clientAsPromised = this.initClient();
}
Expand Down Expand Up @@ -110,7 +110,7 @@ export class MasterSigningClientService {

while (txIndex < messages.length) {
txes.push(
await client.sign(masterAddress, messages[txIndex], await this.estimateFee(messages[txIndex], this.config.DEPLOYMENT_GRANT_DENOM, { mock: true }), "", {
await client.sign(masterAddress, messages[txIndex], await this.estimateFee(messages[txIndex], this.FEES_DENOM, { mock: true }), "", {
accountNumber: this.accountInfo.accountNumber,
sequence: this.accountInfo.sequence++,
chainId: this.chainId
Expand All @@ -137,7 +137,7 @@ export class MasterSigningClientService {
private async estimateFee(messages: readonly EncodeObject[], denom: string, options?: { mock?: boolean }) {
if (options?.mock) {
return {
amount: [{ denom: "uakt", amount: "15000" }],
amount: [{ denom: this.FEES_DENOM, amount: "15000" }],
gas: "500000"
};
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
import { DirectSecp256k1HdWallet, OfflineDirectSigner } from "@cosmjs/proto-signing";
import { SignDoc } from "cosmjs-types/cosmos/tx/v1beta1/tx";
import { singleton } from "tsyringe";

import { BillingConfig, InjectBillingConfig } from "@src/billing/providers";

@singleton()
export class MasterWalletService implements OfflineDirectSigner {
private readonly PREFIX = "akash";

private readonly instanceAsPromised: Promise<DirectSecp256k1HdWallet>;

constructor(@InjectBillingConfig() private readonly config: BillingConfig) {
this.instanceAsPromised = DirectSecp256k1HdWallet.fromMnemonic(this.config.MASTER_WALLET_MNEMONIC, { prefix: this.PREFIX });
constructor(mnemonic: string) {
this.instanceAsPromised = DirectSecp256k1HdWallet.fromMnemonic(mnemonic, { prefix: this.PREFIX });
}

async getAccounts() {
Expand Down
3 changes: 2 additions & 1 deletion apps/api/src/billing/services/tx-signer/tx-signer.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { singleton } from "tsyringe";
import { AuthService } from "@src/auth/services/auth.service";
import { BillingConfig, InjectBillingConfig } from "@src/billing/providers";
import { InjectTypeRegistry } from "@src/billing/providers/type-registry.provider";
import { InjectWallet } from "@src/billing/providers/wallet.provider";
import { UserWalletOutput, UserWalletRepository } from "@src/billing/repositories";
import { MasterWalletService } from "@src/billing/services";
import { BalancesService } from "@src/billing/services/balances/balances.service";
Expand All @@ -30,7 +31,7 @@ export class TxSignerService {
@InjectBillingConfig() private readonly config: BillingConfig,
@InjectTypeRegistry() private readonly registry: Registry,
private readonly userWalletRepository: UserWalletRepository,
private readonly masterWalletService: MasterWalletService,
@InjectWallet("MANAGED") private readonly masterWalletService: MasterWalletService,
private readonly balancesService: BalancesService,
private readonly authService: AuthService,
private readonly chainErrorService: ChainErrorService,
Expand Down
1 change: 1 addition & 0 deletions apps/api/src/billing/types/wallet.type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type MasterWalletType = "MANAGED" | "USDC_TOP_UP" | "UAKT_TOP_UP";
2 changes: 1 addition & 1 deletion apps/api/test/functional/app.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ describe("app", () => {
rss: expect.stringMatching(/^[0-9.]+ (M|G)B$/)
},
tasks: [],
version: expect.stringMatching(/^[0-9]+.[0-9]+.[0-9]+$/)
version: expect.stringMatching(/^[0-9]+.[0-9]+.[0-9]+(-beta\.[0-9]+)?$/)
});
});
});
Expand Down
3 changes: 2 additions & 1 deletion apps/api/test/functional/create-deployment.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { container } from "tsyringe";
import { app } from "@src/app";
import { config } from "@src/billing/config";
import { TYPE_REGISTRY } from "@src/billing/providers/type-registry.provider";
import { MANAGED_MASTER_WALLET } from "@src/billing/providers/wallet.provider";
import { MasterWalletService } from "@src/billing/services";

jest.setTimeout(30000);
Expand All @@ -21,7 +22,7 @@ const yml = fs.readFileSync(path.resolve(__dirname, "../mocks/hello-world-sdl.ym
describe("Tx Sign", () => {
const registry = container.resolve<Registry>(TYPE_REGISTRY);
const walletService = new WalletTestingService(app);
const masterWalletService = container.resolve(MasterWalletService);
const masterWalletService = container.resolve<MasterWalletService>(MANAGED_MASTER_WALLET);
const dbService = container.resolve(DbTestingService);

afterEach(async () => {
Expand Down

0 comments on commit 5ea00e4

Please sign in to comment.