Skip to content
This repository was archived by the owner on Feb 12, 2026. It is now read-only.

Commit a60bfb9

Browse files
committed
feat: add account types: wallet, new keypair and pda
1 parent 2cfe897 commit a60bfb9

16 files changed

Lines changed: 674 additions & 383 deletions

src/components/client/accounts/Account.tsx

Lines changed: 47 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,50 @@
11
import { CloseIcon, EditIcon } from "@chakra-ui/icons";
22
import {
33
Flex,
4+
Grid,
45
Icon,
56
IconButton,
6-
Input,
7-
InputGroup,
8-
InputLeftElement,
9-
InputRightElement,
107
Tooltip,
118
useBreakpointValue,
12-
useToast,
9+
Wrap,
1310
} from "@chakra-ui/react";
14-
import { useWallet } from "@solana/wallet-adapter-react";
15-
import { Keypair } from "@solana/web3.js";
1611
import { WritableDraft } from "immer/dist/internal";
1712
import React from "react";
18-
import { FaKey, FaPenNib, FaWallet } from "react-icons/fa";
13+
import { FaPenNib } from "react-icons/fa";
14+
import { useAccountType } from "../../../hooks/useAccountType";
1915
import { useInstruction } from "../../../hooks/useInstruction";
2016
import { useSessionStoreWithUndo } from "../../../hooks/useSessionStore";
21-
import { IAccount } from "../../../types/internal";
17+
import { IAccount, IAccountTypeConfigPda } from "../../../types/internal";
2218
import { removeFrom } from "../../../utils/sortable";
23-
import { isValidPublicKey } from "../../../utils/web3js";
2419
import { DragHandle } from "../../common/DragHandle";
2520
import { EditableName } from "../../common/EditableName";
26-
import { ExplorerButton } from "../../common/ExplorerButton";
2721
import { Numbering } from "../../common/Numbering";
2822
import { ToggleIconButton } from "../../common/ToggleIconButton";
29-
import { AirdropButton } from "./AirdropButton";
30-
import { PdaButton } from "./PdaButton";
23+
import { AccountInput } from "./AccountInput";
24+
import { PdaTypeConfig } from "./type-configs/PdaTypeConfig";
3125

3226
export const Account: React.FC<{
3327
account: IAccount;
3428
index: number;
3529
isAnchor?: boolean;
36-
}> = ({
37-
account: { id, name, pubkey, isWritable, isSigner },
38-
index,
39-
isAnchor = false,
40-
}) => {
30+
}> = ({ account, index, isAnchor = false }) => {
4131
const {
42-
instruction: { programId },
43-
update,
44-
} = useInstruction();
32+
id,
33+
type: { type, config },
34+
name,
35+
pubkey,
36+
isWritable,
37+
isSigner,
38+
} = account;
4539

46-
const { publicKey: walletPubkey } = useWallet();
47-
const [rpcEndpoint, keypairs, setSession] = useSessionStoreWithUndo(
48-
(state) => [state.rpcEndpoint, state.keypairs, state.set]
49-
);
50-
const toast = useToast();
40+
const { update } = useInstruction();
41+
const { isConfigurable: isTypeConfigurable } = useAccountType();
42+
43+
const [keypairs, setSession] = useSessionStoreWithUndo((state) => [
44+
state.keypairs,
45+
state.set,
46+
]);
5147

52-
const isValid = isValidPublicKey(pubkey);
53-
const isWallet = pubkey === walletPubkey?.toBase58();
5448
const hasPrivateKey = Object.keys(keypairs).includes(pubkey);
5549

5650
const updateAccount = (fn: (state: WritableDraft<IAccount>) => void) => {
@@ -71,44 +65,26 @@ export const Account: React.FC<{
7165
}
7266
};
7367

