Skip to content

Commit

Permalink
A useAccount hook that throws if a wallet does not have an account …
Browse files Browse the repository at this point in the history
…matching a given address
  • Loading branch information
steveluscher committed Feb 8, 2024
1 parent afb80a7 commit d40efc0
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 2 deletions.
4 changes: 3 additions & 1 deletion packages/errors/src/codes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export const SOLANA_ERROR__RPC_INTEGER_OVERFLOW = 3 as const;
export const SOLANA_ERROR__CHAIN_NOT_SUPPORTED = 4 as const;
export const SOLANA_ERROR__WALLET_DOES_NOT_SUPPORT_CHAIN = 5 as const;
export const SOLANA_ERROR__WALLET_HAS_NO_CONNECTED_ACCOUNTS_FOR_CHAIN = 6 as const;
export const SOLANA_ERROR__WALLET_ACCOUNT_NOT_FOUND_IN_WALLET = 9 as const;

/**
* A union of every Solana error code
Expand All @@ -34,4 +35,5 @@ export type SolanaErrorCode =
| typeof SOLANA_ERROR__RPC_INTEGER_OVERFLOW
| typeof SOLANA_ERROR__CHAIN_NOT_SUPPORTED
| typeof SOLANA_ERROR__WALLET_DOES_NOT_SUPPORT_CHAIN
| typeof SOLANA_ERROR__WALLET_HAS_NO_CONNECTED_ACCOUNTS_FOR_CHAIN;
| typeof SOLANA_ERROR__WALLET_HAS_NO_CONNECTED_ACCOUNTS_FOR_CHAIN
| typeof SOLANA_ERROR__WALLET_ACCOUNT_NOT_FOUND_IN_WALLET;
5 changes: 5 additions & 0 deletions packages/errors/src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
SOLANA_ERROR__CHAIN_NOT_SUPPORTED,
SOLANA_ERROR__RPC_INTEGER_OVERFLOW,
SOLANA_ERROR__TRANSACTION_MISSING_SIGNATURES,
SOLANA_ERROR__WALLET_ACCOUNT_NOT_FOUND_IN_WALLET,
SOLANA_ERROR__WALLET_DOES_NOT_SUPPORT_CHAIN,
SOLANA_ERROR__WALLET_HAS_NO_CONNECTED_ACCOUNTS_FOR_CHAIN,
SolanaErrorCode,
Expand Down Expand Up @@ -33,6 +34,10 @@ export type SolanaErrorContext = DefaultUnspecifiedErrorContextToUndefined<{
path?: string;
value: bigint;
};
[SOLANA_ERROR__WALLET_ACCOUNT_NOT_FOUND_IN_WALLET]: {
accountAddress: string;
walletName: string;
};
[SOLANA_ERROR__WALLET_DOES_NOT_SUPPORT_CHAIN]: {
chain: `${string}:${string}`;
walletName: string;
Expand Down
3 changes: 3 additions & 0 deletions packages/errors/src/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
SOLANA_ERROR__RPC_INTEGER_OVERFLOW,
SOLANA_ERROR__TRANSACTION_MISSING_SIGNATURES,
SOLANA_ERROR__TRANSACTION_SIGNATURE_NOT_COMPUTABLE,
SOLANA_ERROR__WALLET_ACCOUNT_NOT_FOUND_IN_WALLET,
SOLANA_ERROR__WALLET_DOES_NOT_SUPPORT_CHAIN,
SOLANA_ERROR__WALLET_HAS_NO_CONNECTED_ACCOUNTS_FOR_CHAIN,
SolanaErrorCode,
Expand All @@ -29,6 +30,8 @@ export const SolanaErrorMessages: Readonly<{
[SOLANA_ERROR__TRANSACTION_SIGNATURE_NOT_COMPUTABLE]:
"Could not determine this transaction's signature. Make sure that the transaction has " +
'been signed by its fee payer.',
[SOLANA_ERROR__WALLET_ACCOUNT_NOT_FOUND_IN_WALLET]:
'No account having address `$accountAddress` could be found in the wallet `$walletName}`',
[SOLANA_ERROR__WALLET_DOES_NOT_SUPPORT_CHAIN]:
"The wallet '$walletName' does not support connecting to the chain `$chain`",
[SOLANA_ERROR__WALLET_HAS_NO_CONNECTED_ACCOUNTS_FOR_CHAIN]:
Expand Down
62 changes: 61 additions & 1 deletion packages/react/src/__tests__/wallet-accounts-internal-test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { address } from '@solana/addresses';
import {
SOLANA_ERROR__CHAIN_NOT_SUPPORTED,
SOLANA_ERROR__WALLET_ACCOUNT_NOT_FOUND_IN_WALLET,
SOLANA_ERROR__WALLET_DOES_NOT_SUPPORT_CHAIN,
SOLANA_ERROR__WALLET_HAS_NO_CONNECTED_ACCOUNTS_FOR_CHAIN,
SolanaError,
Expand All @@ -11,7 +12,66 @@ import { StandardEvents, StandardEventsListeners } from '@wallet-standard/featur
import { act } from 'react-test-renderer';

import { renderHook } from '../test-renderer';
import { useWalletAccounts_INTERNAL_ONLY_DO_NOT_EXPORT } from '../wallet-accounts-internal';
import {
useWalletAccount_INTERNAL_ONLY_DO_NOT_EXPORT,
useWalletAccounts_INTERNAL_ONLY_DO_NOT_EXPORT,
} from '../wallet-accounts-internal';

describe('useWalletAccount', () => {
let mockWallet: Wallet;
beforeEach(() => {
mockWallet = {
accounts: [
{
address: address('Httx5rAMNW3zA6NtXbgpnq22RdS9qK6rRBiNi8Msoc8a'),
chains: ['solana:devnet'],
features: ['solana:signMessage', 'solana:signAndSendTransaction'],
icon: 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEAAAAALAAAAAABAAEAAAIBAAA=',
label: 'My Test Account',
publicKey: new Uint8Array([
251, 6, 90, 16, 167, 85, 10, 206, 169, 88, 60, 180, 238, 49, 109, 108, 152, 101, 243, 178, 93,
190, 195, 73, 206, 97, 76, 131, 200, 38, 175, 179,
]),
},
],
chains: SOLANA_CHAINS,
features: {},
icon: 'data:image/svg+xml;base64,ABC',
name: 'Mock Wallet',
version: '1.0.0',
};
// Suppresses console output when an `ErrorBoundary` is hit.
// See https://stackoverflow.com/a/72632884/802047
jest.spyOn(console, 'error').mockImplementation();
jest.spyOn(console, 'warn').mockImplementation();
});
it('returns the account matching the given address', () => {
const { result } = renderHook(() =>
useWalletAccount_INTERNAL_ONLY_DO_NOT_EXPORT(
mockWallet,
address('Httx5rAMNW3zA6NtXbgpnq22RdS9qK6rRBiNi8Msoc8a'),
'devnet',
),
);
expect(result.current).toBe(mockWallet.accounts[0]);
});
it('fatals when a wallet has no account matching the given address', () => {
const { result } = renderHook(() =>
useWalletAccount_INTERNAL_ONLY_DO_NOT_EXPORT(
mockWallet,
address('6bDQKLGyVpAUzhZa8jDvKbAPPs33ESMdTAjN4HX5PEVu'),
'devnet',
),
);
expect(result.__type).toBe('error');
expect(result.current).toEqual(
new SolanaError(SOLANA_ERROR__WALLET_ACCOUNT_NOT_FOUND_IN_WALLET, {
accountAddress: '6bDQKLGyVpAUzhZa8jDvKbAPPs33ESMdTAjN4HX5PEVu',
walletName: 'Mock Wallet',
}),
);
});
});

describe('useWalletAccounts', () => {
let emitWalletChangeEvent: StandardEventsListeners['change'];
Expand Down
18 changes: 18 additions & 0 deletions packages/react/src/wallet-accounts-internal.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Address } from '@solana/addresses';
import {
SOLANA_ERROR__CHAIN_NOT_SUPPORTED,
SOLANA_ERROR__WALLET_ACCOUNT_NOT_FOUND_IN_WALLET,
SOLANA_ERROR__WALLET_DOES_NOT_SUPPORT_CHAIN,
SOLANA_ERROR__WALLET_HAS_NO_CONNECTED_ACCOUNTS_FOR_CHAIN,
SolanaError,
Expand Down Expand Up @@ -27,6 +29,22 @@ function hasEventsFeature(wallet: Wallet): wallet is WalletWithFeatures<Standard
return StandardEvents in wallet.features;
}

export function useWalletAccount_INTERNAL_ONLY_DO_NOT_EXPORT<TWallet extends Wallet>(
wallet: TWallet,
address: Address,
cluster: ChainToCluster<TWallet['chains'][number] & SolanaChain>,
): WalletAccount {
const accounts = useWalletAccounts_INTERNAL_ONLY_DO_NOT_EXPORT(wallet, cluster);
const account = accounts.find(account => account.address === address);
if (!account) {
throw new SolanaError(SOLANA_ERROR__WALLET_ACCOUNT_NOT_FOUND_IN_WALLET, {
accountAddress: address,
walletName: wallet.name,
});
}
return account;
}

export function useWalletAccounts_INTERNAL_ONLY_DO_NOT_EXPORT<TWallet extends Wallet>(
wallet: TWallet,
cluster: ChainToCluster<TWallet['chains'][number] & SolanaChain>,
Expand Down

0 comments on commit d40efc0

Please sign in to comment.