Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Add delegation tab #1186

Merged
merged 58 commits into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
a528704
feat: add output tab to address page
brancoder Feb 13, 2024
3e24988
Merge branch 'dev' into feat/add-outputs-tab
brancoder Feb 13, 2024
aed2313
fix: add components to display native tokens
brancoder Feb 14, 2024
cd0e4ed
fix: resolve conflicts
brancoder Feb 14, 2024
8ffe165
Remove unused code in AddressPageTabbedSections component
brancoder Feb 14, 2024
a55715b
fix: resolve conflicts
brancoder Feb 14, 2024
4c9cf07
fix: resolve confilict
brancoder Feb 14, 2024
44fd78a
remove unused interfaces
brancoder Feb 14, 2024
35d1cbb
Update address states and Add basic outputs API endpoint
brancoder Feb 15, 2024
99df7c0
Merge branch 'dev' into feat/add-native-tokens-tab
brancoder Feb 15, 2024
3a10fe5
Add eslint-disable for unsafe return in novaApiService.ts
brancoder Feb 15, 2024
4027cb5
Add Foundries tab to Accound address page
brancoder Feb 15, 2024
b390f05
Add Foundries tab to Accound address page (#1134)
brancoder Feb 16, 2024
b77d8a9
feat: add block issuance tab to account page
brancoder Feb 16, 2024
0f0b1c5
fix: validation check in foundries endpoint
brancoder Feb 16, 2024
9db3390
fix: component imports
brancoder Feb 16, 2024
1b665f1
Merge branch 'dev' into feat/add-native-tokens-tab
brancoder Feb 16, 2024
c77a7ed
fix: resolve conflicts
brancoder Feb 16, 2024
fa78224
fix: add is congestion loading
brancoder Feb 16, 2024
73d5ea0
fix: add state tab for Anchor Address
brancoder Feb 17, 2024
880d17d
fix: resolve conflicts
brancoder Feb 18, 2024
be4b704
Merge branch 'feat/add-bic-tab' into feat/add-state-tab
brancoder Feb 21, 2024
1d9ef02
fix: add state metadata feature
brancoder Feb 21, 2024
9d09fc2
Merge branch 'dev' into feat/add-bic-tab
brancoder Feb 21, 2024
0c5a720
fix: conflict resolved
brancoder Feb 21, 2024
3450cba
feat: add nft section
brancoder Feb 22, 2024
9ffa6b4
feat: add nft section + fix:add metadata entries FeaturesView and As…
brancoder Feb 22, 2024
fc8fd4c
fix: conflict resolved
brancoder Feb 22, 2024
7fc235e
fix: add null checks in BIC section
brancoder Feb 22, 2024
f1cd3e3
Merge branch 'dev' into feat/add-nft-tab
brancoder Feb 23, 2024
11be5ec
fix: remove leftover nft metadata property in AddressPageTabbedSection
brancoder Feb 23, 2024
6b4ae11
feat: add validators tab to account address page
brancoder Feb 23, 2024
bb32fc0
fix: update sdk commit
brancoder Feb 26, 2024
dd303e6
Merge branch 'dev' into feat/add-nft-tab
msarcev Feb 26, 2024
5fb6494
Merge branch 'feat/add-nft-tab' into feat/add-validation-tab
msarcev Feb 26, 2024
7f8be4b
Add delegation section
brancoder Feb 26, 2024
77e905d
fix: show validator tab if account has staking feature
brancoder Feb 26, 2024
a9c006a
Merge branch 'feat/add-validation-tab' of github.com:iotaledger/explo…
brancoder Feb 26, 2024
774e2aa
Merge branch 'feat/add-validation-tab' into feat/add-delegation-tab
brancoder Feb 26, 2024
a0e1aeb
Add fetch delegation ids endpoint
brancoder Feb 26, 2024
2b52a49
fix: key string
brancoder Feb 26, 2024
77a72d8
Add hook to fetch delegation outputs
brancoder Feb 26, 2024
4d47364
Merge branch 'feat/add-validation-tab' into feat/add-delegation-tab
brancoder Feb 26, 2024
4a6a170
Show delegation outputs total amount
brancoder Feb 26, 2024
d882fdc
Merge branch 'dev' into feat/add-nft-tab
brancoder Feb 26, 2024
c02eef6
Merge branch 'feat/add-nft-tab' of github.com:iotaledger/explorer int…
brancoder Feb 26, 2024
2966225
Feat: Add validators tab to account address page (#1183)
brancoder Feb 26, 2024
25bb7e0
fix: conflict resolved
brancoder Feb 26, 2024
8d60228
fix: conflict resolved
brancoder Feb 27, 2024
f51570f
add delegation output rewards to Delegation Section
brancoder Feb 27, 2024
b29d47b
fix: resolve conflicts
brancoder Feb 27, 2024
79b2702
Merge branch 'dev' into feat/add-delegation-tab
begonaalvarezd Feb 28, 2024
887df57
fix: sdk changes to address calculations + styling fix
brancoder Feb 29, 2024
10b660d
Merge branch 'feat/add-delegation-tab' of github.com:iotaledger/explo…
brancoder Feb 29, 2024
04b556e
Merge branch 'dev' into feat/add-delegation-tab
brancoder Feb 29, 2024
3ec0ef0
fix: delegation response types
brancoder Mar 1, 2024
5712330
fix: Delegation Section models
brancoder Mar 5, 2024
758aca4
fix: lint errors
brancoder Mar 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions api/src/models/api/nova/IDelegationDetailsResponse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { IDelegationWithDetails } from "./IDelegationWithDetails";
import { IResponse } from "./IResponse";

export interface IDelegationDetailsResponse extends IResponse {
/**
* The outputs data.
*/
outputs?: IDelegationWithDetails[];
}
16 changes: 16 additions & 0 deletions api/src/models/api/nova/IDelegationWithDetails.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/* eslint-disable import/no-unresolved */
/* eslint-disable @typescript-eslint/no-unsafe-argument */
import { OutputWithMetadataResponse } from "@iota/sdk-nova";
import { IRewardsResponse } from "./IRewardsResponse";

export interface IDelegationWithDetails {
/**
* The output.
*/
output: OutputWithMetadataResponse;

/**
* The rewards for the output.
*/
rewards: IRewardsResponse;
}
6 changes: 6 additions & 0 deletions api/src/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,12 @@ export const routes: IRoute[] = [
folder: "nova/address/outputs/nft",
func: "get",
},
{
path: "/nova/address/outputs/delegation/:network/:address",
method: "get",
folder: "nova/address/outputs/delegation",
func: "get",
},
{
path: "/nova/output/associated/:network/:address",
method: "post",
Expand Down
30 changes: 30 additions & 0 deletions api/src/routes/nova/address/outputs/delegation/get.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { ServiceFactory } from "../../../../../factories/serviceFactory";
import { IAddressDetailsRequest } from "../../../../../models/api/nova/IAddressDetailsRequest";
import { IDelegationDetailsResponse } from "../../../../../models/api/nova/IDelegationDetailsResponse";
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";

/**
* Fetch the delegation output details by address.
* @param config The configuration.
* @param request The request.
* @returns The response.
*/
export async function get(config: IConfiguration, request: IAddressDetailsRequest): Promise<IDelegationDetailsResponse> {
const networkService = ServiceFactory.get<NetworkService>("network");
const networks = networkService.networkNames();
ValidationHelper.oneOf(request.network, networks, "network");
ValidationHelper.string(request.address, "address");

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

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

const novaApiService = ServiceFactory.get<NovaApiService>(`api-service-${networkConfig.network}`);
return novaApiService.delegationOutputDetailsByAddress(request.address);
}
55 changes: 55 additions & 0 deletions api/src/services/nova/novaApiService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import { IAnchorDetailsResponse } from "../../models/api/nova/IAnchorDetailsResp
import { IBlockDetailsResponse } from "../../models/api/nova/IBlockDetailsResponse";
import { IBlockResponse } from "../../models/api/nova/IBlockResponse";
import { ICongestionResponse } from "../../models/api/nova/ICongestionResponse";
import { IDelegationDetailsResponse } from "../../models/api/nova/IDelegationDetailsResponse";
import { IDelegationWithDetails } from "../../models/api/nova/IDelegationWithDetails";
import { INftDetailsResponse } from "../../models/api/nova/INftDetailsResponse";
import { IOutputDetailsResponse } from "../../models/api/nova/IOutputDetailsResponse";
import { IRewardsResponse } from "../../models/api/nova/IRewardsResponse";
Expand Down Expand Up @@ -270,6 +272,25 @@ export class NovaApiService {
}
}

/**
* Get the outputs mana rewards.
* @param outputIds The output ids to get the mana rewards for.
* @returns The rewards details.
*/
public async outputsRewardsDetails(outputIds: string[]): Promise<IRewardsResponse[]> {
const promises: Promise<IRewardsResponse>[] = [];

for (const outputId of outputIds) {
const promise = this.getRewards(outputId);
promises.push(promise);
}
try {
return await Promise.all(promises);
} catch (e) {
logger.error(`Fetching outputs rewards failed. Cause: ${e}`);
}
}

/**
* Get the relevant basic output details for an address.
* @param addressBech32 The address in bech32 format.
Expand Down Expand Up @@ -323,6 +344,40 @@ export class NovaApiService {
};
}

/**
* Get the relevant basic output details for an address.
* @param addressBech32 The address in bech32 format.
* @returns The basic output details.
*/
public async delegationOutputDetailsByAddress(addressBech32: string): Promise<IDelegationDetailsResponse> {
let cursor: string | undefined;
let outputIds: string[] = [];
const delegationResponse: IDelegationWithDetails[] = [];

do {
try {
const outputIdsResponse = await this.client.delegationOutputIds({ address: addressBech32, cursor: cursor ?? "" });

outputIds = outputIds.concat(outputIdsResponse.items);
cursor = outputIdsResponse.cursor;
} catch (e) {
logger.error(`Fetching delegation output ids failed. Cause: ${e}`);
}
} while (cursor);

const outputRewards = await this.outputsRewardsDetails(outputIds);
const outputResponses = await this.outputsDetails(outputIds);

for (const outputResponse of outputResponses) {
const matchingReward = outputRewards?.find((outputReward) => outputReward.outputId === outputResponse.metadata.outputId);
delegationResponse.push({ rewards: matchingReward, output: outputResponse });
}

return {
outputs: delegationResponse,
};
}

/**
* Get Congestion for Account
* @param accountId The account address to get the congestion for.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import foundriesMessage from "~assets/modals/stardust/alias/foundries.json";
import stateMessage from "~assets/modals/stardust/alias/state.json";
import bicMessage from "~assets/modals/nova/account/bic.json";
import validatorMessage from "~assets/modals/nova/account/validator.json";
import delegationMessage from "~assets/modals/nova/delegation.json";
import nftMetadataMessage from "~assets/modals/stardust/nft/metadata.json";
import addressNftsMessage from "~assets/modals/stardust/address/nfts-in-wallet.json";
import TabbedSection from "../../../hoc/TabbedSection";
Expand All @@ -26,12 +27,14 @@ import NftSection from "~/app/components/nova/address/section/nft/NftSection";
import NftMetadataSection from "~/app/components/nova/address/section/nft/NftMetadataSection";
import { TransactionsHelper } from "~/helpers/nova/transactionsHelper";
import AccountValidatorSection from "./account/AccountValidatorSection";
import DelegationSection from "./delegation/DelegationSection";

enum DEFAULT_TABS {
Transactions = "Transactions",
AssocOutputs = "Outputs",
NativeTokens = "Native Tokens",
Nfts = "NFTs",
Delegation = "Delegation",
}

enum ACCOUNT_TABS {
Expand All @@ -52,8 +55,10 @@ const buildDefaultTabsOptions = (
tokensCount: number,
nftsCount: number,
associatedOutputCount: number,
delegationCount: number,
isNativeTokensLoading: boolean,
isNftOutputsLoading: boolean,
isDelegationOutputsLoading: boolean,
isAddressHistoryLoading: boolean,
isAddressHistoryDisabled: boolean,
) => ({
Expand Down Expand Up @@ -83,6 +88,13 @@ const buildDefaultTabsOptions = (
isLoading: isNftOutputsLoading,
infoContent: addressNftsMessage,
},
[DEFAULT_TABS.Delegation]: {
disabled: delegationCount === 0,
hidden: delegationCount === 0,
counter: delegationCount,
isLoading: isDelegationOutputsLoading,
infoContent: delegationMessage,
},
});

const buildAccountAddressTabsOptions = (
Expand Down Expand Up @@ -156,7 +168,15 @@ export const AddressPageTabbedSections: React.FC<IAddressPageTabbedSectionsProps
if (!addressState.addressDetails) {
return null;
}
const { addressDetails, addressBasicOutputs, isAddressHistoryLoading, isAddressHistoryDisabled } = addressState;
const {
addressDetails,
addressBasicOutputs,
isBasicOutputsLoading,
isNftOutputsLoading,
isDelegationOutputsLoading,
isAddressHistoryLoading,
isAddressHistoryDisabled,
} = addressState;
const { bech32: addressBech32 } = addressDetails;
const { name: network } = networkInfo;

Expand All @@ -176,6 +196,7 @@ export const AddressPageTabbedSections: React.FC<IAddressPageTabbedSectionsProps
/>,
<AssetsTable key={`assets-table-${addressBech32}`} outputs={addressBasicOutputs} setTokensCount={setTokensCount} />,
<NftSection key={`nft-section-${addressBech32}`} outputs={addressState.addressNftOutputs} />,
<DelegationSection key={`delegation-${addressBech32}`} delegationDetails={addressState.addressDelegationOutputs} />,
];

const accountAddressSections =
Expand Down Expand Up @@ -209,12 +230,15 @@ export const AddressPageTabbedSections: React.FC<IAddressPageTabbedSectionsProps

let tabEnums = DEFAULT_TABS;
const nftsCount = addressState.addressNftOutputs?.length ?? 0;
const delegationCount = addressState.addressDelegationOutputs?.length ?? 0;
const defaultTabsOptions = buildDefaultTabsOptions(
tokensCount,
nftsCount,
outputCount,
addressState.isBasicOutputsLoading,
addressState.isNftOutputsLoading,
delegationCount,
isBasicOutputsLoading,
isNftOutputsLoading,
isDelegationOutputsLoading,
isAddressHistoryLoading,
isAddressHistoryDisabled,
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
@import "../../../../../../scss/fonts";
@import "../../../../../../scss/media-queries";
@import "../../../../../../scss/mixins";
@import "../../../../../../scss/variables";
@import "../../../../../../scss/themes";

.table--delegation {
width: 100%;
border-spacing: 12px 28px;
border-collapse: separate;

@include tablet-down {
display: none;
}

tr {
@include font-size(14px);

color: $gray-7;
font-family: $inter;
letter-spacing: 0.5px;

th {
@include font-size(12px);

color: $gray-6;
font-weight: 600;
text-align: left;
text-transform: uppercase;
}

td {
color: var(--body-color);

&.highlight {
color: var(--link-color);
@include font-size(14px);

font-family: $ibm-plex-mono;
font-weight: normal;
letter-spacing: 0.02em;
line-height: 20px;

a {
max-width: 200px;
}
}
&.truncate {
max-width: 150px;
}
}
}
}

.cards--delegation {
display: none;

@include tablet-down {
display: block;
}
.card--delegation {
margin-bottom: 16px;
padding: 8px;

.field {
margin-bottom: 8px;

.label {
color: $gray-6;
font-family: $inter;
letter-spacing: 0.5px;

@include font-size(14px, 21px);
}

.value {
@include font-size(14px, 21px);

color: var(--body-color);
font-family: $metropolis;
font-weight: 700;

.highlight {
color: var(--link-color);
@include font-size(14px);

font-family: $ibm-plex-mono;
font-weight: normal;
letter-spacing: 0.02em;
line-height: 20px;
max-width: 200px;
}
}
}

.field:last-child {
margin-bottom: 0;
}
}
}
Loading
Loading