Skip to content

Commit

Permalink
feat(network): API endpoint listing bids
Browse files Browse the repository at this point in the history
refs #767
  • Loading branch information
jzsfkzm committed Feb 19, 2025
1 parent 9dbe69e commit ff989dd
Show file tree
Hide file tree
Showing 11 changed files with 507 additions and 59 deletions.
2 changes: 2 additions & 0 deletions apps/api/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { RequestContextInterceptor } from "@src/core/services/request-context-in
import { HonoInterceptor } from "@src/core/types/hono-interceptor.type";
import packageJson from "../package.json";
import { apiKeysRouter } from "./auth/routes/api-keys/api-keys.router";
import { bidsRouter } from "./bid/routes/bids/bids.router";
import { chainDb, syncUserSchema, userDb } from "./db/dbConnection";
import { deploymentSettingRouter } from "./deployment/routes/deployment-setting/deployment-setting.router";
import { clientInfoMiddleware } from "./middlewares/clientInfoMiddleware";
Expand Down Expand Up @@ -87,6 +88,7 @@ appHono.route("/", getAnonymousUserRouter);
appHono.route("/", sendVerificationEmailRouter);
appHono.route("/", deploymentSettingRouter);
appHono.route("/", apiKeysRouter);
appHono.route("/", bidsRouter);

