Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
begonaalvarezd committed Mar 27, 2024
2 parents d6c0a1c + cbdf907 commit afbd280
Show file tree
Hide file tree
Showing 14 changed files with 134 additions and 75 deletions.
1 change: 1 addition & 0 deletions .github/workflows/build-images.reusable.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ on:
jobs:
build-and-push-image:
name: Build and Push Docker Image
environment: release
runs-on: ubuntu-latest
steps:
- name: Checkout code
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/docker-deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ on:
jobs:
build-and-deploy:
runs-on: ubuntu-latest
environment: release
steps:
- name: Checkout code
uses: actions/checkout@v2
Expand Down
22 changes: 11 additions & 11 deletions api/package-lock.json

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

4 changes: 2 additions & 2 deletions api/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "explorer-api",
"description": "API for Tangle Explorer",
"version": "3.3.4",
"version": "3.3.6",
"author": "Martyn Janes <[email protected]>",
"repository": {
"type": "git",
Expand Down Expand Up @@ -31,7 +31,7 @@
"@iota/crypto.js": "^1.8.6",
"@iota/identity-wasm": "^0.5.0-dev.6",
"@iota/identity-wasm-0.4": "npm:@iota/identity-wasm@^0.4.3",
"@iota/identity-wasm-0.7": "npm:@iota/identity-wasm@^0.7.0-alpha.6",
"@iota/identity-wasm-stardust": "npm:@iota/identity-wasm@^1.1.0",
"@iota/iota.js-chrysalis": "npm:@iota/iota.js@^1.8.6",
"@iota/mqtt.js": "^1.8.6",
"@iota/sdk": "1.1.1",
Expand Down
56 changes: 33 additions & 23 deletions api/src/routes/networks/get.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { ServiceFactory } from "../../factories/serviceFactory";
import { INetworkGetResponse } from "../../models/api/INetworkGetResponse";
import { IConfiguration } from "../../models/configuration/IConfiguration";
import { NetworkService } from "../../services/networkService";
import { NodeInfoService } from "../../services/stardust/nodeInfoService";

/**
* Get the networks.
Expand All @@ -19,28 +20,37 @@ export async function get(_: IConfiguration): Promise<INetworkGetResponse> {
// and copy the fields needed by the client
// as we don't want to expose all the information
.filter((n) => !n.isHidden && n.isEnabled)
.map((n) => ({
network: n.network,
label: n.label,
protocolVersion: n.protocolVersion,
coordinatorAddress: n.coordinatorAddress,
coordinatorSecurityLevel: n.coordinatorSecurityLevel,
isEnabled: n.isEnabled,
showMarket: n.showMarket,
uiTheme: n.uiTheme,
hasStatisticsSupport:
Boolean(n.analyticsInfluxDbEndpoint) &&
Boolean(n.analyticsInfluxDbDatabase) &&
Boolean(n.analyticsInfluxDbUsername) &&
Boolean(n.analyticsInfluxDbPassword),
description: n.description,
bechHrp: n.bechHrp,
didExample: n.didExample,
faucet: n.faucet,
milestoneInterval: n.milestoneInterval,
circulatingSupply: n.circulatingSupply,
identityResolverEnabled: n.identityResolverEnabled,
tokenRegistryEndpoint: n.tokenRegistryEndpoint,
})),
.map((n) => {
const nodeInfoService = ServiceFactory.get<NodeInfoService>(`node-info-${n.network}`);
let circulatingSupplyFromSupplyTracker: number | null = null;

if (nodeInfoService) {
circulatingSupplyFromSupplyTracker = nodeInfoService.circulatingSupply;
}

return {
network: n.network,
label: n.label,
protocolVersion: n.protocolVersion,
coordinatorAddress: n.coordinatorAddress,
coordinatorSecurityLevel: n.coordinatorSecurityLevel,
isEnabled: n.isEnabled,
showMarket: n.showMarket,
uiTheme: n.uiTheme,
hasStatisticsSupport:
Boolean(n.analyticsInfluxDbEndpoint) &&
Boolean(n.analyticsInfluxDbDatabase) &&
Boolean(n.analyticsInfluxDbUsername) &&
Boolean(n.analyticsInfluxDbPassword),
description: n.description,
bechHrp: n.bechHrp,
didExample: n.didExample,
faucet: n.faucet,
milestoneInterval: n.milestoneInterval,
circulatingSupply: circulatingSupplyFromSupplyTracker ?? n.circulatingSupply,
identityResolverEnabled: n.identityResolverEnabled,
tokenRegistryEndpoint: n.tokenRegistryEndpoint,
};
}),
};
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { IotaDID, IotaDocument, IotaIdentityClient } from "@iota/identity-wasm-0.7/node";
import { IotaDID, IotaDocument, IotaIdentityClient } from "@iota/identity-wasm-stardust/node";
import { Client } from "@iota/sdk-wasm/node";
import { ServiceFactory } from "../../../../factories/serviceFactory";
import { IIdentityStardustResolveRequest } from "../../../../models/api/stardust/identity/IIdentityStardustResolveRequest";
Expand Down
47 changes: 46 additions & 1 deletion api/src/services/stardust/nodeInfoService.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import { INodeInfoBaseToken, IRent, Client } from "@iota/sdk";
import cron from "node-cron";
import { StardustApiService } from "./stardustApiService";
import { NodeInfoError } from "../../errors/nodeInfoError";
import { ServiceFactory } from "../../factories/serviceFactory";
import logger from "../../logger";
import { INetwork } from "../../models/db/INetwork";

// The cron interval value to update the circulating supply every 10 minutes.
const CIRCULATING_SUPPLY_UPDATE_INTERVAL = "*/10 * * * *";

/**
* The reduced node info fields relevant for Explorer.
*/
Expand Down Expand Up @@ -39,14 +45,25 @@ export class NodeInfoService {
*/
protected _nodeInfo: IReducedNodeInfo;

/**
* The circulating supply of the network if available.
*/
protected _ciruclatingSupply?: number | null;

/**
* Create a new instance of NodeInfoService.
* @param network The network config.
* @param nodeInfo The fetched node info
* @param nodeInfo The fetched node info.
*/
private constructor(network: INetwork, nodeInfo: IReducedNodeInfo) {
this._network = network;
this._nodeInfo = nodeInfo;

this.setupCirculatingSupplyUpdater();
}

public get circulatingSupply() {
return this._ciruclatingSupply;
}

public static async build(network: INetwork): Promise<NodeInfoService> {
Expand All @@ -70,4 +87,32 @@ export class NodeInfoService {
public getNodeInfo(): IReducedNodeInfo {
return this._nodeInfo;
}

private setupCirculatingSupplyUpdater() {
// eslint-disable-next-line no-void
void this.updateCirculatingSupply();

cron.schedule(CIRCULATING_SUPPLY_UPDATE_INTERVAL, async () => {
await this.updateCirculatingSupply();
});
}

private async updateCirculatingSupply() {
const stardustApiService = ServiceFactory.get<StardustApiService>(`api-service-${this._network.network}`);
let circulatingSupply: number | null = null;

try {
const circulatingSupplyInBaseToken = await stardustApiService.circulatingSupply();
if (circulatingSupplyInBaseToken) {
// The circulating supply from inx-supply-tracker is returned in base token,
// so we format it to subunit
circulatingSupply = circulatingSupplyInBaseToken * Math.pow(10, this._nodeInfo.baseToken.decimals);
logger.debug(`[NodeInfoService] Circulating supply for ${this._network.network} (in subunit): ${circulatingSupply}`);

this._ciruclatingSupply = circulatingSupply;
}
} catch {
logger.debug(`[NodeInfoService] Failed fetching circulating supply for ${this._network.network}`);
}
}
}
13 changes: 13 additions & 0 deletions api/src/services/stardust/stardustApiService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,19 @@ export class StardustApiService {
};
}

/**
* Get the circulating supply from inx-supply-tracking (in base token).
* @returns The circulating supply.
*/
public async circulatingSupply(): Promise<number | null> {
const path: string = "api/supply/v1/";
const methodPath: string = "circulating";
const method = "GET";
const circulatingSupply: number | null = await this.nodePluginFetch<number | null>(path, method, methodPath);

return circulatingSupply;
}

/**
* Find item on the stardust network.
* @param query The query to use for finding items.
Expand Down
24 changes: 1 addition & 23 deletions api/src/utils/stardust/searchExecutor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,29 +25,6 @@ export class SearchExecutor {
const promises: Promise<void>[] = [];
let promisesResult: ISearchResponse | null = null;

if (searchQuery.did) {
promises.push(
new Promise((resolve, reject) => {
this.apiService
.aliasDetails(searchQuery.aliasId)
.then((aliasOutputs) => {
if (aliasOutputs) {
promisesResult = {
aliasId: searchQuery.aliasId,
did: searchQuery.did,
};
resolve();
} else {
reject(new Error("Output (aliasId) not present"));
}
})
.catch((_) => {
reject(new Error("Output (aliasId) fetch failed"));
});
}),
);
}

if (searchQuery.milestoneIndex) {
promises.push(
new Promise((resolve, reject) => {
Expand Down Expand Up @@ -165,6 +142,7 @@ export class SearchExecutor {
if (aliasOutputs) {
promisesResult = {
aliasId: searchQuery.aliasId,
did: searchQuery.did,
};
resolve();
} else {
Expand Down
4 changes: 2 additions & 2 deletions client/package-lock.json

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

7 changes: 4 additions & 3 deletions client/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "explorer-client",
"description": "Tangle Explorer UI",
"version": "3.3.4",
"version": "3.3.6",
"author": "Martyn Janes <[email protected]>",
"type": "module",
"repository": {
Expand Down Expand Up @@ -115,6 +115,7 @@
],
"lint-staged": {
"src/**/*.{ts,js,tsx,jsx}": "eslint --cache --fix",
"src/**/*.{ts,js,tsx,jsx,scss,css}": "prettier --ignore-path=.prettierignore --write"
"src/**/*.{ts,js,tsx,jsx,scss,css}": "prettier --ignore-path=.prettierignore --write",
"src/**/*.scss": "stylelint --fix"
}
}
}
15 changes: 12 additions & 3 deletions client/src/app/routes/stardust/landing/AnalyticStats.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,18 @@ const AnalyticStats: React.FC<AnalyticStatsProps> = ({ analytics, circulatingSup

let claimedAndPercentLabels: [string, string] | undefined;
if (analytics?.unclaimedShimmer && circulatingSupply) {
const totalSupplyBigInt = (BigInt(circulatingSupply) * BigInt(100)) / BigInt(80); // https://github.com/iotaledger/explorer/issues/584
const shimmerClaimedBigInt = totalSupplyBigInt - BigInt(analytics.unclaimedShimmer);
claimedAndPercentLabels = buildShimmerClaimedStats(shimmerClaimedBigInt.toString(), totalSupplyBigInt.toString(), tokenInfo);
// in shimmer, the circulating supply is the same as the total supply
const totalShimmerSupplyBigInt = BigInt(circulatingSupply);
// 80% of the Shimmer genesis token supply were distributed to IOTA token holders
// genesis distribution: https://github.com/iotaledger/tips/blob/main/tips/TIP-0032/tip-0032.md#global-parameters
const claimableShimmerSupplyBigInt = (totalShimmerSupplyBigInt * BigInt(80)) / BigInt(100);
// TEA and DAO genesis outputs are spent, so we can assume all unspent genesis outputs are stakers
const shimmerClaimedBigInt = claimableShimmerSupplyBigInt - BigInt(analytics.unclaimedShimmer);
claimedAndPercentLabels = buildShimmerClaimedStats(
shimmerClaimedBigInt.toString(),
claimableShimmerSupplyBigInt.toString(),
tokenInfo,
);
}

return analytics && !analytics.error ? (
Expand Down
11 changes: 6 additions & 5 deletions client/src/helpers/stardust/hooks/useAddressHistory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,12 +121,13 @@ export function useAddressHistory(
);

outputsWithDetails = [...outputsWithDetails, ...fulfilledOutputs].sort((a, b) => {
// Ensure that entries with equal timestamp, but different isSpent,
// have the spending before the depositing
if (a.milestoneTimestamp === b.milestoneTimestamp && a.isSpent !== b.isSpent) {
return a.isSpent ? 1 : -1;
// Sort by milestoneTimestamp in descending order (newest first)
if (a.milestoneTimestamp !== b.milestoneTimestamp) {
return b.milestoneTimestamp - a.milestoneTimestamp;
}
return 1;
// If milestoneTimestamp is the same, prioritize 'isSpent'
// Place 'isSpent: true' after 'isSpent: false' if timestamps are equal
return a.isSpent ? 1 : -1;
});

transactionIdToOutputs = groupOutputsByTransactionId(outputsWithDetails);
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "explorer",
"description": "Tangle Explorer",
"version": "3.3.4",
"version": "3.3.6",
"scripts": {
"setup:client": "cd client && npm install && npm run postinstall",
"setup:api": "cd api && npm install && npm run build-compile && npm run build-config",
Expand Down

0 comments on commit afbd280

Please sign in to comment.