Skip to content
This repository was archived by the owner on Mar 11, 2025. It is now read-only.

Commit a2bbabc

Browse files
gebob19obiwan
andauthored
add getMultipleAccounts for Token (#2990)
* add getMultipleAccounts for Token * update naming convention of fcn * rename & reorg code * lint fix Co-authored-by: obiwan <[email protected]>
1 parent 4ddaae5 commit a2bbabc

File tree

1 file changed

+54
-25
lines changed

1 file changed

+54
-25
lines changed

token/js/src/state/account.ts

Lines changed: 54 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { struct, u32, u8 } from '@solana/buffer-layout';
22
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';
44
import { TOKEN_PROGRAM_ID } from '../constants';
55
import {
66
TokenAccountNotFoundError,
@@ -99,32 +99,32 @@ export async function getAccount(
9999
programId = TOKEN_PROGRAM_ID
100100
): Promise<Account> {
101101
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+
}
105104

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);
112126
}
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;
128128
}
129129

130130
/** Get the minimum lamport balance for a base token account to be rent exempt
@@ -156,3 +156,32 @@ export async function getMinimumBalanceForRentExemptAccountWithExtensions(
156156
const accountLen = getAccountLen(extensions);
157157
return await connection.getMinimumBalanceForRentExemption(accountLen, commitment);
158158
}
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

Comments
 (0)