Skip to content

Commit

Permalink
Merge pull request #831 from alleslabs/feature/cfe-381-bonded-changes…
Browse files Browse the repository at this point in the history
…-transaction

[CFE-381]: Feature - bonded changes transaction
  • Loading branch information
Poafs1 committed Mar 22, 2024
2 parents cbeacbc + baa08bc commit 8e0741a
Show file tree
Hide file tree
Showing 16 changed files with 480 additions and 65 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Features

- [#833](https://github.com/alleslabs/celatone-frontend/pull/833) Add link to transactions page to current bonded token component
- [#832](https://github.com/alleslabs/celatone-frontend/pull/832) Add bonded token changes delegation related transactions
- [#831](https://github.com/alleslabs/celatone-frontend/pull/831) Update zod schema to apply with new validator delegation related txs api spec
- [#828](https://github.com/alleslabs/celatone-frontend/pull/828) Add validator detail voting power overview section with data from APIs
- [#835](https://github.com/alleslabs/celatone-frontend/pull/835) Add amp view in json - block and proposal details
- [#823](https://github.com/alleslabs/celatone-frontend/pull/823) Add validator uptime and penalty events
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { Box, Flex, Heading, Text } from "@chakra-ui/react";
import { Box, Button, Flex, Heading, Text } from "@chakra-ui/react";
import type { BigSource } from "big.js";
import type { ScriptableContext, TooltipModel } from "chart.js";

import { TabIndex } from "../../types";
import { useInternalNavigate } from "lib/app-provider";
import { LineChart } from "lib/components/chart/LineChart";
import { CustomIcon } from "lib/components/icon";
import { Loading } from "lib/components/Loading";
import { ErrorFetching } from "lib/components/state";
import { useValidatorHistoricalPowers } from "lib/services/validatorService";
Expand All @@ -17,13 +20,17 @@ interface VotingPowerChartProps {
validatorAddress: ValidatorAddr;
singleStakingDenom: Option<string>;
assetInfos: Option<AssetInfos>;
isOverview?: boolean;
}

export const VotingPowerChart = ({
validatorAddress,
singleStakingDenom,
assetInfos,
isOverview,
}: VotingPowerChartProps) => {
const navigate = useInternalNavigate();

const { data: historicalPowers, isLoading } =
useValidatorHistoricalPowers(validatorAddress);

Expand Down Expand Up @@ -115,27 +122,50 @@ export const VotingPowerChart = ({
rounded={8}
w="100%"
>
<Flex gap={2} direction="column" w={250} minW={250}>
<Heading variant="h6">
{singleStakingDenom
? "Current Bonded Token"
: "Current Voting Powers"}
</Heading>
<Heading variant="h5" fontWeight={600}>
{currentPrice} {currency}
</Heading>
<Text variant="body1">
<Text
as="span"
fontWeight={700}
color={diffInLast24Hr >= 0 ? "success.main" : "error.main"}
<Flex gap={6} direction="column" w={250} minW={250}>
<Flex gap={2} direction="column">
<Heading variant="h6">
{singleStakingDenom
? "Current Bonded Token"
: "Current Voting Powers"}
</Heading>
<Heading variant="h5" fontWeight={600}>
{currentPrice} {currency}
</Heading>
<Text variant="body1">
<Text
as="span"
fontWeight={700}
color={diffInLast24Hr >= 0 ? "success.main" : "error.main"}
>
{diffInLast24Hr >= 0
? `+${handleFormatValue(diffInLast24Hr)}`
: `-${handleFormatValue(-diffInLast24Hr)}`}
</Text>{" "}
{currency} in last 24 hr
</Text>
</Flex>
{isOverview && (
<Button
variant="ghost-secondary"
p="unset"
size="md"
pl={2}
onClick={() =>
navigate({
pathname: "/validators/[validatorAddress]/[tab]",
query: {
validatorAddress,
tab: TabIndex.BondedTokenChanges,
},
options: { shallow: true },
})
}
>
{diffInLast24Hr >= 0
? `+${handleFormatValue(diffInLast24Hr)}`
: `-${handleFormatValue(-diffInLast24Hr)}`}
</Text>{" "}
{currency} in last 24 hr
</Text>
See all related transactions
<CustomIcon name="chevron-right" boxSize={3} />
</Button>
)}
</Flex>
<Box w="100%" h="272px" id="voting-power-chart-container">
<LineChart
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { Flex } from "@chakra-ui/react";
import { Box, Flex } from "@chakra-ui/react";

import { RelatedTransactionTable } from "../tables/RelatedTransactionsTable";
import { RelatedTransactionTable } from "../tables";
import { useMobile } from "lib/app-provider";
import { Pagination } from "lib/components/pagination";
import { usePaginator } from "lib/components/pagination/usePaginator";
import { TableTitle } from "lib/components/table";
import { useValidatorDelegationRelatedTxs } from "lib/services/validatorService";
import type { AssetInfos, Option, ValidatorAddr } from "lib/types";

import { VotingPowerChart } from "./VotingPowerChart";
Expand All @@ -15,13 +20,74 @@ export const BondedTokenChanges = ({
validatorAddress,
singleStakingDenom,
assetInfos,
}: BondedTokenChangesProps) => (
<Flex direction="column" gap={{ base: 4, md: 8 }} pt={6}>
<VotingPowerChart
validatorAddress={validatorAddress}
singleStakingDenom={singleStakingDenom}
assetInfos={assetInfos}
/>
<RelatedTransactionTable />
</Flex>
);
}: BondedTokenChangesProps) => {
const isMobile = useMobile();

const {
pagesQuantity,
setTotalData,
currentPage,
setCurrentPage,
pageSize,
setPageSize,
offset,
} = usePaginator({
initialState: {
pageSize: 10,
currentPage: 1,
isDisabled: false,
},
});

const { data, isLoading } = useValidatorDelegationRelatedTxs(
validatorAddress,
pageSize,
offset,
{
onSuccess: ({ total }) => setTotalData(total),
}
);

const tableHeaderId = "relatedTransactionTableHeader";

return (
<Flex direction="column" gap={{ base: 4, md: 8 }} pt={6}>
<VotingPowerChart
validatorAddress={validatorAddress}
singleStakingDenom={singleStakingDenom}
assetInfos={assetInfos}
/>
<Box>
{!isMobile && (
<TableTitle
title="Delegation-Related Transactions"
count={data?.total ?? 0}
id={tableHeaderId}
helperText="Shows transactions relevant to changes in delegated tokens, excluding any token reduction due to slashing."
/>
)}
<RelatedTransactionTable
delegationRelatedTxs={data?.items}
isLoading={isLoading}
assetInfos={assetInfos}
/>
{!!data?.total && data.total > 10 && (
<Pagination
currentPage={currentPage}
pagesQuantity={pagesQuantity}
offset={offset}
totalData={data.total}
scrollComponentId={tableHeaderId}
pageSize={pageSize}
onPageChange={setCurrentPage}
onPageSizeChange={(e) => {
const size = Number(e.target.value);
setPageSize(size);
setCurrentPage(1);
}}
/>
)}
</Box>
</Flex>
);
};

This file was deleted.

2 changes: 1 addition & 1 deletion src/lib/pages/validator-details/components/tables/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export * from "./ProposedBlocksTable";
export * from "./RelatedTransactionsTable";
export * from "./VotedProposalsTable";
export * from "./related-transactions";
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { Box, Flex, Text } from "@chakra-ui/react";
import type { BigSource } from "big.js";
import type Big from "big.js";

import { TokenImageRender } from "lib/components/token";
import { getUndefinedTokenIcon } from "lib/pages/pools/utils";
import type { AssetInfos, Coin, Option, Token, U, USD } from "lib/types";
import {
coinToTokenWithValue,
formatPrice,
formatUTokenWithPrecision,
getTokenLabel,
} from "lib/utils";

interface RelatedTransactionsBondedTokenChangesProps {
txHash: string;
coin: Coin;
assetInfos: Option<AssetInfos>;
}

export const RelatedTransactionsBondedTokenChanges = ({
txHash,
coin,
assetInfos,
}: RelatedTransactionsBondedTokenChangesProps) => {
const token = coinToTokenWithValue(coin.denom, coin.amount, assetInfos);
const isPositiveAmount = token.amount.gte(0);
const formattedAmount = `${isPositiveAmount ? "+" : "-"}${formatUTokenWithPrecision(token.amount.abs() as U<Token<Big>>, token.precision ?? 0, false, 2)}`;

return (
<Flex
gap={2}
key={`${txHash}-${coin.denom}`}
w="100%"
justifyContent={{ base: "start", md: "end" }}
alignItems="center"
>
<Box textAlign={{ base: "left", md: "right" }}>
<Flex alignItems="center" gap={1}>
<Text
fontWeight={700}
color={isPositiveAmount ? "success.main" : "error.main"}
>
{formattedAmount}
</Text>
<Text>{getTokenLabel(token.denom, token.symbol)}</Text>
<TokenImageRender
display={{ base: "block", md: "none" }}
boxSize={4}
logo={token.logo ?? getUndefinedTokenIcon(token.denom)}
/>
</Flex>
<Text variant="body3" color="text.dark">
{token.value
? `(${formatPrice(token.value.abs() as USD<BigSource>)})`
: "-"}
</Text>
</Box>
<TokenImageRender
display={{ base: "none", md: "block" }}
boxSize={6}
logo={token.logo ?? getUndefinedTokenIcon(token.denom)}
/>
</Flex>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { Badge, Box, Flex, Grid, GridItem, Text } from "@chakra-ui/react";

import { ExplorerLink } from "lib/components/ExplorerLink";
import { MobileCardTemplate } from "lib/components/table";
import type { ValidatorDelegationRelatedTxsResponseItem } from "lib/services/validator";
import type { AssetInfos, Option } from "lib/types";
import { dateFromNow, extractMsgType, formatUTC } from "lib/utils";

import { RelatedTransactionsBondedTokenChanges } from "./RelatedTransactionsBondedTokenChanges";

interface RelatedTransactionsMobileCardProps {
delegationRelatedTx: ValidatorDelegationRelatedTxsResponseItem;
assetInfos: Option<AssetInfos>;
onRowSelect: (txHash: string) => void;
}

export const RelatedTransactionsMobileCard = ({
delegationRelatedTx,
assetInfos,
onRowSelect,
}: RelatedTransactionsMobileCardProps) => (
<MobileCardTemplate
topContent={
<Flex w="100%" flexDirection="column" gap={2}>
<Grid templateColumns="1fr 1fr" gap={2}>
<GridItem>
<Text variant="body3" color="text.dark" fontWeight={600}>
Transaction Hash
</Text>
<ExplorerLink
value={delegationRelatedTx.txHash.toLocaleUpperCase()}
type="tx_hash"
showCopyOnHover
/>
{delegationRelatedTx.messages.length > 1 && (
<Badge variant="secondary" ml={2}>
{delegationRelatedTx.messages.length}
</Badge>
)}
</GridItem>
<GridItem>
<Text variant="body3" color="text.dark" fontWeight={600}>
Sender
</Text>
<ExplorerLink
value={delegationRelatedTx.sender}
type="user_address"
showCopyOnHover
/>
</GridItem>
</Grid>
<Box>
<Text variant="body3" color="text.dark" fontWeight={600}>
Action
</Text>
<Text variant="body2" color="white">
{delegationRelatedTx.messages.length > 1
? `${delegationRelatedTx.messages.length} Messages`
: extractMsgType(delegationRelatedTx.messages[0].type)}
</Text>
</Box>
<Box>
<Text variant="body3" color="text.dark" fontWeight={600}>
Bonded Token Changes
</Text>
{delegationRelatedTx.tokens.map((token) => (
<RelatedTransactionsBondedTokenChanges
txHash={delegationRelatedTx.txHash}
coin={token}
assetInfos={assetInfos}
key={delegationRelatedTx.txHash + token.amount + token.denom}
/>
))}
</Box>
</Flex>
}
bottomContent={
<Box>
<Text variant="body2" color="text.dark">
{formatUTC(delegationRelatedTx.timestamp)}
</Text>
<Text variant="body3" color="text.disabled">
{`(${dateFromNow(delegationRelatedTx.timestamp)})`}
</Text>
</Box>
}
onClick={() => onRowSelect(delegationRelatedTx.txHash)}
/>
);
Loading

0 comments on commit 8e0741a

Please sign in to comment.