74-
const generateKeypair = () => {
75-
const keypair = Keypair.generate();
76-
const publicKey = keypair.publicKey.toBase58();
77-
setSession((state) => {
78-
state.keypairs[publicKey] = keypair.secretKey;
79-
});
80-
updateAccount((state) => {
81-
state.pubkey = publicKey;
82-
state.isSigner = true;
83-
});
84-
toast({
85-
title: "Keypair has been successfully created!",
86-
description:
87-
"Private keys are stored in memory only. They will not survive page reloads.",
88-
status: "info",
89-
duration: 8000,
90-
isClosable: true,
91-
});
92-
};
93-
9468
const nameWidth = useBreakpointValue({
9569
base: "100px",
9670
lg: "150px",
9771
xl: "200px",
9872
});
9973

10074
return (
101-
<Flex mb="2" alignItems="center">
75+
<Flex mb="2" alignItems="start">
10276
<DragHandle
103-
unlockedProps={{ h: "2.5", w: "2.5" }}
104-
lockedProps={{ h: "3" }}
77+
unlockedProps={{ mt: "2", h: "2.5", w: "2.5" }}
78+
lockedProps={{ mt: "2", h: "3" }}
10579
locked={isAnchor}
10680
/>
10781

10882
<Numbering index={index} ml="2" minW="30px" fontSize="sm" />
10983

11084
<EditableName
85+
mt="0.5"
11186
ml="2"
87+
mr="2"
11288
minW={nameWidth}
11389
maxW={nameWidth}
11490
textAlign="right"
@@ -124,90 +100,27 @@ export const Account: React.FC<{
124100
}}
125101
/>
126102

