-
Notifications
You must be signed in to change notification settings - Fork 2
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
Dynamic client side envs 2 #80
Changes from 11 commits
03179ea
fb644e1
6e8bdf6
5c197bb
a13e384
4a5cd3c
99101c3
6948ae3
11428fa
a3eb862
726c0d0
95871aa
68a0a8e
c9de74a
b014301
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
PENUMBRA_GRPC_ENDPOINT= | ||
PENUMBRA_INDEXER_ENDPOINT= | ||
PENUMBRA_INDEXER_CA_CERT= | ||
PENUMBRA_CHAIN_ID= | ||
PENUMBRA_CUILOA_URL= |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { useQuery } from '@tanstack/react-query'; | ||
import { ClientEnv } from '@/utils/env/types'; | ||
|
||
export const useEnv = () => { | ||
return useQuery({ | ||
queryKey: ['clientEnv'], | ||
queryFn: async (): Promise<ClientEnv> => { | ||
return fetch('/api/env').then(resp => resp.json() as unknown as ClientEnv); | ||
}, | ||
staleTime: Infinity, | ||
}); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { ChainRegistryClient, Registry } from '@penumbra-labs/registry'; | ||
import { useQuery } from '@tanstack/react-query'; | ||
import { useEnv } from './env'; | ||
|
||
export const chainRegistryClient = new ChainRegistryClient(); | ||
|
||
export const useRegistry = () => { | ||
const { data: env } = useEnv(); | ||
const chainId = env?.PENUMBRA_CHAIN_ID; | ||
|
||
return useQuery({ | ||
queryKey: ['penumbraRegistry', chainId], | ||
queryFn: async (): Promise<Registry | null> => { | ||
if (!chainId) { | ||
return null; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue: in general, let's try to avoid null when we can. This can go into bike shedding territory, but I'm in the camp that it is a mistake in the language. There is another issue though, the returned loading and error states from export const useRegistry = () => {
const { data: env, isLoading: isEnvLoading, error: envError } = useEnv();
const {
data: registry,
isLoading: isRegistryLoading,
error: registryError,
} = useQuery({
queryKey: ['penumbraRegistry', env],
queryFn: async () => {
const chainId = env?.PENUMBRA_CHAIN_ID;
if (!chainId) {
throw new Error('chain id not available to query registry');
}
return chainRegistryClient.remote.get(chainId);
},
staleTime: Infinity,
enabled: Boolean(env),
});
return {
registry,
isLoading: isEnvLoading || isRegistryLoading,
error: envError || registryError,
};
}; |
||
} | ||
return chainRegistryClient.remote.get(chainId); | ||
}, | ||
staleTime: Infinity, | ||
}); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import { useRegistry } from "./registry"; | ||
import { AssetId } from '@penumbra-zone/protobuf/penumbra/core/asset/v1/asset_pb'; | ||
import { decimalsFromDenomUnits, imagePathFromAssetImages } from '@/old/utils/token/tokenFetch' | ||
import { uint8ArrayToBase64, base64ToUint8Array } from '@/old/utils/math/base64'; | ||
import { Token } from '@/old/utils/types/token'; | ||
|
||
export const useTokenAssets = (): Token[] => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue: with |
||
const { data: registry } = useRegistry(); | ||
if (!registry) { | ||
return []; | ||
} | ||
|
||
const assets = registry.getAllAssets(); | ||
|
||
return assets | ||
.filter(asset => asset.penumbraAssetId && !asset.display.startsWith('delegation_')) | ||
.map(asset => { | ||
const displayParts = asset.display.split('/'); | ||
return { | ||
decimals: decimalsFromDenomUnits(asset.denomUnits), | ||
display: displayParts[displayParts.length - 1] ?? '', | ||
symbol: asset.symbol, | ||
inner: asset.penumbraAssetId?.inner && uint8ArrayToBase64(asset.penumbraAssetId.inner), | ||
imagePath: imagePathFromAssetImages(asset.images), | ||
}; | ||
}) as Token[]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue: it is tempting to use our own types, but we should do our best to use types from BufBuild that are more universally relied upon in the penumbra ecosystem. For instance, we shouldn't have components that accept a Token, but probably ones that accept a |
||
}; | ||
|
||
export const useTokenAsset = (tokenId: Uint8Array | string): null | Token => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue: we should return some data field + loading + error states |
||
const { data: registry } = useRegistry(); | ||
|
||
if (!registry) { | ||
return null; | ||
} | ||
|
||
const assetId: AssetId = new AssetId(); | ||
assetId.inner = typeof tokenId !== 'string' ? tokenId : base64ToUint8Array(tokenId); | ||
|
||
const tokenMetadata = registry.getMetadata(assetId); | ||
const displayParts = tokenMetadata.display.split('/'); | ||
return { | ||
decimals: decimalsFromDenomUnits(tokenMetadata.denomUnits), | ||
display: displayParts[displayParts.length - 1] ?? '', | ||
symbol: tokenMetadata.symbol, | ||
inner: typeof tokenId !== 'string' ? uint8ArrayToBase64(tokenId) : tokenId, | ||
imagePath: imagePathFromAssetImages(tokenMetadata.images), | ||
}; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: In async functions, I find sticking to async await to be more readable than promise chaining: