Skip to content

Commit a9659a2

Browse files
authored
fix(staking): use anchor wallet for API interfaces (#1869)
1 parent abfd713 commit a9659a2

File tree

4 files changed

+46
-16
lines changed

4 files changed

+46
-16
lines changed

apps/staking/src/api.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// TODO remove these disables when moving off the mock APIs
22
/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-non-null-assertion */
33

4-
import type { WalletContextState } from "@solana/wallet-adapter-react";
4+
import type { AnchorWallet } from "@solana/wallet-adapter-react";
55
import type { Connection } from "@solana/web3.js";
66

77
export type StakeAccount = {
@@ -10,7 +10,7 @@ export type StakeAccount = {
1010

1111
export type Context = {
1212
connection: Connection;
13-
wallet: WalletContextState;
13+
wallet: AnchorWallet;
1414
stakeAccount: StakeAccount;
1515
};
1616

@@ -141,7 +141,7 @@ type AccountHistory = {
141141

142142
export const getStakeAccounts = async (
143143
_connection: Connection,
144-
_wallet: WalletContextState,
144+
_wallet: AnchorWallet,
145145
): Promise<StakeAccount[]> => {
146146
await new Promise((resolve) => setTimeout(resolve, MOCK_DELAY));
147147
return MOCK_STAKE_ACCOUNTS;

apps/staking/src/components/WalletButton/index.tsx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,11 @@ import {
3131
type SVGAttributes,
3232
type ReactNode,
3333
type ElementType,
34+
type Ref,
3435
useCallback,
3536
useMemo,
3637
useState,
38+
forwardRef,
3739
} from "react";
3840

3941
import { usePrimaryDomain } from "../../hooks/use-primary-domain";
@@ -191,27 +193,26 @@ type WalletMenuItemProps<T extends ElementType> = Omit<
191193
icon?: ComponentType<SVGAttributes<SVGSVGElement>>;
192194
};
193195

194-
const WalletMenuItem = <T extends ElementType>({
195-
as,
196-
children,
197-
icon: Icon,
198-
className,
199-
...props
200-
}: WalletMenuItemProps<T>) => {
196+
const WalletMenuItemImpl = <T extends ElementType>(
197+
{ as, children, icon: Icon, className, ...props }: WalletMenuItemProps<T>,
198+
ref: Ref<HTMLButtonElement>,
199+
) => {
201200
const Component = as ?? "button";
202201
return (
203202
<Component
204203
className={clsx(
205204
"flex items-center gap-2 whitespace-nowrap px-4 py-2 text-left hover:bg-pythpurple-800/20 data-[focus]:bg-pythpurple-800/20",
206205
className,
207206
)}
207+
ref={ref}
208208
{...props}
209209
>
210210
{Icon && <Icon className="size-4 text-pythpurple-600" />}
211211
{children}
212212
</Component>
213213
);
214214
};
215+
const WalletMenuItem = forwardRef(WalletMenuItemImpl);
215216

216217
const DisconnectedButton = (props: Props) => {
217218
const modal = useWalletModal();

apps/staking/src/hooks/use-api-context.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
1-
import { useWallet, useConnection } from "@solana/wallet-adapter-react";
1+
import { useAnchorWallet, useConnection } from "@solana/wallet-adapter-react";
22
import { useMemo } from "react";
33

44
import { StateType, useStakeAccount } from "./use-stake-account";
55
import type { Context } from "../api";
66

77
export const useApiContext = (): Context => {
8-
const wallet = useWallet();
8+
const wallet = useAnchorWallet();
99
const { connection } = useConnection();
1010
const stakeAccount = useStakeAccount();
1111

12+
if (wallet === undefined) {
13+
throw new NoWalletConnectedError();
14+
}
15+
1216
if (stakeAccount.type !== StateType.Loaded) {
1317
throw new NoStakeAccountSelectedError();
1418
}
@@ -19,6 +23,14 @@ export const useApiContext = (): Context => {
1923
);
2024
};
2125

26+
class NoWalletConnectedError extends Error {
27+
constructor() {
28+
super(
29+
"The `useApiContext` hook cannot be called if a wallet isn't connected! Ensure all components that use this hook are only rendered if a wallet is connected!",
30+
);
31+
}
32+
}
33+
2234
class NoStakeAccountSelectedError extends Error {
2335
constructor() {
2436
super(

apps/staking/src/hooks/use-stake-account.tsx

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,18 @@ const useStakeAccountState = () => {
7575
if (wallet.connected && !wallet.disconnecting && !loading.current) {
7676
loading.current = true;
7777
setState(State.Loading());
78-
getStakeAccounts(connection, wallet)
78+
if (
79+
!wallet.publicKey ||
80+
!wallet.signAllTransactions ||
81+
!wallet.signTransaction
82+
) {
83+
throw new WalletConnectedButInvalidError();
84+
}
85+
getStakeAccounts(connection, {
86+
publicKey: wallet.publicKey,
87+
signAllTransactions: wallet.signAllTransactions,
88+
signTransaction: wallet.signTransaction,
89+
})
7990
.then((accounts) => {
8091
const [firstAccount, ...otherAccounts] = accounts;
8192
if (firstAccount) {
@@ -96,12 +107,10 @@ const useStakeAccountState = () => {
96107
.finally(() => {
97108
loading.current = false;
98109
});
99-
} else if (!wallet.connected) {
100-
setState(State.NoWallet());
101110
}
102111
}, [connection, setAccount, wallet]);
103112

104-
return state;
113+
return wallet.connected && !wallet.disconnecting ? state : State.NoWallet();
105114
};
106115

107116
export const useStakeAccount = () => {
@@ -120,3 +129,11 @@ class NotInitializedError extends Error {
120129
);
121130
}
122131
}
132+
133+
class WalletConnectedButInvalidError extends Error {
134+
constructor() {
135+
super(
136+
"The wallet is connected but is missing a public key or methods to sign transactions!",
137+
);
138+
}
139+
}

0 commit comments

Comments
 (0)