|
1 | 1 | import { struct, u32, u8 } from '@solana/buffer-layout';
|
2 | 2 | import { publicKey, u64 } from '@solana/buffer-layout-utils';
|
3 |
| -import { Commitment, Connection, PublicKey } from '@solana/web3.js'; |
| 3 | +import { Commitment, Connection, PublicKey, AccountInfo } from '@solana/web3.js'; |
4 | 4 | import { TOKEN_PROGRAM_ID } from '../constants';
|
5 | 5 | import {
|
6 | 6 | TokenAccountNotFoundError,
|
@@ -99,32 +99,32 @@ export async function getAccount(
|
99 | 99 | programId = TOKEN_PROGRAM_ID
|
100 | 100 | ): Promise<Account> {
|
101 | 101 | const info = await connection.getAccountInfo(address, commitment);
|
102 |
| - if (!info) throw new TokenAccountNotFoundError(); |
103 |
| - if (!info.owner.equals(programId)) throw new TokenInvalidAccountOwnerError(); |
104 |
| - if (info.data.length < ACCOUNT_SIZE) throw new TokenInvalidAccountSizeError(); |
| 102 | + return unpackAccount(info, address, programId); |
| 103 | +} |
105 | 104 |
|
106 |
| - const rawAccount = AccountLayout.decode(info.data.slice(0, ACCOUNT_SIZE)); |
107 |
| - let tlvData = Buffer.alloc(0); |
108 |
| - if (info.data.length > ACCOUNT_SIZE) { |
109 |
| - if (info.data.length === MULTISIG_SIZE) throw new TokenInvalidAccountSizeError(); |
110 |
| - if (info.data[ACCOUNT_SIZE] != AccountType.Account) throw new TokenInvalidAccountError(); |
111 |
| - tlvData = info.data.slice(ACCOUNT_SIZE + ACCOUNT_TYPE_SIZE); |
| 105 | +/** |
| 106 | + * Retrieve information about multiple token accounts in a single RPC call |
| 107 | + * |
| 108 | + * @param connection Connection to use |
| 109 | + * @param addresses Token accounts |
| 110 | + * @param commitment Desired level of commitment for querying the state |
| 111 | + * @param programId SPL Token program account |
| 112 | + * |
| 113 | + * @return Token account information |
| 114 | + */ |
| 115 | +export async function getMultipleAccounts( |
| 116 | + connection: Connection, |
| 117 | + addresses: PublicKey[], |
| 118 | + commitment?: Commitment, |
| 119 | + programId = TOKEN_PROGRAM_ID |
| 120 | +): Promise<Account[]> { |
| 121 | + const infos = await connection.getMultipleAccountsInfo(addresses, commitment); |
| 122 | + const accounts = []; |
| 123 | + for (let i = 0; i < infos.length; i++) { |
| 124 | + const account = unpackAccount(infos[i], addresses[i], programId); |
| 125 | + accounts.push(account); |
112 | 126 | }
|
113 |
| - |
114 |
| - return { |
115 |
| - address, |
116 |
| - mint: rawAccount.mint, |
117 |
| - owner: rawAccount.owner, |
118 |
| - amount: rawAccount.amount, |
119 |
| - delegate: rawAccount.delegateOption ? rawAccount.delegate : null, |
120 |
| - delegatedAmount: rawAccount.delegatedAmount, |
121 |
| - isInitialized: rawAccount.state !== AccountState.Uninitialized, |
122 |
| - isFrozen: rawAccount.state === AccountState.Frozen, |
123 |
| - isNative: !!rawAccount.isNativeOption, |
124 |
| - rentExemptReserve: rawAccount.isNativeOption ? rawAccount.isNative : null, |
125 |
| - closeAuthority: rawAccount.closeAuthorityOption ? rawAccount.closeAuthority : null, |
126 |
| - tlvData, |
127 |
| - }; |
| 127 | + return accounts; |
128 | 128 | }
|
129 | 129 |
|
130 | 130 | /** Get the minimum lamport balance for a base token account to be rent exempt
|
@@ -156,3 +156,32 @@ export async function getMinimumBalanceForRentExemptAccountWithExtensions(
|
156 | 156 | const accountLen = getAccountLen(extensions);
|
157 | 157 | return await connection.getMinimumBalanceForRentExemption(accountLen, commitment);
|
158 | 158 | }
|
| 159 | + |
| 160 | +function unpackAccount(info: AccountInfo<Buffer> | null, address: PublicKey, programId: PublicKey) { |
| 161 | + if (!info) throw new TokenAccountNotFoundError(); |
| 162 | + if (!info.owner.equals(programId)) throw new TokenInvalidAccountOwnerError(); |
| 163 | + if (info.data.length < ACCOUNT_SIZE) throw new TokenInvalidAccountSizeError(); |
| 164 | + |
| 165 | + const rawAccount = AccountLayout.decode(info.data.slice(0, ACCOUNT_SIZE)); |
| 166 | + let tlvData = Buffer.alloc(0); |
| 167 | + if (info.data.length > ACCOUNT_SIZE) { |
| 168 | + if (info.data.length === MULTISIG_SIZE) throw new TokenInvalidAccountSizeError(); |
| 169 | + if (info.data[ACCOUNT_SIZE] != AccountType.Account) throw new TokenInvalidAccountError(); |
| 170 | + tlvData = info.data.slice(ACCOUNT_SIZE + ACCOUNT_TYPE_SIZE); |
| 171 | + } |
| 172 | + |
| 173 | + return { |
| 174 | + address, |
| 175 | + mint: rawAccount.mint, |
| 176 | + owner: rawAccount.owner, |
| 177 | + amount: rawAccount.amount, |
| 178 | + delegate: rawAccount.delegateOption ? rawAccount.delegate : null, |
| 179 | + delegatedAmount: rawAccount.delegatedAmount, |
| 180 | + isInitialized: rawAccount.state !== AccountState.Uninitialized, |
| 181 | + isFrozen: rawAccount.state === AccountState.Frozen, |
| 182 | + isNative: !!rawAccount.isNativeOption, |
| 183 | + rentExemptReserve: rawAccount.isNativeOption ? rawAccount.isNative : null, |
| 184 | + closeAuthority: rawAccount.closeAuthorityOption ? rawAccount.closeAuthority : null, |
| 185 | + tlvData, |
| 186 | + }; |
| 187 | +} |
0 commit comments