-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
See interchain balances [part 3] (#1021)
* Balance hooks * Working balances
- Loading branch information
Showing
13 changed files
with
268 additions
and
32 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { assets as cosmosAssetList } from 'chain-registry'; | ||
import { Coin } from 'osmo-query'; | ||
import { Asset } from '@chain-registry/types'; | ||
import { BigNumber } from 'bignumber.js'; | ||
|
||
// Searches for corresponding denom in asset registry and returns the metadata | ||
export const augmentToAsset = (coin: Coin, chainName: string): Asset => { | ||
const match = cosmosAssetList | ||
.find(({ chain_name }) => chain_name === chainName) | ||
?.assets.find(asset => asset.base === coin.denom); | ||
|
||
return match ? match : fallbackAsset(coin); | ||
}; | ||
|
||
const fallbackAsset = (coin: Coin): Asset => { | ||
return { | ||
base: coin.denom, | ||
denom_units: [{ denom: coin.denom, exponent: 0 }], | ||
display: coin.denom, | ||
name: coin.denom, | ||
symbol: coin.denom, | ||
}; | ||
}; | ||
|
||
// Helps us convert from say 41000000uosmo to the more readable 41osmo | ||
export const rawToDisplayAmount = (asset: Asset, amount: string) => { | ||
const displayUnit = asset.denom_units.find(({ denom }) => denom === asset.display)?.exponent ?? 0; | ||
return new BigNumber(amount).shiftedBy(-displayUnit).toString(); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import { useChainConnector, useCosmosChainBalances } from './hooks'; | ||
import { useStore } from '../../../state'; | ||
import { ibcInSelector } from '../../../state/ibc-in'; | ||
import { | ||
Table, | ||
TableBody, | ||
TableCell, | ||
TableHead, | ||
TableHeader, | ||
TableRow, | ||
} from '@penumbra-zone/ui/components/ui/table'; | ||
import { Avatar, AvatarImage } from '@penumbra-zone/ui/components/ui/avatar'; | ||
import { Identicon } from '@penumbra-zone/ui/components/ui/identicon'; | ||
import { LineWave } from 'react-loader-spinner'; | ||
|
||
export const AssetsTable = () => { | ||
const { address } = useChainConnector(); | ||
const { selectedChain } = useStore(ibcInSelector); | ||
const { data, isLoading, error } = useCosmosChainBalances(); | ||
|
||
// User has not connected their wallet yet | ||
if (!address || !selectedChain) return <></>; | ||
|
||
if (isLoading) { | ||
return ( | ||
<div className='flex justify-center text-stone-700'> | ||
<span className='text-purple-700'>Loading balances...</span> | ||
<LineWave visible={true} height='70' width='70' color='#7e22ce' wrapperClass='-mt-9' /> | ||
</div> | ||
); | ||
} | ||
|
||
if (error) { | ||
return <div className='flex justify-center italic text-red-700'>{String(error)}</div>; | ||
} | ||
|
||
return ( | ||
<div className='text-stone-700'> | ||
<div className='flex justify-center italic text-stone-400'> | ||
Balances on {selectedChain.label} | ||
</div> | ||
<Table> | ||
<TableHeader> | ||
<TableRow> | ||
<TableHead className='w-[100px]'>Denom</TableHead> | ||
<TableHead className='text-right'>Amount</TableHead> | ||
</TableRow> | ||
</TableHeader> | ||
<TableBody> | ||
{data?.length === 0 && noBalancesRow()} | ||
{data?.map(b => { | ||
return ( | ||
<TableRow key={b.displayDenom}> | ||
<TableCell className='flex gap-2'> | ||
<Avatar className='size-6'> | ||
<AvatarImage src={b.icon} /> | ||
<Identicon uniqueIdentifier={b.displayDenom} type='gradient' size={22} /> | ||
</Avatar> | ||
<span className='max-w-[200px] truncate'>{b.displayDenom}</span> | ||
</TableCell> | ||
<TableCell className='text-right'>{b.displayAmount}</TableCell> | ||
</TableRow> | ||
); | ||
})} | ||
</TableBody> | ||
</Table> | ||
</div> | ||
); | ||
}; | ||
|
||
const noBalancesRow = () => { | ||
return ( | ||
<TableRow> | ||
<TableCell className='italic'>No balances</TableCell> | ||
</TableRow> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
11 changes: 2 additions & 9 deletions
11
apps/minifront/src/components/ibc/ibc-in/cosmos-wallet-connector.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
import { useStore } from '../../../state'; | ||
import { ibcInSelector } from '../../../state/ibc-in'; | ||
import { useChain, useManager } from '@cosmos-kit/react'; | ||
import { UseQueryResult } from '@tanstack/react-query'; | ||
import { ProtobufRpcClient } from '@cosmjs/stargate'; | ||
import { Coin, createRpcQueryHooks, useRpcClient, useRpcEndpoint } from 'osmo-query'; | ||
import { augmentToAsset, rawToDisplayAmount } from './asset-utils'; | ||
|
||
// This is sad, but osmo-query's custom hooks require calling .toJSON() on all fields. | ||
// This will throw an error for bigint, so needs to be added to the prototype. | ||
declare global { | ||
interface BigInt { | ||
toJSON(): string; | ||
} | ||
} | ||
|
||
BigInt.prototype.toJSON = function () { | ||
return this.toString(); | ||
}; | ||
|
||
export const useChainConnector = () => { | ||
const { selectedChain } = useStore(ibcInSelector); | ||
const { chainRecords } = useManager(); | ||
const defaultChain = chainRecords[0]!.name; | ||
return useChain(selectedChain?.chainName ?? defaultChain); | ||
}; | ||
|
||
const useCosmosQueryHooks = () => { | ||
const { address, getRpcEndpoint, chain } = useChainConnector(); | ||
|
||
const rpcEndpointQuery = useRpcEndpoint({ | ||
getter: getRpcEndpoint, | ||
options: { | ||
enabled: !!address, | ||
staleTime: Infinity, | ||
queryKey: ['rpc endpoint', address, chain.chain_name], | ||
// Needed for osmo-query's internal caching | ||
queryKeyHashFn: queryKey => { | ||
return JSON.stringify([...queryKey, chain.chain_name]); | ||
}, | ||
}, | ||
}) as UseQueryResult<string>; | ||
|
||
const rpcClientQuery = useRpcClient({ | ||
rpcEndpoint: rpcEndpointQuery.data ?? '', | ||
options: { | ||
enabled: !!address && !!rpcEndpointQuery.data, | ||
staleTime: Infinity, | ||
queryKey: ['rpc client', address, rpcEndpointQuery.data, chain.chain_name], | ||
// Needed for osmo-query's internal caching | ||
queryKeyHashFn: queryKey => { | ||
return JSON.stringify([...queryKey, chain.chain_name]); | ||
}, | ||
}, | ||
}) as UseQueryResult<ProtobufRpcClient>; | ||
|
||
const { cosmos: cosmosQuery, osmosis: osmosisQuery } = createRpcQueryHooks({ | ||
rpc: rpcClientQuery.data, | ||
}); | ||
|
||
const isReady = !!address && !!rpcClientQuery.data; | ||
const isFetching = rpcEndpointQuery.isFetching || rpcClientQuery.isFetching; | ||
|
||
return { cosmosQuery, osmosisQuery, isReady, isFetching, address, chain }; | ||
}; | ||
|
||
interface BalancesResponse { | ||
balances: Coin[]; | ||
pagination: { nexKey: Uint8Array; total: bigint }; | ||
} | ||
|
||
interface CosmosAssetBalance { | ||
raw: Coin; | ||
displayDenom: string; | ||
displayAmount: string; | ||
icon?: string; | ||
} | ||
|
||
interface UseCosmosChainBalancesRes { | ||
data?: CosmosAssetBalance[]; | ||
isLoading: boolean; | ||
error: unknown; | ||
} | ||
|
||
export const useCosmosChainBalances = (): UseCosmosChainBalancesRes => { | ||
const { address, cosmosQuery, isReady, chain } = useCosmosQueryHooks(); | ||
|
||
const { data, isLoading, error } = cosmosQuery.bank.v1beta1.useAllBalances({ | ||
request: { | ||
address: address ?? '', | ||
pagination: { | ||
offset: 0n, | ||
limit: 100n, | ||
key: new Uint8Array(), | ||
countTotal: true, | ||
reverse: false, | ||
}, | ||
}, | ||
options: { | ||
enabled: isReady, | ||
}, | ||
}) as UseQueryResult<BalancesResponse>; | ||
|
||
const augmentedAssets = data?.balances.map(coin => { | ||
const asset = augmentToAsset(coin, chain.chain_name); | ||
return { | ||
raw: coin, | ||
displayDenom: asset.display, | ||
displayAmount: rawToDisplayAmount(asset, coin.amount), | ||
icon: asset.logo_URIs?.svg ?? asset.logo_URIs?.png, | ||
}; | ||
}); | ||
return { data: augmentedAssets, isLoading, error }; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.