Skip to content

fix staking-tokens example #72

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

Merged
merged 1 commit into from
Feb 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
18 changes: 8 additions & 10 deletions examples/stake-tokens/components/react/all-validators.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ export const Thumbnail = ({
mr={2}
/>
) : (
<Center boxSize="30px" bgColor="gray.400" borderRadius="full" mr={2}>
{name && name.slice(0, 1).toUpperCase()}
<Center boxSize="30px" bgColor="gray.200" borderRadius="full" mr={2}>
{name && name.trim().slice(0, 1).toUpperCase()}
</Center>
)}
</>
Expand Down Expand Up @@ -195,8 +195,8 @@ const AllValidators = ({
<ModalBody>
<ValidatorInfo
imgUrl={
currentValidator?.description?.identity
? thumbnails[currentValidator.description.identity]
currentValidator
? thumbnails[currentValidator?.operatorAddress]
: ''
}
name={currentValidator?.description?.moniker || ''}
Expand Down Expand Up @@ -270,17 +270,15 @@ const AllValidators = ({
<Thumbnail
identity={validator.description?.identity}
name={validator.description?.moniker}
thumbnailUrl={
validator.description?.identity
? thumbnails[validator.description.identity]
: ''
}
thumbnailUrl={thumbnails[validator.operatorAddress]}
/>
<Text>{validator?.description?.moniker}</Text>
</Box>
</Td>
<Td>
{Math.floor(exponentiate(validator.tokens, -exp))}
{Math.floor(
exponentiate(validator.tokens, -exp)
).toLocaleString()}
&nbsp;
<Token color="blackAlpha.800" token={coin.symbol} />
</Td>
Expand Down
26 changes: 8 additions & 18 deletions examples/stake-tokens/components/react/my-validators.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -387,9 +387,7 @@ const MyValidators = ({
<ModalBody>
<ValidatorInfo
imgUrl={
currentValidator?.identity
? thumbnails[currentValidator.identity]
: ''
currentValidator ? thumbnails[currentValidator.address] : ''
}
name={currentValidator?.name || ''}
commission={
Expand Down Expand Up @@ -443,9 +441,7 @@ const MyValidators = ({
<ModalBody>
<ValidatorInfo
imgUrl={
currentValidator?.identity
? thumbnails[currentValidator.identity]
: ''
currentValidator ? thumbnails[currentValidator.address] : ''
}
name={currentValidator?.name || ''}
commission={
Expand Down Expand Up @@ -498,9 +494,7 @@ const MyValidators = ({
<ModalBody>
<ValidatorInfo
imgUrl={
currentValidator?.identity
? thumbnails[currentValidator.identity]
: ''
currentValidator ? thumbnails[currentValidator.address] : ''
}
name={currentValidator?.name || ''}
commission={
Expand Down Expand Up @@ -582,17 +576,15 @@ const MyValidators = ({
<Thumbnail
identity={validator.description?.identity}
name={validator.description?.moniker}
thumbnailUrl={
validator.description?.identity
? thumbnails[validator.description.identity]
: ''
}
thumbnailUrl={thumbnails[validator.operatorAddress]}
/>
<Text>{validator?.description?.moniker}</Text>
</Box>
</Td>
<Td>
{Math.floor(exponentiate(validator.tokens, -exp))}
{Math.floor(
exponentiate(validator.tokens, -exp)
).toLocaleString()}
&nbsp;
<Token color="blackAlpha.800" token={coin.symbol} />
</Td>
Expand Down Expand Up @@ -687,9 +679,7 @@ const MyValidators = ({
<Thumbnail
identity={validator.identity}
name={validator.name}
thumbnailUrl={
validator.identity ? thumbnails[validator.identity] : ''
}
thumbnailUrl={thumbnails[validator.address]}
/>
<Text>{validator.name}</Text>
</Box>
Expand Down
162 changes: 121 additions & 41 deletions examples/stake-tokens/components/react/staking.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useCallback, useEffect, useState } from 'react';
import { useChain } from '@cosmos-kit/react';
import { Box, Skeleton } from '@chakra-ui/react';
import { Box, SkeletonText } from '@chakra-ui/react';
import { cosmos } from 'interchain';
import BigNumber from 'bignumber.js';
import { decodeCosmosSdkDecFromProto } from '@cosmjs/stargate';
Expand All @@ -16,6 +16,7 @@ import AllValidators from './all-validators';
import { getCoin } from '../../config';
import router from 'next/router';
import { ChainName } from '@cosmos-kit/core';
import { ImageSource } from '../types';

export const exponentiate = (num: number | string, exp: number) => {
return new BigNumber(num)
Expand All @@ -38,6 +39,109 @@ const splitIntoChunks = (arr: any[], chunkSize: number) => {
return res;
};

const convertChainName = (chainName: string) => {
if (chainName.endsWith('testnet')) {
return chainName.replace('testnet', '-testnet');
}

switch (chainName) {
case 'cosmoshub':
return 'cosmos';
case 'assetmantle':
return 'asset-mantle';
case 'cryptoorgchain':
return 'crypto-org';
case 'dig':
return 'dig-chain';
case 'gravitybridge':
return 'gravity-bridge';
case 'kichain':
return 'ki-chain';
case 'oraichain':
return 'orai-chain';
case 'terra':
return 'terra-classic';
default:
return chainName;
}
};

const isUrlValid = async (url: string) => {
const res = await fetch(url, { method: 'HEAD' });
const contentType = res?.headers?.get('Content-Type') || '';
return contentType.startsWith('image');
};

const getCosmostationUrl = (chainName: string, validatorAddr: string) => {
const cosmostationChainName = convertChainName(chainName);
return `https://raw.githubusercontent.com/cosmostation/chainlist/main/chain/${cosmostationChainName}/moniker/${validatorAddr}.png`;
};

const addImageSource = async (
validator: Validator,
chainName: string
): Promise<Validator & ImageSource> => {
const url = getCosmostationUrl(chainName, validator.operatorAddress);
const isValid = await isUrlValid(url);
return { ...validator, imageSource: isValid ? 'cosmostation' : 'keybase' };
};

const getKeybaseUrl = (identity: string) => {
return `https://keybase.io/_/api/1.0/user/lookup.json?key_suffix=${identity}&fields=pictures`;
};

const getImgUrls = async (validators: Validator[], chainName: string) => {
const validatorsWithImgSource = await Promise.all(
validators.map((validator) => addImageSource(validator, chainName))
);

// cosmostation urls
const cosmostationUrls = validatorsWithImgSource
.filter((validator) => validator.imageSource === 'cosmostation')
.map(({ operatorAddress }) => {
return {
address: operatorAddress,
url: getCosmostationUrl(chainName, operatorAddress),
};
});

// keybase urls
const keybaseIdentities = validatorsWithImgSource
.filter((validator) => validator.imageSource === 'keybase')
.map((validator) => ({
address: validator.operatorAddress,
identity: validator.description?.identity || '',
}));

const chunkedIdentities = splitIntoChunks(keybaseIdentities, 20);

let responses: any[] = [];

for (const chunk of chunkedIdentities) {
const thumbnailRequests = chunk.map(({ address, identity }) => {
if (!identity) return { address, url: '' };

return fetch(getKeybaseUrl(identity))
.then((response) => response.json())
.then((res) => ({
address,
url: res.them?.[0]?.pictures?.primary.url || '',
}));
});
responses = [...responses, await Promise.all(thumbnailRequests)];
await new Promise((resolve) => setTimeout(resolve, 500));
}

const keybaseUrls = responses.flat();

const allUrls = [...cosmostationUrls, ...keybaseUrls].reduce(
(prev, cur) => ({ ...prev, [cur.address]: cur.url }),
{}
);

return allUrls;
};

interface StakingTokens {
balance: number;
rewards: Reward[];
Expand Down Expand Up @@ -91,7 +195,7 @@ export const StakingSection = ({ chainName }: { chainName: ChainName }) => {
let rpcEndpoint = await getRpcEndpoint();

if (!rpcEndpoint) {
console.log('no rpc endpoint — using a fallback');
console.log('no rpc endpoint — using a fallback');
rpcEndpoint = `https://rpc.cosmos.directory/${chainName}`;
}

Expand Down Expand Up @@ -163,42 +267,16 @@ export const StakingSection = ({ chainName }: { chainName: ChainName }) => {
: 0;

// THUMBNAILS
let 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,
{}
);

thumbnails = await getImgUrls(validators, chainName);
localStorage.setItem(
`${chainName}-validator-thumbnails`,
JSON.stringify(thumbnails)
Expand Down Expand Up @@ -233,7 +311,13 @@ export const StakingSection = ({ chainName }: { chainName: ChainName }) => {

return (
<Box my={14}>
<Skeleton isLoaded={!isLoading}>
<SkeletonText
isLoaded={!isLoading}
mt="0"
noOfLines={4}
spacing="4"
skeletonHeight="4"
>
<Stats
balance={data.balance}
rewards={data.rewards}
Expand All @@ -242,9 +326,7 @@ export const StakingSection = ({ chainName }: { chainName: ChainName }) => {
updateData={getData}
chainName={chainName}
/>
</Skeleton>
{data.myValidators.length > 0 && (
<Skeleton isLoaded={!isLoading}>
{data.myValidators.length > 0 && (
<MyValidators
validators={data.myValidators}
allValidator={data.allValidators}
Expand All @@ -256,10 +338,8 @@ export const StakingSection = ({ chainName }: { chainName: ChainName }) => {
chainName={chainName}
thumbnails={data.thumbnails}
/>
</Skeleton>
)}
{data.allValidators.length > 0 && (
<Skeleton isLoaded={!isLoading}>
)}
{data.allValidators.length > 0 && (
<AllValidators
balance={data.balance}
validators={data.allValidators}
Expand All @@ -269,8 +349,8 @@ export const StakingSection = ({ chainName }: { chainName: ChainName }) => {
chainName={chainName}
thumbnails={data.thumbnails}
/>
</Skeleton>
)}
)}
</SkeletonText>
</Box>
);
};
4 changes: 4 additions & 0 deletions examples/stake-tokens/components/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,7 @@ export interface MyValidator {
identity: string | undefined;
commission: string | undefined;
}

export type ImageSource = {
imageSource: 'cosmostation' | 'keybase';
};