Skip to content

Commit

Permalink
Merge pull request #402 from alleslabs/feat/keybase-val-img
Browse files Browse the repository at this point in the history
fix: add validator image resolver hook (add keybase as a fallback opt…
  • Loading branch information
evilpeach committed Jul 18, 2023
2 parents 79a2ab3 + cc1ed9c commit 527b714
Show file tree
Hide file tree
Showing 12 changed files with 172 additions and 64 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Improvements

- [#402](https://github.com/alleslabs/celatone-frontend/pull/402) Add validator image resolver hook (add keybase as a fallback option)
- [#408](https://github.com/alleslabs/celatone-frontend/pull/408) Improve dropdown and combo box interaction
- [#421](https://github.com/alleslabs/celatone-frontend/pull/421) Generate example addresses from a fixed-bytes array
- [#431](https://github.com/alleslabs/celatone-frontend/pull/431) Add new Osmosis v16 tx messages
Expand Down
6 changes: 6 additions & 0 deletions public/validator.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
36 changes: 17 additions & 19 deletions src/lib/components/ValidatorBadge.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import type { ImageProps } from "@chakra-ui/react";
import { Flex, Image, Text } from "@chakra-ui/react";
import { Spinner, Flex, Image, Text } from "@chakra-ui/react";

import { CURR_THEME } from "env";
import { useCelatoneApp, useMobile } from "lib/app-provider";
import validatorDefaultImg from "../../../public/validator.svg";
import { useMobile } from "lib/app-provider";
import { ExplorerLink } from "lib/components/ExplorerLink";
import { MobileLabel } from "lib/pages/account-details/components/mobile/MobileLabel";
import { useValidatorImage } from "lib/services/validatorService";
import type { ValidatorInfo } from "lib/types";
import { removeSpecialChars } from "lib/utils";

interface ValidatorBadgeProps {
validator: ValidatorInfo | null;
Expand Down Expand Up @@ -41,26 +41,24 @@ export const ValidatorBadge = ({
maxWidth = "160px",
hasLabel = true,
}: ValidatorBadgeProps) => {
const {
chainConfig: { chain },
} = useCelatoneApp();
const { data: valImgSrc, isLoading } = useValidatorImage(validator);
const isMobile = useMobile();
return (
<Flex alignItems="center" gap={2}>
{validator ? (
<>
<Image
boxSize={badgeSize}
src={`https://raw.githubusercontent.com/cosmostation/chainlist/master/chain/${chain}/moniker/${validator.validatorAddress}.png`}
alt={validator.moniker}
fallbackSrc={`https://ui-avatars.com/api/?name=${removeSpecialChars(
validator.moniker ?? ""
)}&background=${CURR_THEME.colors.secondary.main.replace(
"#",
""
)}&color=fff`}
borderRadius="50%"
/>
{isLoading ? (
<Spinner boxSize={badgeSize} />
) : (
<Image
boxSize={badgeSize}
src={valImgSrc}
alt={validator.moniker}
borderRadius="50%"
fallbackSrc={validatorDefaultImg.src}
fallbackStrategy="onError"
/>
)}
<Flex direction="column">
{isMobile && hasLabel && <MobileLabel label="Validator" />}
<ExplorerLink
Expand Down
12 changes: 6 additions & 6 deletions src/lib/gql/gql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ const documents = {
types.GetAccountIdByAddressQueryDocumentDocument,
"\n query getBlockTimestampByHeightQuery($height: Int!) {\n blocks_by_pk(height: $height) {\n timestamp\n }\n }\n":
types.GetBlockTimestampByHeightQueryDocument,
"\n query getBlockListQuery($limit: Int!, $offset: Int!) {\n blocks(limit: $limit, offset: $offset, order_by: { height: desc }) {\n hash\n height\n timestamp\n transactions_aggregate {\n aggregate {\n count\n }\n }\n validator {\n moniker\n operator_address\n }\n }\n }\n":
"\n query getBlockListQuery($limit: Int!, $offset: Int!) {\n blocks(limit: $limit, offset: $offset, order_by: { height: desc }) {\n hash\n height\n timestamp\n transactions_aggregate {\n aggregate {\n count\n }\n }\n validator {\n moniker\n operator_address\n identity\n }\n }\n }\n":
types.GetBlockListQueryDocument,
"\n query getBlockDetailsByHeight($height: Int!) {\n blocks_by_pk(height: $height) {\n hash\n height\n timestamp\n transactions_aggregate {\n aggregate {\n sum {\n gas_used\n gas_limit\n }\n }\n }\n validator {\n moniker\n operator_address\n }\n }\n }\n":
"\n query getBlockDetailsByHeight($height: Int!) {\n blocks_by_pk(height: $height) {\n hash\n height\n timestamp\n transactions_aggregate {\n aggregate {\n sum {\n gas_used\n gas_limit\n }\n }\n }\n validator {\n moniker\n operator_address\n identity\n }\n }\n }\n":
types.GetBlockDetailsByHeightDocument,
"\n query getLatestBlockInfo {\n blocks(limit: 1, order_by: { height: desc }) {\n height\n timestamp\n }\n }\n":
types.GetLatestBlockInfoDocument,
Expand Down Expand Up @@ -106,11 +106,11 @@ export function graphql(
source: "\n query getBlockTimestampByHeightQuery($height: Int!) {\n blocks_by_pk(height: $height) {\n timestamp\n }\n }\n"
): typeof documents["\n query getBlockTimestampByHeightQuery($height: Int!) {\n blocks_by_pk(height: $height) {\n timestamp\n }\n }\n"];
export function graphql(
source: "\n query getBlockListQuery($limit: Int!, $offset: Int!) {\n blocks(limit: $limit, offset: $offset, order_by: { height: desc }) {\n hash\n height\n timestamp\n transactions_aggregate {\n aggregate {\n count\n }\n }\n validator {\n moniker\n operator_address\n }\n }\n }\n"
): typeof documents["\n query getBlockListQuery($limit: Int!, $offset: Int!) {\n blocks(limit: $limit, offset: $offset, order_by: { height: desc }) {\n hash\n height\n timestamp\n transactions_aggregate {\n aggregate {\n count\n }\n }\n validator {\n moniker\n operator_address\n }\n }\n }\n"];
source: "\n query getBlockListQuery($limit: Int!, $offset: Int!) {\n blocks(limit: $limit, offset: $offset, order_by: { height: desc }) {\n hash\n height\n timestamp\n transactions_aggregate {\n aggregate {\n count\n }\n }\n validator {\n moniker\n operator_address\n identity\n }\n }\n }\n"
): typeof documents["\n query getBlockListQuery($limit: Int!, $offset: Int!) {\n blocks(limit: $limit, offset: $offset, order_by: { height: desc }) {\n hash\n height\n timestamp\n transactions_aggregate {\n aggregate {\n count\n }\n }\n validator {\n moniker\n operator_address\n identity\n }\n }\n }\n"];
export function graphql(
source: "\n query getBlockDetailsByHeight($height: Int!) {\n blocks_by_pk(height: $height) {\n hash\n height\n timestamp\n transactions_aggregate {\n aggregate {\n sum {\n gas_used\n gas_limit\n }\n }\n }\n validator {\n moniker\n operator_address\n }\n }\n }\n"
): typeof documents["\n query getBlockDetailsByHeight($height: Int!) {\n blocks_by_pk(height: $height) {\n hash\n height\n timestamp\n transactions_aggregate {\n aggregate {\n sum {\n gas_used\n gas_limit\n }\n }\n }\n validator {\n moniker\n operator_address\n }\n }\n }\n"];
source: "\n query getBlockDetailsByHeight($height: Int!) {\n blocks_by_pk(height: $height) {\n hash\n height\n timestamp\n transactions_aggregate {\n aggregate {\n sum {\n gas_used\n gas_limit\n }\n }\n }\n validator {\n moniker\n operator_address\n identity\n }\n }\n }\n"
): typeof documents["\n query getBlockDetailsByHeight($height: Int!) {\n blocks_by_pk(height: $height) {\n hash\n height\n timestamp\n transactions_aggregate {\n aggregate {\n sum {\n gas_used\n gas_limit\n }\n }\n }\n validator {\n moniker\n operator_address\n identity\n }\n }\n }\n"];
export function graphql(
source: "\n query getLatestBlockInfo {\n blocks(limit: 1, order_by: { height: desc }) {\n height\n timestamp\n }\n }\n"
): typeof documents["\n query getLatestBlockInfo {\n blocks(limit: 1, order_by: { height: desc }) {\n height\n timestamp\n }\n }\n"];
Expand Down
10 changes: 10 additions & 0 deletions src/lib/gql/graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11878,6 +11878,7 @@ export type GetBlockListQueryQuery = {
__typename?: "validators";
moniker: string;
operator_address: string;
identity: string;
} | null;
}>;
};
Expand Down Expand Up @@ -11908,6 +11909,7 @@ export type GetBlockDetailsByHeightQuery = {
__typename?: "validators";
moniker: string;
operator_address: string;
identity: string;
} | null;
} | null;
};
Expand Down Expand Up @@ -12919,6 +12921,10 @@ export const GetBlockListQueryDocument = {
kind: "Field",
name: { kind: "Name", value: "operator_address" },
},
{
kind: "Field",
name: { kind: "Name", value: "identity" },
},
],
},
},
Expand Down Expand Up @@ -13024,6 +13030,10 @@ export const GetBlockDetailsByHeightDocument = {
kind: "Field",
name: { kind: "Name", value: "operator_address" },
},
{
kind: "Field",
name: { kind: "Name", value: "identity" },
},
],
},
},
Expand Down
4 changes: 4 additions & 0 deletions src/lib/pages/account-details/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ export const useUserDelegationInfos = (walletAddress: HumanAddr) => {
validator: {
validatorAddress: raw.validatorAddress,
moniker: validators[raw.validatorAddress]?.moniker,
identity: raw.identity,
},
token: coinToTokenWithValue(raw.denom, raw.amount, assetInfos[raw.denom]),
}));
Expand All @@ -296,6 +297,7 @@ export const useUserDelegationInfos = (walletAddress: HumanAddr) => {
validator: {
validatorAddress: raw.validatorAddress,
moniker: validators[raw.validatorAddress]?.moniker,
identity: raw.identity,
},
completionTime: raw.completionTime,
token: coinToTokenWithValue(
Expand Down Expand Up @@ -344,10 +346,12 @@ export const useUserDelegationInfos = (walletAddress: HumanAddr) => {
srcValidator: {
validatorAddress: raw.srcValidatorAddress,
moniker: validators[raw.srcValidatorAddress]?.moniker,
identity: raw.srcIdentity,
},
dstValidator: {
validatorAddress: raw.dstValidatorAddress,
moniker: validators[raw.dstValidatorAddress]?.moniker,
identity: raw.dstIdentity,
},
completionTime: raw.completionTime,
token: coinToTokenWithValue(
Expand Down
2 changes: 2 additions & 0 deletions src/lib/query/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export const getBlockListQueryDocument = graphql(`
validator {
moniker
operator_address
identity
}
}
}
Expand All @@ -44,6 +45,7 @@ export const getBlockDetailsByHeightQueryDocument = graphql(`
validator {
moniker
operator_address
identity
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/lib/services/blockService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export const useBlocklistQuery = (
moniker: validator.moniker,
validatorAddress:
validator.operator_address as ValidatorAddr,
identity: validator.identity,
}
: null,
})
Expand Down Expand Up @@ -99,6 +100,7 @@ export const useBlockDetailsQuery = (
moniker: blocks_by_pk.validator.moniker,
validatorAddress: blocks_by_pk.validator
.operator_address as ValidatorAddr,
identity: blocks_by_pk.validator.identity,
}
: null,
}
Expand Down
96 changes: 62 additions & 34 deletions src/lib/services/delegation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import big from "big.js";
import type { Addr, Token, U, ValidatorAddr } from "lib/types";
import { parseDate, formatSeconds } from "lib/utils";

import { getValidator } from "./validator";

interface StakingParamsResponse {
params: {
unbonding_time: string; // e.g. "1209600s"
Expand Down Expand Up @@ -82,12 +84,14 @@ export interface RawStakingParams {

export interface RawDelegation {
validatorAddress: ValidatorAddr;
identity: string;
denom: string;
amount: string;
}

export interface RawUnbonding {
validatorAddress: ValidatorAddr;
identity: string;
completionTime: Date;
amount: string;
}
Expand All @@ -104,7 +108,9 @@ export interface RawDelegationRewards {

export interface RawRedelegation {
srcValidatorAddress: ValidatorAddr;
srcIdentity: string;
dstValidatorAddress: ValidatorAddr;
dstIdentity: string;
completionTime: Date;
amount: string;
}
Expand Down Expand Up @@ -133,14 +139,25 @@ export const getDelegations = async (
const { data } = await axios.get<DelegationResponse>(
`${endpoint}/cosmos/staking/v1beta1/delegations/${address}`
);
return data.delegation_responses
.map<RawDelegation>((delegation) => ({
validatorAddress: delegation.delegation
.validator_address as ValidatorAddr,
denom: delegation.balance.denom,
amount: delegation.balance.amount,
}))
.sort((a, b) => big(b.amount).cmp(a.amount));
return Promise.all(
data.delegation_responses.map<Promise<RawDelegation>>(
async (delegation) => {
const valInfo = await getValidator(
endpoint,
delegation.delegation.validator_address
);
return {
validatorAddress: delegation.delegation
.validator_address as ValidatorAddr,
identity: valInfo.identity,
denom: delegation.balance.denom,
amount: delegation.balance.amount,
};
}
)
).then((delegations) =>
delegations.sort((a, b) => big(b.amount).cmp(a.amount))
);
};

export const getUnbondings = async (
Expand All @@ -150,19 +167,22 @@ export const getUnbondings = async (
const { data } = await axios.get<UnbondingResponse>(
`${endpoint}/cosmos/staking/v1beta1/delegators/${address}/unbonding_delegations`
);
return data.unbonding_responses
.reduce<RawUnbonding[]>(
(prev, validator) =>
prev.concat(
...validator.entries.map((entry) => ({
validatorAddress: validator.validator_address as ValidatorAddr,
completionTime: parseDate(entry.completion_time),
amount: entry.balance,
}))
),
[]
)
.sort((a, b) => a.completionTime.getTime() - b.completionTime.getTime());
return Promise.all(
data.unbonding_responses.map<Promise<RawUnbonding[]>>(async (validator) => {
const valInfo = await getValidator(endpoint, validator.validator_address);

return validator.entries.map<RawUnbonding>((entry) => ({
validatorAddress: validator.validator_address as ValidatorAddr,
identity: valInfo.identity,
completionTime: parseDate(entry.completion_time),
amount: entry.balance,
}));
})
).then((unbondings) =>
unbondings
.flat()
.sort((a, b) => a.completionTime.getTime() - b.completionTime.getTime())
);
};

export const getDelegationRewards = async (
Expand All @@ -188,20 +208,28 @@ export const getRedelegations = async (
const { data } = await axios.get<RedelegationsResponse>(
`${endpoint}/cosmos/staking/v1beta1/delegators/${address}/redelegations`
);
return data.redelegation_responses
.reduce<RawRedelegation[]>(
(prev, redelegate) =>
prev.concat(
...redelegate.entries.map((entry) => ({
srcValidatorAddress: redelegate.redelegation.validator_src_address,
dstValidatorAddress: redelegate.redelegation.validator_dst_address,
completionTime: parseDate(entry.redelegation_entry.completion_time),
amount: entry.balance,
}))
),
[]
return Promise.all(
data.redelegation_responses.map<Promise<RawRedelegation[]>>(
async (redelegate) => {
const [srcValInfo, dstValInfo] = await Promise.all([
getValidator(endpoint, redelegate.redelegation.validator_src_address),
getValidator(endpoint, redelegate.redelegation.validator_dst_address),
]);
return redelegate.entries.map((entry) => ({
srcValidatorAddress: redelegate.redelegation.validator_src_address,
srcIdentity: srcValInfo.identity,
dstValidatorAddress: redelegate.redelegation.validator_dst_address,
dstIdentity: dstValInfo.identity,
completionTime: parseDate(entry.redelegation_entry.completion_time),
amount: entry.balance,
}));
}
)
.sort((a, b) => a.completionTime.getTime() - b.completionTime.getTime());
).then((redelegations) =>
redelegations
.flat()
.sort((a, b) => a.completionTime.getTime() - b.completionTime.getTime())
);
};

export const getCommission = async (
Expand Down
Loading

2 comments on commit 527b714

@vercel
Copy link

@vercel vercel bot commented on 527b714 Jul 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on 527b714 Jul 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.