Skip to content

Commit

Permalink
Merge branch 'dev' into feat/nova/mark-finalized-blocks
Browse files Browse the repository at this point in the history
  • Loading branch information
VmMad committed Feb 14, 2024
2 parents 9670dff + e2ccf77 commit 5adfd58
Show file tree
Hide file tree
Showing 65 changed files with 2,401 additions and 360 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/nova-build-temp.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
TARGET_COMMIT:
description: "Target Commit Hash for the SDK"
required: false
default: "7d32ae0bb7e93552618923fb272a5c1cc67c2c60"
default: "133f911b18191cda9099f1b4aeaf7d0022dfe0fb"
environment:
type: choice
description: "Select the environment to deploy to"
Expand Down
12 changes: 6 additions & 6 deletions api/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions api/src/models/api/nova/IAddressDetails.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/* eslint-disable import/no-unresolved */
/* eslint-disable @typescript-eslint/no-unsafe-argument */
import { AddressType } from "@iota/sdk-nova";

export interface IAddressDetails {
bech32: string;
hex?: string;
type?: AddressType;
label?: string;
restricted: boolean;
capabilities?: number[];
}
11 changes: 11 additions & 0 deletions api/src/models/api/nova/IAssociationsRequest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export interface IAssociationsRequest {
/**
* The network to search on.
*/
network: string;

/**
* The address to get the associated outputs for.
*/
address: string;
}
8 changes: 8 additions & 0 deletions api/src/models/api/nova/IAssociationsRequestBody.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { IAddressDetails } from "./IAddressDetails";

export interface IAssociationsRequestBody {
/**
* The address details of the address to get the associated outputs for.
*/
addressDetails: IAddressDetails;
}
11 changes: 11 additions & 0 deletions api/src/models/api/nova/ISearchRequest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export interface ISearchRequest {
/**
* The network to search on.
*/
network: string;

/**
* The query to look for.
*/
query: string;
}
42 changes: 42 additions & 0 deletions api/src/models/api/nova/ISearchResponse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/* eslint-disable import/no-unresolved */
/* eslint-disable @typescript-eslint/no-unsafe-argument */
import { Block, OutputResponse } from "@iota/sdk-nova";
import { IAddressDetails } from "./IAddressDetails";
import { IResponse } from "../IResponse";

export interface ISearchResponse extends IResponse {
/**
* Block if it was found.
*/
block?: Block;

/**
* Address details.
*/
addressDetails?: IAddressDetails;

/**
* Output if it was found (block will also be populated).
*/
output?: OutputResponse;

/**
* Account id if it was found.
*/
accountId?: string;

/**
* Anchor id if it was found.
*/
anchorId?: string;

/**
* Foundry id if it was found.
*/
foundryId?: string;

/**
* Nft id if it was found.
*/
nftId?: string;
}
11 changes: 11 additions & 0 deletions api/src/models/api/nova/chronicle/IAddressBalanceRequest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export interface IAddressBalanceRequest {
/**
* The network to search on.
*/
network: string;

/**
* The bech32 address to get the balance for.
*/
address: string;
}
18 changes: 18 additions & 0 deletions api/src/models/api/nova/chronicle/IAddressBalanceResponse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { IResponse } from "../../IResponse";

export interface IAddressBalanceResponse extends IResponse {
/**
* The total balance (including Expiration, Timelock and StorageDepositReturn outputs)
*/
totalBalance?: number;

/**
* The balance of all spendable outputs by the address at this time.
*/
availableBalance?: number;

/**
* The ledger index at which this balance data was valid.
*/
ledgerIndex?: number;
}
7 changes: 7 additions & 0 deletions api/src/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,13 @@ export const routes: IRoute[] = [
func: "get",
},
// Nova
{
path: "/nova/balance/chronicle/:network/:address",
method: "get",
folder: "nova/address/balance/chronicle",
func: "get",
},
{ path: "/nova/search/:network/:query", method: "get", folder: "nova", func: "search" },
{ path: "/nova/output/:network/:outputId", method: "get", folder: "nova/output", func: "get" },
{ path: "/nova/output/rewards/:network/:outputId", method: "get", folder: "nova/output/rewards", func: "get" },
{ path: "/nova/account/:network/:accountId", method: "get", folder: "nova/account", func: "get" },
Expand Down
34 changes: 34 additions & 0 deletions api/src/routes/nova/address/balance/chronicle/get.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { ServiceFactory } from "../../../../../factories/serviceFactory";
import { IAddressBalanceRequest } from "../../../../../models/api/nova/chronicle/IAddressBalanceRequest";
import { IAddressBalanceResponse } from "../../../../../models/api/nova/chronicle/IAddressBalanceResponse";
import { IConfiguration } from "../../../../../models/configuration/IConfiguration";
import { NOVA } from "../../../../../models/db/protocolVersion";
import { NetworkService } from "../../../../../services/networkService";
import { ChronicleService } from "../../../../../services/nova/chronicleService";
import { ValidationHelper } from "../../../../../utils/validationHelper";