127-
<InputGroup size="sm">
128-
{isWallet && (
129-
<InputLeftElement
130-
pointerEvents="none"
131-
ml="2"
132-
children={<Icon as={FaWallet} color="gray.400" />}
133-
/>
134-
)}
135-
{hasPrivateKey && (
136-
<InputLeftElement pointerEvents="none" ml="2">
137-
<Icon as={FaKey} color="gray.400" />
138-
</InputLeftElement>
139-
)}
140-
<Input
141-
ml="2"
142-
fontFamily="mono"
143-
placeholder="Account Public Key"
144-
// TODO should be smarter based on the number of children in InputRightElement
145-
paddingEnd="14"
146-
value={pubkey}
147-
onChange={(e) => {
148-
updateAccount((state) => {
149-
state.pubkey = e.target.value.trim();
150-
});
151-
}}
152-
></Input>
153-
<InputRightElement
154-
// chakra hardcode the width so we can't have multiple buttons
155-
w=""
156-
mr="1"
103+
<Grid flex="1">
104+
<AccountInput account={account} />
105+
106+
<Wrap
107+
// TODO ignores other future tags
108+
pt={isTypeConfigurable ? "1" : undefined}
157109
>
158-
{isValid ? (
159-
<>
160-
{rpcEndpoint.network !== "mainnet-beta" && (
161-
<AirdropButton accountPubkey={pubkey} />
162-
)}
163-
<ExplorerButton
164-
size="xs"
165-
valueType="account"
166-
value={pubkey}
167-
rpcEndpoint={rpcEndpoint}
168-
/>
169-
</>
170-
) : (
171-
<>
172-
{programId && (
173-
<PdaButton
174-
programId={programId}
175-
setPubkey={(pubkey) => {
176-
updateAccount((state) => {
177-
state.pubkey = pubkey;
178-
});
179-
}}
180-
/>
181-
)}
182-
{walletPubkey && (
183-
<Tooltip label="Use Wallet">
184-
<IconButton
185-
ml="1"
186-
size="xs"
187-
variant="ghost"
188-
aria-label="Use Wallet"
189-
icon={<Icon as={FaWallet} />}
190-
onClick={() => {
191-
updateAccount((state) => {
192-
state.pubkey = walletPubkey?.toBase58();
193-
});
194-
}}
195-
/>
196-
</Tooltip>
197-
)}
198-
<Tooltip label="Generate Keypair">
199-
<IconButton
200-
size="xs"
201-
variant="ghost"
202-
aria-label="Generate Keypair"
203-
icon={<Icon as={FaKey} />}
204-
onClick={generateKeypair}
205-
/>
206-
</Tooltip>
207-
</>
110+
{/* TODO */}
111+
{/* {type === "ata" && (
112+
<AtaTypeConfig config={config as IAccountTypeConfigAta} />
113+
)} */}
114+
115+
{type === "pda" && (
116+
<PdaTypeConfig config={config as IAccountTypeConfigPda} />
208117
)}
209-
</InputRightElement>
210-
</InputGroup>
118+
119+
{/* TODO account balance */}
120+
</Wrap>
121+
122+
{/* TODO description when not minimised */}
123+
</Grid>
211124

212125
<ToggleIconButton
213126
ml="1"
@@ -240,6 +153,7 @@ export const Account: React.FC<{
240153
{!isAnchor && (
241154
<Tooltip label="Remove">
242155
<IconButton
156+
mt="1"
243157
ml="3"
244158
size="xs"
245159
aria-label="Remove"
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import {
2+
Icon,
3+
IconButton,
4+
Input,
5+
InputGroup,
6+
InputLeftElement,
7+
InputRightElement,
8+
Tooltip,
9+
} from "@chakra-ui/react";
10+
import React from "react";
11+
import { FaMagic } from "react-icons/fa";
12+
import { useAccount } from "../../../hooks/useAccount";
13+
import { useAccountType } from "../../../hooks/useAccountType";
14+
import { useSessionStoreWithUndo } from "../../../hooks/useSessionStore";
15+
import { IAccount } from "../../../types/internal";
16+
import { isValidPublicKey } from "../../../utils/web3js";
17+
import { ExplorerButton } from "../../common/ExplorerButton";
18+
import { AccountTypeButton } from "./AccountTypeButton";
19+
import { AirdropButton } from "./AirdropButton";
20+
21+
export const AccountInput: React.FC<{
22+
account: IAccount;
23+
}> = ({ account: { pubkey, type } }) => {
24+
const { update } = useAccount();
25+
const rpcEndpoint = useSessionStoreWithUndo((state) => state.rpcEndpoint);
26+
const { populate } = useAccountType();
27+
28+
const isValid = isValidPublicKey(pubkey);
29+
30+
return (
31+
<InputGroup size="sm">
32+
<InputLeftElement
33+
ml="1"
34+
// chakra hardcode the width so we can't have multiple buttons
35+
w=""
36+
>
37+
<AccountTypeButton type={type} />
38+
</InputLeftElement>
39+
40+
<Input
41+
fontFamily="mono"
42+
placeholder={
43+
type?.type === "wallet"
44+
? "Add connected wallet's public key"
45+
: type.type === "keypair"
46+
? "Generate new keypair"
47+
: type.type === "pda"
48+
? "Configure program ID and seeds to find program address"
49+
: type.type === "ata"
50+
? "Configure mint to get token account associated with your wallet"
51+
: type.type === "program"
52+
? "Start typing for well-known programs"
53+
: type.type === "sysvar"
54+
? "Start typing for sysvars"
55+
: "Account public key"
56+
}
57+
paddingStart="12"
58+
// TODO should be smarter based on the number of children in InputRightElement
59+
paddingEnd="14"
60+
value={pubkey}
61+
onChange={(e) => {
62+
update((state) => {
63+
state.pubkey = e.target.value.trim();
64+
});
65+
}}
66+
></Input>
67+
68+
<InputRightElement
69+
// chakra hardcode the width so we can't have multiple buttons
70+
w=""
71+
mr="1"
72+
>
73+
{isValid && (
74+
<>
75+
{rpcEndpoint.network !== "mainnet-beta" && (
76+
<AirdropButton accountPubkey={pubkey} />
77+
)}
78+
<ExplorerButton
79+
size="xs"
80+
valueType="account"
81+
value={pubkey}
82+
rpcEndpoint={rpcEndpoint}
83+
/>
84+
</>
85+
)}
86+
87+
<Tooltip label="Auto-fill">
88+
<IconButton
89+
size="xs"
90+
variant="ghost"
91+
colorScheme="teal"
92+
aria-label="Auto-fill"
93+
icon={<Icon as={FaMagic} />}
94+
isDisabled={type.type === "unspecified"}
95+
onClick={() => {
96+
populate();
97+
}}
98+
/>
99+
</Tooltip>
100+
</InputRightElement>
101+
</InputGroup>
102+
);
103+
};

0 commit comments

Comments
 (0)