appHono.get("/status", c => {
const version = packageJson.version;
Expand Down
36 changes: 36 additions & 0 deletions apps/api/src/bid/controllers/bid/bid.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { BidHttpService } from "@akashnetwork/http-sdk";
import assert from "http-assert";
import flatten from "lodash/flatten";
import { singleton } from "tsyringe";

import { AuthService, Protected } from "@src/auth/services/auth.service";
import { ListBidsResponse } from "@src/bid/http-schemas/bid.schema";
import { UserWalletRepository } from "@src/billing/repositories";

@singleton()
export class BidController {
constructor(
private readonly bidHttpService: BidHttpService,
private readonly authService: AuthService,
private readonly userWalletRepository: UserWalletRepository,
) {}

@Protected([{ action: "create", subject: "DeploymentSetting" }])
async list(dseq: string, userId?: string): Promise<ListBidsResponse> {
const { currentUser } = this.authService;
assert(this.isValidUserId(userId), 401);

const wallets = await this.userWalletRepository.findByUserId(userId ?? currentUser.userId);
const allBids = await Promise.all(wallets.map(wallet => this.bidHttpService.list(wallet.address, dseq)));

return { data: flatten(allBids) };
}

private isValidUserId(userId?: string) {
const { currentUser, ability } = this.authService;

return userId === undefined
|| userId === currentUser.userId
|| ability.can("manage", "all");
}
}
100 changes: 100 additions & 0 deletions apps/api/src/bid/http-schemas/bid.schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { z } from "zod";

const DeploymentResource_V3 = z.object({
cpu: z.object({
units: z.object({
val: z.string(),
}),
attributes: z.array(z.object({
key: z.string(),
value: z.string(),
})),
}),
gpu: z.object({
units: z.object({
val: z.string(),
}),
attributes: z.array(z.object({
key: z.string(),
value: z.string(),
})),
}),
memory: z.object({
quantity: z.object({
val: z.string(),
}),
attributes: z.array(z.object({
key: z.string(),
value: z.string(),
})),
}),
storage: z.array(z.object({
name: z.string(),
quantity: z.object({
val: z.string(),
}),
attributes: z.array(z.object({
key: z.string(),
value: z.string(),
})),
})),
endpoints: z.array(z.object({
kind: z.string(),
sequence_number: z.number()
}))
});

export const BidResponseSchema = z.object({
bid: z.object({
bid_id: z.object({
owner: z.string(),
dseq: z.string(),
gseq: z.number(),
oseq: z.number(),
provider: z.string(),
}),
state: z.string(),
price: z.object({
denom: z.string(),
amount: z.string(),
}),
created_at: z.string(),
resources_offer: z.array(z.object({
resources: DeploymentResource_V3,
count: z.number(),
}))
}),
escrow_account: z.object({
id: z.object({
scope: z.string(),
xid: z.string(),
}),
owner: z.string(),
state: z.string(),
balance: z.object({
denom: z.string(),
amount: z.string(),
}),
transferred: z.object({
denom: z.string(),
amount: z.string(),
}),
settled_at: z.string(),
depositor: z.string(),
funds: z.object({
denom: z.string(),
amount: z.string(),
}),
})
});

export const ListBidsQuerySchema = z.object({
dseq: z.string(),
userId: z.optional(z.string()),
});

export const ListBidsResponseSchema = z.object({
data: z.array(BidResponseSchema)
});

export type ListBidsResponse = z.infer<typeof ListBidsResponseSchema>;
34 changes: 34 additions & 0 deletions apps/api/src/bid/routes/bids/bids.router.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { createRoute } from "@hono/zod-openapi";
import { container } from "tsyringe";

import { BidController } from "@src/bid/controllers/bid/bid.controller";
import { ListBidsQuerySchema, ListBidsResponseSchema } from "@src/bid/http-schemas/bid.schema";
import { OpenApiHonoHandler } from "@src/core/services/open-api-hono-handler/open-api-hono-handler";

const listRoute = createRoute({
method: "get",
path: "/v1/bids",
summary: "List bids",
tags: ["Bids"],
request: {
query: ListBidsQuerySchema
},
responses: {
200: {
description: "List of bids",
content: {
"application/json": {
schema: ListBidsResponseSchema
}
}
}
}
});

export const bidsRouter = new OpenApiHonoHandler();

bidsRouter.openapi(listRoute, async function routeListBids(c) {
const { dseq, userId } = c.req.valid("query");
const result = await container.resolve(BidController).list(dseq, userId);
return c.json(result, 200);
});
4 changes: 2 additions & 2 deletions apps/api/src/core/providers/http-sdk.provider.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { AuthzHttpService, BalanceHttpService, BlockHttpService } from "@akashnetwork/http-sdk";
import { AuthzHttpService, BalanceHttpService, BidHttpService, BlockHttpService } from "@akashnetwork/http-sdk";
import { container } from "tsyringe";

import { apiNodeUrl } from "@src/utils/constants";

const SERVICES = [BalanceHttpService, AuthzHttpService, BlockHttpService];
const SERVICES = [BalanceHttpService, AuthzHttpService, BlockHttpService, BidHttpService];

SERVICES.forEach(Service => container.register(Service, { useValue: new Service({ baseURL: apiNodeUrl }) }));
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { MsgCloseDeployment, MsgCreateDeployment } from "@akashnetwork/akash-api/v1beta3";
import { SDL } from "@akashnetwork/akashjs/build/sdl";
import { getAkashTypeRegistry } from "@akashnetwork/akashjs/build/stargate";
import { BidHttpService } from "@akashnetwork/http-sdk";
import { LoggerService } from "@akashnetwork/logging";
import { DirectSecp256k1HdWallet, EncodeObject, Registry } from "@cosmjs/proto-signing";
import { calculateFee, SigningStargateClient } from "@cosmjs/stargate";
Expand All @@ -10,7 +11,6 @@ import { singleton } from "tsyringe";

import { BillingConfigService } from "@src/billing/services/billing-config/billing-config.service";
import { getGpuModelsAvailability } from "@src/routes/v1/gpu";
import { RestAkashBidListResponseType } from "@src/types/rest";
import { apiNodeUrl } from "@src/utils/constants";
import { sleep } from "@src/utils/delay";
import { env } from "@src/utils/env";
Expand All @@ -20,7 +20,10 @@ import { sdlTemplateWithRam, sdlTemplateWithRamAndInterface } from "./sdl-templa
export class GpuBidsCreatorService {
private readonly logger = LoggerService.forContext(GpuBidsCreatorService.name);

constructor(private readonly config: BillingConfigService) {}
constructor(
private readonly config: BillingConfigService,
private readonly bidHttpService: BidHttpService,
) { }

async createGpuBids() {
if (!env.GPU_BOT_WALLET_MNEMONIC) throw new Error("The env variable GPU_BOT_WALLET_MNEMONIC is not set.");
Expand Down Expand Up @@ -107,9 +110,9 @@ export class GpuBidsCreatorService {

await sleep(30_000);

const bids = await this.getBids(walletAddress, dseq);
const bids = await this.bidHttpService.list(walletAddress, dseq);

this.logger.info(`Got ${bids.bids.length} bids. Closing deployment...`);
this.logger.info(`Got ${bids.length} bids. Closing deployment...`);

await this.closeDeployment(client, walletAddress, dseq);

Expand Down Expand Up @@ -163,12 +166,6 @@ export class GpuBidsCreatorService {
await this.signAndBroadcast(owner, client, [message]);
}

private async getBids(owner: string, dseq: string) {
const response = await axios.get<RestAkashBidListResponseType>(`${apiNodeUrl}/akash/market/v1beta4/bids/list?filters.owner=${owner}&filters.dseq=${dseq}`);

return response.data;
}

private getModelSdl(vendor: string, model: string, ram: string, gpuInterface?: string) {
let gpuSdl = gpuInterface ? sdlTemplateWithRamAndInterface : sdlTemplateWithRam;
gpuSdl = gpuSdl.replace("<VENDOR>", vendor);
Expand Down
47 changes: 0 additions & 47 deletions apps/api/src/types/rest/akashBidListResponse.ts

This file was deleted.

Loading

0 comments on commit ff989dd

Please sign in to comment.