Skip to content

Commit f630c5e

Browse files
committed
feat: better balances fetching
1 parent 09b7198 commit f630c5e

12 files changed

+6564
-1180
lines changed

package.json

+25-13
Original file line numberDiff line numberDiff line change
@@ -23,32 +23,42 @@
2323
"test": "vitest run",
2424
"generate-pindexer-schema": "tsx ./scripts/generate-pindexer-schema.ts"
2525
},
26+
"pnpm": {
27+
"overrides": {
28+
"cosmos-kit": "2.23.9"
29+
}
30+
},
2631
"dependencies": {
2732
"@bufbuild/protobuf": "^1.10.0",
28-
"@chain-registry/types": "^0.50.80",
33+
"@chain-registry/types": "^0.50.82",
2934
"@chakra-ui/icons": "^2.2.4",
3035
"@chakra-ui/react": "^2.10.4",
3136
"@chakra-ui/styled-system": "^2.12.0",
3237
"@connectrpc/connect": "^1.6.1",
3338
"@connectrpc/connect-web": "^1.6.1",
3439
"@cosmjs/proto-signing": "^0.33.0",
35-
"@cosmjs/stargate": "^0.33.0",
40+
"@cosmjs/stargate": "0.33.0",
41+
"@cosmos-kit/core": "^2.14.0",
42+
"@cosmos-kit/keplr": "^2.14.7",
43+
"@cosmos-kit/leap": "^2.14.7",
44+
"@cosmos-kit/react": "^2.21.7",
3645
"@emotion/react": "^11.13.5",
3746
"@emotion/styled": "^11.13.5",
3847
"@formkit/auto-animate": "^0.8.2",
3948
"@grpc/proto-loader": "^0.7.13",
49+
"@interchain-ui/react": "^1.26.2",
4050
"@keplr-wallet/types": "^0.12.190",
41-
"@penumbra-labs/registry": "^12.1.1",
42-
"@penumbra-zone/bech32m": "^13.0.0",
43-
"@penumbra-zone/client": "^24.0.0",
44-
"@penumbra-zone/crypto-web": "^36.0.0",
45-
"@penumbra-zone/getters": "^23.0.0",
46-
"@penumbra-zone/perspective": "^47.0.0",
47-
"@penumbra-zone/protobuf": "^7.2.0",
51+
"@penumbra-labs/registry": "^12.4.0",
52+
"@penumbra-zone/bech32m": "^14.0.0",
53+
"@penumbra-zone/client": "^25.0.0",
54+
"@penumbra-zone/crypto-web": "^38.0.0",
55+
"@penumbra-zone/getters": "^24.1.0",
56+
"@penumbra-zone/perspective": "^49.0.0",
57+
"@penumbra-zone/protobuf": "^8.0.0",
4858
"@penumbra-zone/transport-dom": "^7.5.0",
49-
"@penumbra-zone/types": "29.1.0",
50-
"@penumbra-zone/ui": "^13.11.0",
51-
"@penumbra-zone/wasm": "40.0.0",
59+
"@penumbra-zone/types": "31.0.0",
60+
"@penumbra-zone/ui": "^13.13.0",
61+
"@penumbra-zone/wasm": "42.0.0",
5262
"@radix-ui/react-icons": "^1.3.2",
5363
"@rehooks/component-size": "^1.0.3",
5464
"@tanstack/react-query": "^5.62.3",
@@ -57,14 +67,15 @@
5767
"@tsconfig/vite-react": "^3.4.0",
5868
"bech32": "^2.0.0",
5969
"bignumber.js": "^9.1.2",
60-
"chain-registry": "^1.69.134",
70+
"chain-registry": "^1.69.136",
6171
"chart.js": "^4.4.7",
6272
"chartjs-adapter-date-fns": "^3.0.0",
6373
"chartjs-chart-financial": "^0.2.1",
6474
"chartjs-plugin-annotation": "^3.1.0",
6575
"chartjs-plugin-zoom": "^2.2.0",
6676
"clsx": "^2.1.1",
6777
"configs": "github:prax-wallet/configs#main",
78+
"cosmos-kit": "^2.23.9",
6879
"date-fns": "^4.1.0",
6980
"echarts": "^5.5.1",
7081
"echarts-for-react": "^3.0.2",
@@ -78,6 +89,7 @@
7889
"mobx": "^6.13.5",
7990
"mobx-react-lite": "^4.0.7",
8091
"next": "^14.2.20",
92+
"osmo-query": "^16.14.0",
8193
"pg": "^8.13.1",
8294
"prettier": "^3.4.2",
8395
"react": "^18.3.1",

pnpm-lock.yaml

+6,222-1,047
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { ChainProvider } from '@cosmos-kit/react';
2+
import { assets, chains } from 'chain-registry';
3+
import { SignerOptions, wallets } from 'cosmos-kit';
4+
import { ReactNode, useMemo } from 'react';
5+
import { Chain, Registry as PenumbraRegistry } from '@penumbra-labs/registry';
6+
7+
import { GeneratedType, Registry } from '@cosmjs/proto-signing';
8+
import { AminoTypes } from '@cosmjs/stargate';
9+
import {
10+
cosmosAminoConverters,
11+
cosmosProtoRegistry,
12+
cosmwasmAminoConverters,
13+
cosmwasmProtoRegistry,
14+
ibcAminoConverters,
15+
ibcProtoRegistry,
16+
osmosisAminoConverters,
17+
osmosisProtoRegistry,
18+
} from 'osmo-query';
19+
20+
// @ts-expect-error type error, but it works.
21+
import '@interchain-ui/react/styles';
22+
23+
const protoRegistry: readonly [string, GeneratedType][] = [
24+
...cosmosProtoRegistry,
25+
...cosmwasmProtoRegistry,
26+
...ibcProtoRegistry,
27+
...osmosisProtoRegistry,
28+
];
29+
30+
const aminoConverters = {
31+
...cosmosAminoConverters,
32+
...cosmwasmAminoConverters,
33+
...ibcAminoConverters,
34+
...osmosisAminoConverters,
35+
};
36+
37+
const registry = new Registry(protoRegistry);
38+
const aminoTypes = new AminoTypes(aminoConverters);
39+
40+
const signerOptions: SignerOptions = {
41+
// @ts-expect-error type error, but it works.
42+
signingStargate: () => {
43+
return {
44+
aminoTypes,
45+
registry,
46+
};
47+
},
48+
};
49+
50+
interface IbcProviderProps {
51+
registry: PenumbraRegistry;
52+
children: ReactNode;
53+
}
54+
55+
export const IbcChainProvider = ({ registry, children }: IbcProviderProps) => {
56+
const chainsToDisplay = useMemo(
57+
() => chainsInPenumbraRegistry(registry.ibcConnections),
58+
[registry],
59+
);
60+
61+
return (
62+
<ChainProvider
63+
chains={chainsToDisplay}
64+
assetLists={assets}
65+
// Not using mobile wallets as WalletConnect is a centralized service that requires an account
66+
wallets={wallets.extension}
67+
signerOptions={signerOptions}
68+
modalTheme={{ defaultTheme: 'light' }}
69+
logLevel={'NONE'}
70+
>
71+
{children}
72+
</ChainProvider>
73+
);
74+
};
75+
76+
// Searches cosmos registry for chains that have ibc connections to Penumbra
77+
export const chainsInPenumbraRegistry = (ibcConnections: Chain[]) => {
78+
return chains.filter(c => ibcConnections.some(i => c.chain_id === i.chainId));
79+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { observer } from 'mobx-react-lite';
2+
import { Button, ButtonProps } from '@penumbra-zone/ui/Button';
3+
import dynamic from 'next/dynamic';
4+
import { useChains } from '@cosmos-kit/react';
5+
import { Wallet2 } from 'lucide-react';
6+
import { Density } from '@penumbra-zone/ui/Density';
7+
import { chainsInPenumbraRegistry } from '@/features/cosmos/chain-provider.tsx';
8+
import { useRegistry } from '@/shared/api/registry.ts';
9+
import { useBalances } from '@/features/cosmos/use-augmented-balances.ts';
10+
11+
interface CosmosConnectButtonProps {
12+
actionType?: ButtonProps['actionType'];
13+
variant?: 'default' | 'minimal';
14+
}
15+
16+
const CosmosConnectButtonInner = observer(
17+
({ actionType = 'accent', variant = 'default' }: CosmosConnectButtonProps) => {
18+
const { data: registry } = useRegistry();
19+
const penumbraIbcChains = chainsInPenumbraRegistry(registry?.ibcConnections ?? []).map(
20+
c => c.chain_name,
21+
);
22+
const chains = useChains(penumbraIbcChains);
23+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- Osmosis is always available
24+
const { address, disconnect, openView, isWalletConnected } = chains['osmosis']!;
25+
26+
const handleConnect = () => {
27+
openView();
28+
};
29+
30+
const { balances } = useBalances();
31+
32+
return (
33+
<Density variant={variant === 'default' ? 'sparse' : 'compact'}>
34+
{isWalletConnected && address ? (
35+
<>
36+
<Button actionType={actionType} onClick={() => void disconnect()}>
37+
{`${address.slice(0, 8)}...${address.slice(-4)}`}
38+
</Button>
39+
<pre className={'text-white overflow-scroll'}>
40+
{balances.map(b => JSON.stringify(b, undefined, 4))}
41+
</pre>
42+
</>
43+
) : (
44+
<Button icon={Wallet2} actionType={actionType} onClick={handleConnect}>
45+
Connect Cosmos Wallet
46+
</Button>
47+
)}
48+
</Density>
49+
);
50+
},
51+
);
52+
53+
// Export a dynamic component to prevent SSR issues with window object
54+
export const CosmosConnectButton = dynamic(() => Promise.resolve(CosmosConnectButtonInner), {
55+
ssr: false,
56+
});

src/features/cosmos/get-cosmos-balances.tsx

-35
This file was deleted.

src/features/cosmos/types.ts

-9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import type { Asset, Chain } from '@chain-registry/types';
2-
31
export interface Balance {
42
denom: string;
53
amount: string;
@@ -8,13 +6,6 @@ export interface Balance {
86
displayAmount?: string;
97
}
108

11-
export interface ChainConfig {
12-
chainId: string;
13-
restEndpoint: string;
14-
chain: Chain & { bech32_prefix: string };
15-
assets: Asset[];
16-
}
17-
189
export interface BankBalancesResponse {
1910
balances: {
2011
denom: string;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { useState, useEffect } from 'react';
2+
import { fetchChainBalances } from './utils/fetch-balances';
3+
import { useWallet } from '@cosmos-kit/react';
4+
import { Balance } from '@/features/cosmos/types.ts';
5+
6+
/**
7+
* Hook that augments Cosmos balances with Penumbra-specific data
8+
*
9+
* This scans balances across all supported chains and adds:
10+
* - Display information (symbol, denom, amount)
11+
* - Asset icons
12+
* - Asset type information
13+
* - A flag indicating if an asset is a Penumbra asset (can be transferred via IBC)
14+
*/
15+
export const useBalances = () => {
16+
const [balances, setBalances] = useState<Balance[]>([]);
17+
const [isLoading, setIsLoading] = useState(false);
18+
const [error, setError] = useState<string | null>(null);
19+
const { chainWallets, status } = useWallet();
20+
21+
useEffect(() => {
22+
const scanAllChains = async () => {
23+
try {
24+
setIsLoading(true);
25+
setError(null);
26+
27+
const allBalances: Balance[] = [];
28+
29+
// For each chain, try to fetch balances
30+
await Promise.all(
31+
chainWallets.map(async chain => {
32+
try {
33+
// Get the address for this chain
34+
const address = chain.address;
35+
36+
if (!address) {
37+
return;
38+
}
39+
40+
// Fetch balances for this chain
41+
const chainBalances = await fetchChainBalances(address, chain);
42+
allBalances.push(...chainBalances);
43+
} catch (err) {
44+
console.warn(`Error fetching balances for chain ${chain.chainId}:`, err);
45+
}
46+
}),
47+
);
48+
49+
setBalances(allBalances);
50+
} catch (err) {
51+
console.error('Error scanning balances:', err);
52+
setError(err instanceof Error ? err.message : 'Unknown error');
53+
} finally {
54+
setIsLoading(false);
55+
}
56+
};
57+
58+
void scanAllChains();
59+
}, [status]);
60+
return {
61+
balances,
62+
isLoading: isLoading,
63+
error: error,
64+
};
65+
};

src/features/cosmos/utils/chain-configs.ts

-25
This file was deleted.

0 commit comments

Comments
 (0)