/**
* Fetch the address balance from chronicle nova.
* @param _ The configuration.
* @param request The request.
* @returns The response.
*/
export async function get(_: IConfiguration, request: IAddressBalanceRequest): Promise<IAddressBalanceResponse> {
const networkService = ServiceFactory.get<NetworkService>("network");
const networks = networkService.networkNames();
ValidationHelper.oneOf(request.network, networks, "network");

const networkConfig = networkService.get(request.network);

if (networkConfig.protocolVersion !== NOVA) {
return {};
}

if (!networkConfig.permaNodeEndpoint) {
return {};
}

const chronicleService = ServiceFactory.get<ChronicleService>(`chronicle-${networkConfig.network}`);

return chronicleService.addressBalance(request.address);
}
4 changes: 2 additions & 2 deletions api/src/routes/nova/output/associated/post.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ServiceFactory } from "../../../../factories/serviceFactory";
import { IAssociationsRequest } from "../../../../models/api/nova/IAssociationsRequest";
import { IAssociationsRequestBody } from "../../../../models/api/nova/IAssociationsRequestBody";
import { IAssociation, IAssociationsResponse } from "../../../../models/api/nova/IAssociationsResponse";
import { IAssociationsRequest } from "../../../../models/api/stardust/IAssociationsRequest";
import { IAssociationsRequestBody } from "../../../../models/api/stardust/IAssociationsRequestBody";
import { IConfiguration } from "../../../../models/configuration/IConfiguration";
import { NOVA } from "../../../../models/db/protocolVersion";
import { NetworkService } from "../../../../services/networkService";
Expand Down
30 changes: 30 additions & 0 deletions api/src/routes/nova/search.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { ServiceFactory } from "../../factories/serviceFactory";
import { ISearchRequest } from "../../models/api/nova/ISearchRequest";
import { ISearchResponse } from "../../models/api/nova/ISearchResponse";
import { IConfiguration } from "../../models/configuration/IConfiguration";
import { NOVA } from "../../models/db/protocolVersion";
import { NetworkService } from "../../services/networkService";
import { NovaApiService } from "../../services/nova/novaApiService";
import { ValidationHelper } from "../../utils/validationHelper";

/**
* Find the object from the network.
* @param _ The configuration.
* @param request The request.
* @returns The response.
*/
export async function search(_: IConfiguration, request: ISearchRequest): Promise<ISearchResponse> {
const networkService = ServiceFactory.get<NetworkService>("network");
const networks = networkService.networkNames();
ValidationHelper.oneOf(request.network, networks, "network");
ValidationHelper.string(request.query, "query");

const networkConfig = networkService.get(request.network);

if (networkConfig.protocolVersion !== NOVA) {
return {};
}

const novaApiService = ServiceFactory.get<NovaApiService>(`api-service-${networkConfig.network}`);
return novaApiService.search(request.query);
}
43 changes: 43 additions & 0 deletions api/src/services/nova/chronicleService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import logger from "../../logger";
import { IAddressBalanceResponse } from "../../models/api/nova/chronicle/IAddressBalanceResponse";
import { INetwork } from "../../models/db/INetwork";
import { FetchHelper } from "../../utils/fetchHelper";

const CHRONICLE_ENDPOINTS = {
balance: "/api/explorer/v3/balance/",
};

export class ChronicleService {
/**
* The endpoint for performing communications.
*/
private readonly chronicleEndpoint: string;

/**
* The network config in context.
*/
private readonly networkConfig: INetwork;

constructor(config: INetwork) {
this.networkConfig = config;
this.chronicleEndpoint = config.permaNodeEndpoint;
}

/**
* Get the current address balance info.
* @param address The address to fetch the balance for.
* @returns The address balance response.
*/
public async addressBalance(address: string): Promise<IAddressBalanceResponse | undefined> {
try {
return await FetchHelper.json<never, IAddressBalanceResponse>(
this.chronicleEndpoint,
`${CHRONICLE_ENDPOINTS.balance}${address}`,
"get",
);
} catch (error) {
const network = this.networkConfig.network;
logger.warn(`[ChronicleService (Nova)] Failed fetching address balance for ${address} on ${network}. Cause: ${error}`);
}
}
}
19 changes: 19 additions & 0 deletions api/src/services/nova/novaApiService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,28 @@ import { IBlockResponse } from "../../models/api/nova/IBlockResponse";
import { INftDetailsResponse } from "../../models/api/nova/INftDetailsResponse";
import { IOutputDetailsResponse } from "../../models/api/nova/IOutputDetailsResponse";
import { IRewardsResponse } from "../../models/api/nova/IRewardsResponse";
import { ISearchResponse } from "../../models/api/nova/ISearchResponse";
import { INetwork } from "../../models/db/INetwork";
import { HexHelper } from "../../utils/hexHelper";
import { SearchExecutor } from "../../utils/nova/searchExecutor";
import { SearchQueryBuilder } from "../../utils/nova/searchQueryBuilder";

/**
* Class to interact with the nova API.
*/
export class NovaApiService {
/**
* The network in context.
*/
private readonly network: INetwork;

/**
* The client to use for requests.
*/
private readonly client: Client;

constructor(network: INetwork) {
this.network = network;
this.client = ServiceFactory.get<Client>(`client-${network.network}`);
}

Expand Down Expand Up @@ -154,4 +163,14 @@ export class NovaApiService {

return manaRewardsResponse ? { outputId, manaRewards: manaRewardsResponse } : { outputId, message: "Rewards data not found" };
}

/**
* Find item on the stardust network.
* @param query The query to use for finding items.
* @returns The item found.
*/
public async search(query: string): Promise<ISearchResponse> {
const searchQuery = new SearchQueryBuilder(query, this.network.bechHrp).build();
return new SearchExecutor(this.network, searchQuery).run();
}
}
Loading

0 comments on commit 5adfd58

Please sign in to comment.