From 751e2c29a6d3c9d94a9174ba724cc0665827d8de Mon Sep 17 00:00:00 2001 From: luca Date: Tue, 31 Jan 2023 19:56:55 +0800 Subject: [PATCH 1/2] add validator thumbnail pictures --- .../components/react/all-validators.tsx | 55 +++++++++++++--- .../components/react/delegate-modal.tsx | 9 ++- .../components/react/my-validators.tsx | 54 +++++++++++----- .../stake-tokens/components/react/staking.tsx | 62 ++++++++++++++++++- examples/stake-tokens/components/types.tsx | 1 + 5 files changed, 154 insertions(+), 27 deletions(-) diff --git a/examples/stake-tokens/components/react/all-validators.tsx b/examples/stake-tokens/components/react/all-validators.tsx index 9c0ca7dd3..325ca8cf4 100644 --- a/examples/stake-tokens/components/react/all-validators.tsx +++ b/examples/stake-tokens/components/react/all-validators.tsx @@ -24,6 +24,7 @@ import { Text, Image, useColorMode, + Center, } from '@chakra-ui/react'; import { DelegateWarning, @@ -46,6 +47,34 @@ import type { import { TransactionResult } from '../types'; import { ChainName } from '@cosmos-kit/core'; +export const Thumbnail = ({ + identity, + name, + thumbnailUrl, +}: { + identity: string | undefined; + name: string | undefined; + thumbnailUrl: string; +}) => { + return ( + <> + {identity && thumbnailUrl ? ( + {name} + ) : ( +
+ {name && name.slice(0, 1).toUpperCase()} +
+ )} + + ); +}; + const AllValidators = ({ validators, balance, @@ -53,6 +82,7 @@ const AllValidators = ({ updateData, unbondingDays, chainName, + thumbnails, }: { validators: Validator[]; balance: number; @@ -60,6 +90,9 @@ const AllValidators = ({ updateData: () => void; unbondingDays: number; chainName: ChainName; + thumbnails: { + [key: string]: string; + }; }) => { const { isOpen, onOpen, onClose } = useDisclosure(); const { getSigningStargateClient, address } = useChain(chainName); @@ -161,7 +194,11 @@ const AllValidators = ({ {index + 1} - {/* {validator.description.moniker} */} + {validator?.description?.moniker} diff --git a/examples/stake-tokens/components/react/delegate-modal.tsx b/examples/stake-tokens/components/react/delegate-modal.tsx index ed5310d5d..ebf7a2be8 100644 --- a/examples/stake-tokens/components/react/delegate-modal.tsx +++ b/examples/stake-tokens/components/react/delegate-modal.tsx @@ -22,6 +22,7 @@ import { UnorderedList, useColorModeValue, useToast, + Center, } from '@chakra-ui/react'; import { TransactionResult } from '../types'; @@ -37,7 +38,13 @@ export const ValidatorInfo = ({ apr: number; }) => ( - {name} + {imgUrl ? ( + {name} + ) : ( +
+ {name.slice(0, 1).toUpperCase()} +
+ )} {name} diff --git a/examples/stake-tokens/components/react/my-validators.tsx b/examples/stake-tokens/components/react/my-validators.tsx index b7517bd42..8d623fb1f 100644 --- a/examples/stake-tokens/components/react/my-validators.tsx +++ b/examples/stake-tokens/components/react/my-validators.tsx @@ -47,6 +47,7 @@ import type { } from 'interchain/types/codegen/cosmos/staking/v1beta1/staking'; import type { DelegationDelegatorReward as Reward } from 'interchain/types/codegen/cosmos/distribution/v1beta1/distribution'; import { ChainName } from '@cosmos-kit/core'; +import { Thumbnail } from './all-validators'; const MyValidators = ({ validators, @@ -57,6 +58,7 @@ const MyValidators = ({ updateData, unbondingDays, chainName, + thumbnails, }: { validators: Validator[]; allValidator: Validator[]; @@ -66,6 +68,9 @@ const MyValidators = ({ updateData: () => void; unbondingDays: number; chainName: ChainName; + thumbnails: { + [key: string]: string; + }; }) => { const { getSigningStargateClient, address } = useChain(chainName); @@ -152,6 +157,7 @@ const MyValidators = ({ return { details: validator?.description?.details, name: validator?.description?.moniker, + identity: validator?.description?.identity, address: validator.operatorAddress, staked: exponentiate(delegation.balance!.amount, -exp), reward: Number(exponentiate(rewardAmount, -exp).toFixed(6)), @@ -379,7 +385,11 @@ const MyValidators = ({ {index + 1} - {/* {validator.description.moniker} */} + {validator?.description?.moniker} @@ -645,13 +665,13 @@ const MyValidators = ({ overflowX="hidden" > {index + 1} - {/* {validator.name} */} + {validator.name} diff --git a/examples/stake-tokens/components/react/staking.tsx b/examples/stake-tokens/components/react/staking.tsx index b4ab65995..aaee4cbc1 100644 --- a/examples/stake-tokens/components/react/staking.tsx +++ b/examples/stake-tokens/components/react/staking.tsx @@ -29,6 +29,15 @@ export const getExponent = (chainName: string) => { )?.exponent as number; }; +const splitIntoChunks = (arr: any[], chunkSize: number) => { + const res = []; + for (let i = 0; i < arr.length; i += chunkSize) { + const chunk = arr.slice(i, i + chunkSize); + res.push(chunk); + } + return res; +}; + interface StakingTokens { balance: number; rewards: Reward[]; @@ -38,6 +47,9 @@ interface StakingTokens { myValidators: Validator[]; allValidators: Validator[]; unbondingDays: number; + thumbnails: { + [key: string]: string; + }; } export const StakingSection = ({ chainName }: { chainName: ChainName }) => { @@ -52,6 +64,7 @@ export const StakingSection = ({ chainName }: { chainName: ChainName }) => { myValidators: [], allValidators: [], unbondingDays: 0, + thumbnails: {}, }); const coin = getCoin(chainName); @@ -68,6 +81,7 @@ export const StakingSection = ({ chainName }: { chainName: ChainName }) => { myValidators: [], allValidators: [], unbondingDays: 0, + thumbnails: {}, }); return; } @@ -145,6 +159,49 @@ export const StakingSection = ({ chainName }: { chainName: ChainName }) => { ? Number((params?.unbondingTime?.seconds.low / 86400).toFixed(0)) : 0; + // THUMBNAILS + const validatorThumbnails = localStorage.getItem( + `${chainName}-validator-thumbnails` + ); + + let thumbnails = {}; + + if (validatorThumbnails) { + thumbnails = JSON.parse(validatorThumbnails); + } else { + const identities = allValidators.map( + (validator) => validator.description!.identity + ); + + const chunkedIdentities = splitIntoChunks(identities, 30); + + let responses: any[] = []; + + for (const chunk of chunkedIdentities) { + const thumbnailRequests = chunk.map((identity) => { + const url = `https://keybase.io/_/api/1.0/user/lookup.json?key_suffix=${identity}&fields=pictures`; + return fetch(url).then((response) => response.json()); + }); + responses = [...responses, await Promise.all(thumbnailRequests)]; + await new Promise((resolve) => setTimeout(resolve, 1000)); + } + + const thumbnailUrls = responses + .flat() + .map((value) => value.them?.[0]?.pictures?.primary.url); + + thumbnails = thumbnailUrls.reduce( + (prev, cur, idx) => + identities[idx] && cur ? { ...prev, [identities[idx]]: cur } : prev, + {} + ); + + localStorage.setItem( + `${chainName}-validator-thumbnails`, + JSON.stringify(thumbnails) + ); + } + setData({ rewards, totalReward, @@ -154,9 +211,10 @@ export const StakingSection = ({ chainName }: { chainName: ChainName }) => { myValidators, allValidators, unbondingDays, + thumbnails, }); setIsLoading(false); - }, [address, coin, exp, getRpcEndpoint]); + }, [address, chainName, coin.base, exp, getRpcEndpoint]); useEffect(() => { getData(); @@ -193,6 +251,7 @@ export const StakingSection = ({ chainName }: { chainName: ChainName }) => { updateData={getData} unbondingDays={data.unbondingDays} chainName={chainName} + thumbnails={data.thumbnails} /> )} @@ -205,6 +264,7 @@ export const StakingSection = ({ chainName }: { chainName: ChainName }) => { updateData={getData} unbondingDays={data.unbondingDays} chainName={chainName} + thumbnails={data.thumbnails} /> )} diff --git a/examples/stake-tokens/components/types.tsx b/examples/stake-tokens/components/types.tsx index f95b0397e..67d1ff050 100644 --- a/examples/stake-tokens/components/types.tsx +++ b/examples/stake-tokens/components/types.tsx @@ -96,4 +96,5 @@ export interface MyValidator { address: string; staked: number; reward: number; + identity: string | undefined; } From fe0b8cbefc6fc8fde05a456cc143862a044c4186 Mon Sep 17 00:00:00 2001 From: luca Date: Wed, 1 Feb 2023 19:16:32 +0800 Subject: [PATCH 2/2] add voting power and commission data --- .../components/react/all-validators.tsx | 23 ++++++++++--- .../components/react/delegate-modal.tsx | 2 +- .../components/react/my-validators.tsx | 33 +++++++++++++++---- .../stake-tokens/components/react/staking.tsx | 29 ++++++++-------- examples/stake-tokens/components/types.tsx | 1 + 5 files changed, 62 insertions(+), 26 deletions(-) diff --git a/examples/stake-tokens/components/react/all-validators.tsx b/examples/stake-tokens/components/react/all-validators.tsx index 325ca8cf4..0f0a7d2c1 100644 --- a/examples/stake-tokens/components/react/all-validators.tsx +++ b/examples/stake-tokens/components/react/all-validators.tsx @@ -200,7 +200,14 @@ const AllValidators = ({ : '' } name={currentValidator?.description?.moniker || ''} - commission={5} + commission={ + currentValidator?.commission?.commissionRates?.rate + ? exponentiate( + currentValidator.commission.commissionRates.rate, + -16 + ).toFixed(0) + : 0 + } apr={22.08} /> - {/* {validator.voting} */} - 10,000,000  + {Math.floor(exponentiate(validator.tokens, -exp))} +   - {/* {validator.commission} */} - 5% + + {validator.commission?.commissionRates?.rate && + exponentiate( + validator.commission.commissionRates.rate, + -16 + ).toFixed(0)} + % + {/* {validator.apr} */} diff --git a/examples/stake-tokens/components/react/delegate-modal.tsx b/examples/stake-tokens/components/react/delegate-modal.tsx index ebf7a2be8..ff5709bdc 100644 --- a/examples/stake-tokens/components/react/delegate-modal.tsx +++ b/examples/stake-tokens/components/react/delegate-modal.tsx @@ -34,7 +34,7 @@ export const ValidatorInfo = ({ }: { imgUrl: string; name: string; - commission: number; + commission: number | string; apr: number; }) => ( diff --git a/examples/stake-tokens/components/react/my-validators.tsx b/examples/stake-tokens/components/react/my-validators.tsx index 8d623fb1f..184f3e98c 100644 --- a/examples/stake-tokens/components/react/my-validators.tsx +++ b/examples/stake-tokens/components/react/my-validators.tsx @@ -161,6 +161,7 @@ const MyValidators = ({ address: validator.operatorAddress, staked: exponentiate(delegation.balance!.amount, -exp), reward: Number(exponentiate(rewardAmount, -exp).toFixed(6)), + commission: validator?.commission?.commissionRates?.rate, }; }); @@ -391,7 +392,11 @@ const MyValidators = ({ : '' } name={currentValidator?.name || ''} - commission={5} + commission={ + currentValidator?.commission + ? exponentiate(currentValidator.commission, -16).toFixed(0) + : 0 + } apr={22.08} /> @@ -443,7 +448,11 @@ const MyValidators = ({ : '' } name={currentValidator?.name || ''} - commission={5} + commission={ + currentValidator?.commission + ? exponentiate(currentValidator.commission, -16).toFixed(0) + : 0 + } apr={22.08} /> @@ -494,7 +503,11 @@ const MyValidators = ({ : '' } name={currentValidator?.name || ''} - commission={5} + commission={ + currentValidator?.commission + ? exponentiate(currentValidator.commission, -16).toFixed(0) + : 0 + } apr={22.08} /> @@ -579,12 +592,18 @@ const MyValidators = ({ - {/* {validator.voting} */} - 10,000,000  + {Math.floor(exponentiate(validator.tokens, -exp))} +   - {/* {validator.commission} */} - 5% + + {validator.commission?.commissionRates?.rate && + exponentiate( + validator.commission.commissionRates.rate, + -16 + ).toFixed(0)} + % + {/* {validator.apr} */} diff --git a/examples/stake-tokens/components/react/staking.tsx b/examples/stake-tokens/components/react/staking.tsx index aaee4cbc1..f17ca7fcf 100644 --- a/examples/stake-tokens/components/react/staking.tsx +++ b/examples/stake-tokens/components/react/staking.tsx @@ -129,19 +129,22 @@ export const StakingSection = ({ chainName }: { chainName: ChainName }) => { const totalReward = Number(exponentiate(reward, -exp).toFixed(6)); // ALL VALIDATORS - const { validators: allValidators } = - await client.cosmos.staking.v1beta1.validators({ - status: cosmos.staking.v1beta1.bondStatusToJSON( - cosmos.staking.v1beta1.BondStatus.BOND_STATUS_BONDED - ), - pagination: { - key: new Uint8Array(), - offset: Long.fromNumber(0), - limit: Long.fromNumber(200), - countTotal: false, - reverse: false, - }, - }); + const { validators } = await client.cosmos.staking.v1beta1.validators({ + status: cosmos.staking.v1beta1.bondStatusToJSON( + cosmos.staking.v1beta1.BondStatus.BOND_STATUS_BONDED + ), + pagination: { + key: new Uint8Array(), + offset: Long.fromNumber(0), + limit: Long.fromNumber(200), + countTotal: false, + reverse: false, + }, + }); + + const allValidators = validators.sort((a, b) => + new BigNumber(b.tokens).minus(new BigNumber(a.tokens)).toNumber() + ); // DELEGATIONS const { delegationResponses: delegations } = diff --git a/examples/stake-tokens/components/types.tsx b/examples/stake-tokens/components/types.tsx index 67d1ff050..913fd9910 100644 --- a/examples/stake-tokens/components/types.tsx +++ b/examples/stake-tokens/components/types.tsx @@ -97,4 +97,5 @@ export interface MyValidator { staked: number; reward: number; identity: string | undefined; + commission: string | undefined; }