diff --git a/react-native-expo/components/PortfolioScreen/BalanceList/AssetActionSheet/index.tsx b/react-native-expo/components/PortfolioScreen/BalanceList/AssetActionSheet/index.tsx
index 5b84ba9..04b2bab 100644
--- a/react-native-expo/components/PortfolioScreen/BalanceList/AssetActionSheet/index.tsx
+++ b/react-native-expo/components/PortfolioScreen/BalanceList/AssetActionSheet/index.tsx
@@ -5,6 +5,23 @@ import { useAppDispatch, useAppSelector } from '@/store/hooks';
import { setSelectedAssetSymbol } from '@/store/portfolioScreen';
import { Sx, Text, View } from 'dripsy';
import useTransactionsForAsset from './useTransactionsForAsset';
+import { getBalanceView } from '@penumbra-zone/getters/balances-response';
+import { getSymbolFromValueView } from '@penumbra-zone/getters/value-view';
+import { BalancesResponse } from '@penumbra-zone/protobuf/penumbra/view/v1/view_pb';
+import { createSelector } from 'reselect';
+import { RootState } from '@/store';
+import { getFormattedAmtFromValueView } from '@penumbra-zone/types/value-view';
+
+const balanceSelector = (selectedAssetSymbol?: string) =>
+ createSelector(
+ [(state: RootState) => state.balances.balancesResponses, () => selectedAssetSymbol],
+ balancesResponses =>
+ balancesResponses.find(
+ balancesResponse =>
+ getBalanceView.pipe(getSymbolFromValueView)(new BalancesResponse(balancesResponse)) ===
+ selectedAssetSymbol,
+ ),
+ );
/**
* An action sheet for a given asset, with a list of relevant transactions and
@@ -12,8 +29,9 @@ import useTransactionsForAsset from './useTransactionsForAsset';
*/
export default function AssetActionSheet() {
const selectedAssetSymbol = useAppSelector(state => state.portfolioScreen.selectedAssetSymbol);
- const balance = useAppSelector(state =>
- state.balances.balances.find(balance => balance.assetSymbol === selectedAssetSymbol),
+ const balancesResponse = useAppSelector(balanceSelector(selectedAssetSymbol));
+ const balanceView = getBalanceView.optional(
+ balancesResponse ? new BalancesResponse(balancesResponse) : undefined,
);
const dispatch = useAppDispatch();
const transactions = useTransactionsForAsset(selectedAssetSymbol);
@@ -28,11 +46,12 @@ export default function AssetActionSheet() {
-
- {balance?.amount} {balance?.assetSymbol}
-
-
- {balance?.equivalentValue} USDC
+ {balanceView && (
+
+ {getFormattedAmtFromValueView(balanceView)}
+ {selectedAssetSymbol}
+
+ )}
diff --git a/react-native-expo/components/PortfolioScreen/BalanceList/Balance/index.stories.ts b/react-native-expo/components/PortfolioScreen/BalanceList/Balance/index.stories.ts
index 4fb39db..919077d 100644
--- a/react-native-expo/components/PortfolioScreen/BalanceList/Balance/index.stories.ts
+++ b/react-native-expo/components/PortfolioScreen/BalanceList/Balance/index.stories.ts
@@ -1,13 +1,13 @@
import { Meta, StoryObj } from '@storybook/react';
import Balance from '.';
-import balanceFactory from '@/factories/balance';
+import balancesResponseFactory from '@/factories/balancesResponse';
const meta: Meta = {
component: Balance,
tags: ['autodocs'],
argTypes: {
- balance: { control: false },
+ balancesResponse: { control: false },
},
};
@@ -15,6 +15,6 @@ export default meta;
export const Basic: StoryObj = {
args: {
- balance: balanceFactory.build(),
+ balancesResponse: balancesResponseFactory.build(),
},
};
diff --git a/react-native-expo/components/PortfolioScreen/BalanceList/Balance/index.tsx b/react-native-expo/components/PortfolioScreen/BalanceList/Balance/index.tsx
index 7739643..c8abeda 100644
--- a/react-native-expo/components/PortfolioScreen/BalanceList/Balance/index.tsx
+++ b/react-native-expo/components/PortfolioScreen/BalanceList/Balance/index.tsx
@@ -2,28 +2,35 @@ import AssetIcon from '@/components/AssetIcon';
import ListItem from '@/components/ListItem';
import { useAppDispatch } from '@/store/hooks';
import { setSelectedAssetSymbol } from '@/store/portfolioScreen';
-import IBalance from '@/types/Balance';
+import { getBalanceView } from '@penumbra-zone/getters/balances-response';
+import { getSymbolFromValueView, getDisplayDenomFromView } from '@penumbra-zone/getters/value-view';
+import { getFormattedAmtFromValueView } from '@penumbra-zone/types/value-view';
+import { BalancesResponse } from '@penumbra-zone/protobuf/penumbra/view/v1/view_pb';
import { Sx, Text, View } from 'dripsy';
export interface BalanceProps {
- balance: IBalance;
+ balancesResponse: BalancesResponse;
}
-export default function Balance({ balance }: BalanceProps) {
+export default function Balance({ balancesResponse }: BalanceProps) {
const dispatch = useAppDispatch();
+ const balanceView = getBalanceView(balancesResponse);
+ const symbol = getSymbolFromValueView(balanceView);
+ const displayDenom = getDisplayDenomFromView(balanceView);
+ const formattedAmount = getFormattedAmtFromValueView(balanceView);
return (
}
- primaryText={balance.assetSymbol}
- secondaryText={balance.assetName}
+ primaryText={symbol}
+ secondaryText={displayDenom}
suffix={
- {balance.amount}
- {balance.equivalentValue} USDC
+ {formattedAmount}
+ {/* {balancesResponse.equivalentValue} USDC */}
}
- onPress={() => dispatch(setSelectedAssetSymbol(balance.assetSymbol))}
+ onPress={() => dispatch(setSelectedAssetSymbol(symbol))}
/>
);
}
diff --git a/react-native-expo/components/PortfolioScreen/BalanceList/index.tsx b/react-native-expo/components/PortfolioScreen/BalanceList/index.tsx
index fc06416..28f305f 100644
--- a/react-native-expo/components/PortfolioScreen/BalanceList/index.tsx
+++ b/react-native-expo/components/PortfolioScreen/BalanceList/index.tsx
@@ -1,22 +1,26 @@
import List from '@/components/List';
-import IBalance from '@/types/Balance';
import { useLingui } from '@lingui/react/macro';
import Balance from './Balance';
import AssetActionSheet from './AssetActionSheet';
+import { BalancesResponse } from '@penumbra-zone/protobuf/penumbra/view/v1/view_pb';
+import { getSymbolFromValueView } from '@penumbra-zone/getters/value-view';
export interface BalanceListProps {
- balances: IBalance[];
+ balancesResponses: BalancesResponse[];
}
/** Shows a list of the user's balances in every asset they hold. */
-export default function BalanceList({ balances }: BalanceListProps) {
+export default function BalanceList({ balancesResponses }: BalanceListProps) {
const { t } = useLingui();
return (
<>
- {balances.map(balance => (
-
+ {balancesResponses.map(balancesResponse => (
+
))}
diff --git a/react-native-expo/components/PortfolioScreen/index.tsx b/react-native-expo/components/PortfolioScreen/index.tsx
index eedd2aa..1c96d8b 100644
--- a/react-native-expo/components/PortfolioScreen/index.tsx
+++ b/react-native-expo/components/PortfolioScreen/index.tsx
@@ -1,18 +1,26 @@
-import { Sx, View } from 'dripsy';
+import { ScrollView, Sx } from 'dripsy';
import BalanceAndActions from '../BalanceAndActions';
import BalanceList from './BalanceList';
import { useAppSelector } from '@/store/hooks';
+import { createSelector } from 'reselect';
+import { RootState } from '@/store';
+import { BalancesResponse } from '@penumbra-zone/protobuf/penumbra/view/v1/view_pb';
+
+const balancesResponsesSelector = createSelector(
+ [(state: RootState) => state.balances.balancesResponses],
+ balancesResponses =>
+ balancesResponses.map(balancesResponse => new BalancesResponse(balancesResponse)),
+);
export default function PortfolioScreen() {
- const balances = useAppSelector(state => state.balances.balances);
+ const balancesResponses = useAppSelector(balancesResponsesSelector);
return (
-
- {/** @todo: Make this a `ScrollView`. */}
+
-
-
+
+
);
}
diff --git a/react-native-expo/components/ProfileScreen/DefaultPaymentTokenScreen/useFilteredAssets.ts b/react-native-expo/components/ProfileScreen/DefaultPaymentTokenScreen/useFilteredAssets.ts
index 1ea08d6..00cdda5 100644
--- a/react-native-expo/components/ProfileScreen/DefaultPaymentTokenScreen/useFilteredAssets.ts
+++ b/react-native-expo/components/ProfileScreen/DefaultPaymentTokenScreen/useFilteredAssets.ts
@@ -19,18 +19,18 @@ const ALL_METADATAS = new ChainRegistryClient().bundled.get(PENUMBRA_CHAIN_ID).g
export default function useFilteredAssets() {
const searchText = useAppSelector(state => state.defaultPaymentTokenScreen.searchText);
- const filteredAssets = useMemo(
- () =>
- ALL_METADATAS.filter(metadata => {
- const searchTextLowerCase = searchText.toLocaleLowerCase();
+ const filteredAssets = useMemo(() => {
+ if (!searchText.trim().length) return ALL_METADATAS;
- if (metadata.name.toLocaleLowerCase().includes(searchTextLowerCase)) return true;
- if (metadata.symbol.toLocaleLowerCase().includes(searchTextLowerCase)) return true;
+ return ALL_METADATAS.filter(metadata => {
+ const searchTextLowerCase = searchText.toLocaleLowerCase();
- return false;
- }),
- [searchText],
- );
+ if (metadata.name.toLocaleLowerCase().includes(searchTextLowerCase)) return true;
+ if (metadata.symbol.toLocaleLowerCase().includes(searchTextLowerCase)) return true;
+
+ return false;
+ });
+ }, [searchText]);
return filteredAssets;
}
diff --git a/react-native-expo/factories/amount.ts b/react-native-expo/factories/amount.ts
new file mode 100644
index 0000000..310ed22
--- /dev/null
+++ b/react-native-expo/factories/amount.ts
@@ -0,0 +1,12 @@
+import * as Factory from 'factory.ts';
+import { PlainMessage } from '@bufbuild/protobuf';
+import { Amount } from '@penumbra-zone/protobuf/penumbra/core/num/v1/num_pb';
+
+const randomBigInt = () => BigInt(Math.round(Math.random() * 10 ** 7));
+
+const amountFactory = Factory.makeFactory>({
+ hi: 0n,
+ lo: randomBigInt(),
+});
+
+export default amountFactory;
diff --git a/react-native-expo/factories/balance.ts b/react-native-expo/factories/balance.ts
deleted file mode 100644
index 8f25b4d..0000000
--- a/react-native-expo/factories/balance.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import Balance from '@/types/Balance';
-import * as Factory from 'factory.ts';
-import ASSETS from './mockData/assets';
-
-const randomDecimalWith6DecimalPlaces = () => Math.round(Math.random() * 10 ** 9) / 10 ** 3;
-
-const balanceFactory = Factory.makeFactory({
- amount: Factory.each(randomDecimalWith6DecimalPlaces),
- equivalentValue: Factory.each(randomDecimalWith6DecimalPlaces),
- assetSymbol: Factory.each(i => ASSETS[i % ASSETS.length].symbol),
- assetName: Factory.each(i => ASSETS[i % ASSETS.length].name),
-});
-
-export default balanceFactory;
diff --git a/react-native-expo/factories/balancesResponse.ts b/react-native-expo/factories/balancesResponse.ts
new file mode 100644
index 0000000..672e12c
--- /dev/null
+++ b/react-native-expo/factories/balancesResponse.ts
@@ -0,0 +1,23 @@
+import * as Factory from 'factory.ts';
+import { BalancesResponse } from '@penumbra-zone/protobuf/penumbra/view/v1/view_pb';
+import { ChainRegistryClient } from '@penumbra-labs/registry';
+import { PENUMBRA_CHAIN_ID } from '@/utils/constants';
+import { PlainMessage } from '@bufbuild/protobuf';
+import amountFactory from './amount';
+
+const METADATAS = new ChainRegistryClient().bundled.get(PENUMBRA_CHAIN_ID).getAllAssets();
+
+const balancesResponseFactory = Factory.makeFactory>({
+ balanceView: Factory.each(i => ({
+ valueView: {
+ case: 'knownAssetId',
+ value: {
+ amount: amountFactory.build(),
+ equivalentValues: [],
+ metadata: METADATAS[i % METADATAS.length],
+ },
+ },
+ })),
+});
+
+export default balancesResponseFactory;
diff --git a/react-native-expo/metro.config.cjs b/react-native-expo/metro.config.cjs
index 52860ac..fa7e6dd 100644
--- a/react-native-expo/metro.config.cjs
+++ b/react-native-expo/metro.config.cjs
@@ -17,6 +17,20 @@ const ALIASES = {
* @todo: Fix this!
*/
'@penumbra-zone/bech32m/penumbra': '@penumbra-zone/bech32m/dist/penumbra',
+ /**
+ * Same issue here
+ */
+ '@penumbra-zone/bech32m/passet': '@penumbra-zone/bech32m/dist/passet',
+ '@penumbra-zone/protobuf/penumbra/view/v1/view_pb':
+ '@penumbra-zone/protobuf/dist/gen/penumbra/view/v1/view_pb',
+ '@penumbra-zone/protobuf/penumbra/core/component/stake/v1/stake_pb':
+ '@penumbra-zone/protobuf/dist/gen/penumbra/core/component/stake/v1/stake_pb',
+ '@penumbra-zone/protobuf/penumbra/core/num/v1/num_pb':
+ '@penumbra-zone/protobuf/dist/gen/penumbra/core/num/v1/num_pb',
+ '@penumbra-zone/getters/metadata': '@penumbra-zone/getters/dist/metadata.js',
+ '@penumbra-zone/getters/value-view': '@penumbra-zone/getters/dist/value-view.js',
+ '@penumbra-zone/types/value-view': '@penumbra-zone/types/dist/value-view.js',
+ '@penumbra-zone/getters/balances-response': '@penumbra-zone/getters/dist/balances-response.js',
};
config.resolver.resolveRequest = (context, moduleName, platform) => {
diff --git a/react-native-expo/package.json b/react-native-expo/package.json
index 16a07bf..b74981f 100644
--- a/react-native-expo/package.json
+++ b/react-native-expo/package.json
@@ -67,7 +67,8 @@
"react-native-webview": "13.12.2",
"react-redux": "^9.2.0",
"redux-persist": "^6.0.0",
- "redux-persist-expo-securestore": "^2.0.0"
+ "redux-persist-expo-securestore": "^2.0.0",
+ "reselect": "^5.1.1"
},
"devDependencies": {
"@babel/core": "^7.25.2",
diff --git a/react-native-expo/store/balances.ts b/react-native-expo/store/balances.ts
index 81e2358..7941694 100644
--- a/react-native-expo/store/balances.ts
+++ b/react-native-expo/store/balances.ts
@@ -1,14 +1,19 @@
-import balanceFactory from '@/factories/balance';
-import Balance from '@/types/Balance';
+import { BalancesResponse } from '@penumbra-zone/protobuf/penumbra/view/v1/view_pb';
+import balancesResponseFactory from '@/factories/balancesResponse';
import { createSlice } from '@reduxjs/toolkit';
+import { PlainMessage } from '@bufbuild/protobuf';
+import { ChainRegistryClient } from '@penumbra-labs/registry';
+import { PENUMBRA_CHAIN_ID } from '@/utils/constants';
+
+const METADATAS = new ChainRegistryClient().bundled.get(PENUMBRA_CHAIN_ID).getAllAssets();
export interface BalancesState {
- balances: Balance[];
+ balancesResponses: PlainMessage[];
}
const initialState: BalancesState = {
/** @todo: Populate with real data */
- balances: balanceFactory.buildList(5),
+ balancesResponses: balancesResponseFactory.buildList(METADATAS.length),
};
export const balancesSlice = createSlice({
@@ -17,6 +22,4 @@ export const balancesSlice = createSlice({
reducers: {},
});
-export const {} = balancesSlice.actions;
-
export default balancesSlice.reducer;
diff --git a/react-native-expo/yarn.lock b/react-native-expo/yarn.lock
index 13a8597..c3380af 100644
--- a/react-native-expo/yarn.lock
+++ b/react-native-expo/yarn.lock
@@ -9441,7 +9441,7 @@ requires-port@^1.0.0:
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==
-reselect@^5.1.0:
+reselect@^5.1.0, reselect@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/reselect/-/reselect-5.1.1.tgz#c766b1eb5d558291e5e550298adb0becc24bb72e"
integrity sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==