Skip to content

Commit

Permalink
Merge branch 'main' into @mago/enhanced-provider
Browse files Browse the repository at this point in the history
  • Loading branch information
magiziz committed May 11, 2024
2 parents 1410c93 + 8841891 commit 3eb40ff
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 30 deletions.
26 changes: 26 additions & 0 deletions .changeset/nasty-months-provide.md
@@ -0,0 +1,26 @@
---
"@rainbow-me/rainbowkit": patch
---

Added real-time balance fetching based on the [Recent Transaction](https://www.rainbowkit.com/docs/recent-transactions) API. As a transaction is confirmed on-chain, the user's gas balance will be updated to reflect the transaction.

```tsx
import { useAddRecentTransaction } from '@rainbow-me/rainbowkit';

export default () => {
const addRecentTransaction = useAddRecentTransaction();

return (
<button
onClick={() => {
addRecentTransaction({
hash: '0x...',
description: '...',
});
}}
>
Add recent transaction
</button>
);
};
```
10 changes: 6 additions & 4 deletions packages/rainbowkit/src/components/AccountModal/AccountModal.tsx
@@ -1,7 +1,6 @@
import React from 'react';
import { useAccount, useDisconnect } from 'wagmi';
import { useMainnetEnsAvatar } from '../../hooks/useMainnetEnsAvatar';
import { useMainnetEnsName } from '../../hooks/useMainnetEnsName';
import { useProfile } from '../../hooks/useProfile';
import { Dialog } from '../Dialog/Dialog';
import { DialogContent } from '../Dialog/DialogContent';
import { ProfileDetails } from '../ProfileDetails/ProfileDetails';
Expand All @@ -13,8 +12,10 @@ export interface AccountModalProps {

export function AccountModal({ onClose, open }: AccountModalProps) {
const { address } = useAccount();
const ensName = useMainnetEnsName(address);
const ensAvatar = useMainnetEnsAvatar(ensName);
const { balance, ensAvatar, ensName } = useProfile({
address,
includeBalance: open,
});
const { disconnect } = useDisconnect();

if (!address) {
Expand All @@ -32,6 +33,7 @@ export function AccountModal({ onClose, open }: AccountModalProps) {
address={address}
ensAvatar={ensAvatar}
ensName={ensName}
balance={balance}
onClose={onClose}
onDisconnect={disconnect}
/>
Expand Down
@@ -1,9 +1,8 @@
import React, { ReactNode, useContext } from 'react';
import { useAccount, useBalance, useConfig } from 'wagmi';
import { useAccount, useConfig } from 'wagmi';
import { normalizeResponsiveValue } from '../../css/sprinkles.css';
import { useIsMounted } from '../../hooks/useIsMounted';
import { useMainnetEnsAvatar } from '../../hooks/useMainnetEnsAvatar';
import { useMainnetEnsName } from '../../hooks/useMainnetEnsName';
import { useProfile } from '../../hooks/useProfile';
import { useRecentTransactions } from '../../transactions/useRecentTransactions';
import { isMobile } from '../../utils/isMobile';
import { useAsyncImage } from '../AsyncImage/useAsyncImage';
Expand Down Expand Up @@ -63,8 +62,6 @@ export function ConnectButtonRenderer({
}: ConnectButtonRendererProps) {
const isMounted = useIsMounted();
const { address } = useAccount();
const ensName = useMainnetEnsName(address);
const ensAvatar = useMainnetEnsAvatar(ensName);

const { chainId } = useAccount();
const { chains: wagmiChains } = useConfig();
Expand Down Expand Up @@ -103,13 +100,13 @@ export function ConnectButtonRenderer({

const shouldShowBalance = computeShouldShowBalance();

const { data: balanceData } = useBalance({
address: shouldShowBalance ? address : undefined,
const { balance, ensAvatar, ensName } = useProfile({
address,
includeBalance: shouldShowBalance,
});
const displayBalance = balanceData
? `${abbreviateETHBalance(parseFloat(balanceData.formatted))} ${
balanceData.symbol
}`

const displayBalance = balance
? `${abbreviateETHBalance(parseFloat(balance.formatted))} ${balance.symbol}`
: undefined;

const { openConnectModal } = useConnectModal();
Expand All @@ -124,9 +121,9 @@ export function ConnectButtonRenderer({
account: address
? {
address,
balanceDecimals: balanceData?.decimals,
balanceFormatted: balanceData?.formatted,
balanceSymbol: balanceData?.symbol,
balanceDecimals: balance?.decimals,
balanceFormatted: balance?.formatted,
balanceSymbol: balance?.symbol,
displayBalance,
displayName: ensName
? formatENS(ensName)
Expand Down
@@ -1,7 +1,8 @@
import React, { useCallback, useContext, useEffect, useState } from 'react';
import { GetEnsNameReturnType } from 'viem';
import { GetEnsAvatarReturnType } from 'viem/actions';
import { useAccount, useBalance } from 'wagmi';
import { useAccount } from 'wagmi';
import { useProfile } from '../../hooks/useProfile';
import { isMobile } from '../../utils/isMobile';
import { Avatar } from '../Avatar/Avatar';
import { Box } from '../Box/Box';
Expand All @@ -22,6 +23,7 @@ interface ProfileDetailsProps {
address: ReturnType<typeof useAccount>['address'];
ensAvatar: GetEnsAvatarReturnType | undefined;
ensName: GetEnsNameReturnType | undefined;
balance: ReturnType<typeof useProfile>['balance'];
onClose: () => void;
onDisconnect: () => void;
}
Expand All @@ -30,15 +32,12 @@ export function ProfileDetails({
address,
ensAvatar,
ensName,
balance,
onClose,
onDisconnect,
}: ProfileDetailsProps) {
const showRecentTransactions = useContext(ShowRecentTransactionsContext);

const { data: balanceData } = useBalance({
address,
});

const [copiedAddress, setCopiedAddress] = useState(false);
const copyAddressAction = useCallback(() => {
if (address) {
Expand All @@ -61,7 +60,7 @@ export function ProfileDetails({
}

const accountName = ensName ? formatENS(ensName) : formatAddress(address);
const ethBalance = balanceData?.formatted;
const ethBalance = balance?.formatted;
const displayBalance = ethBalance
? abbreviateETHBalance(parseFloat(ethBalance))
: undefined;
Expand Down Expand Up @@ -117,7 +116,7 @@ export function ProfileDetails({
{accountName}
</Text>
</Box>
{!!balanceData && (
{!!balance && (
<Box textAlign="center">
<Text
as="h1"
Expand All @@ -126,7 +125,7 @@ export function ProfileDetails({
size={mobile ? '16' : '14'}
weight="semibold"
>
{displayBalance} {balanceData.symbol}
{displayBalance} {balance.symbol}
</Text>
</Box>
)}
Expand Down
19 changes: 19 additions & 0 deletions packages/rainbowkit/src/hooks/useProfile.ts
@@ -0,0 +1,19 @@
import { Address } from 'viem';
import { useBalance } from 'wagmi';
import { useMainnetEnsAvatar } from './useMainnetEnsAvatar';
import { useMainnetEnsName } from './useMainnetEnsName';

interface UseProfileParameters {
address?: Address;
includeBalance?: boolean;
}

export function useProfile({ address, includeBalance }: UseProfileParameters) {
const ensName = useMainnetEnsName(address);
const ensAvatar = useMainnetEnsAvatar(ensName);
const { data: balance } = useBalance({
address: includeBalance ? address : undefined,
});

return { ensName, ensAvatar, balance };
}
31 changes: 28 additions & 3 deletions packages/rainbowkit/src/transactions/TransactionStoreContext.tsx
@@ -1,6 +1,12 @@
import React, { createContext, useContext, useEffect, useState } from 'react';
import { PublicClient } from 'viem';
import { useAccount, usePublicClient } from 'wagmi';
import React, {
createContext,
useCallback,
useContext,
useEffect,
useState,
} from 'react';
import { PublicClient, TransactionReceipt } from 'viem';
import { useAccount, useBalance, usePublicClient } from 'wagmi';
import { useChainId } from '../hooks/useChainId';
import { TransactionStore, createTransactionStore } from './transactionStore';

Expand All @@ -20,13 +26,26 @@ export function TransactionStoreProvider({
const provider = usePublicClient() as PublicClient;
const { address } = useAccount();
const chainId = useChainId();
const { refetch } = useBalance({
address,
query: {
enabled: false,
},
});

// Use existing store if it exists, or lazily create one
const [store] = useState(
() =>
storeSingleton ?? (storeSingleton = createTransactionStore({ provider })),
);

const onTransactionStatus = useCallback(
(txStatus: TransactionReceipt['status']) => {
if (txStatus === 'success') refetch();
},
[refetch],
);

// Keep store provider up to date with any wagmi changes
useEffect(() => {
store.setProvider(provider);
Expand All @@ -39,6 +58,12 @@ export function TransactionStoreProvider({
}
}, [store, address, chainId]);

useEffect(() => {
if (store && address && chainId) {
return store.onTransactionStatus(onTransactionStatus);
}
}, [store, address, chainId, onTransactionStatus]);

return (
<TransactionStoreContext.Provider value={store}>
{children}
Expand Down
26 changes: 25 additions & 1 deletion packages/rainbowkit/src/transactions/transactionStore.ts
@@ -1,4 +1,4 @@
import type { Address, PublicClient } from 'viem';
import type { Address, PublicClient, TransactionReceipt } from 'viem';

const storageKey = 'rk-transactions';

Expand Down Expand Up @@ -67,6 +67,9 @@ export function createTransactionStore({

let provider = initialProvider;
const listeners: Set<() => void> = new Set();
const transactionListeners: Set<
(txStatus: TransactionReceipt['status']) => void
> = new Set();
const transactionRequestCache: Map<string, Promise<void>> = new Map();

function setProvider(newProvider: PublicClient): void {
Expand Down Expand Up @@ -153,6 +156,8 @@ export function createTransactionStore({
// @ts-ignore - types changed with viem@1.1.0
status === 0 || status === 'reverted' ? 'failed' : 'confirmed',
);

notifyTransactionListeners(status);
})
.catch(() => {
// If a transaction is not found or cancelled
Expand Down Expand Up @@ -207,6 +212,14 @@ export function createTransactionStore({
}
}

function notifyTransactionListeners(
txStatus: TransactionReceipt['status'],
): void {
for (const transactionListener of transactionListeners) {
transactionListener(txStatus);
}
}

function onChange(fn: () => void): () => void {
listeners.add(fn);

Expand All @@ -215,10 +228,21 @@ export function createTransactionStore({
};
}

function onTransactionStatus(
fn: (txStatus: TransactionReceipt['status']) => void,
): () => void {
transactionListeners.add(fn);

return () => {
transactionListeners.delete(fn);
};
}

return {
addTransaction,
clearTransactions,
getTransactions,
onTransactionStatus,
onChange,
setProvider,
waitForPendingTransactions,
Expand Down

0 comments on commit 3eb40ff

Please sign in to comment.