Created
February 4, 2022 20:10
-
-
Save fersilva16/02dccfd9497ab128fb72e89c13de5ec2 to your computer and use it in GitHub Desktop.
Get tokens of the owner from an given wallet
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import web3 from '@solana/web3.js'; | |
import base58 from 'bs58'; | |
import { asyncFilter } from './asyncFilter'; // From https://gist.github.com/fersilva16/dea9bb85ca69de435d01b8793d44948a | |
const TOKEN_PROGRAM_ID = new web3.PublicKey( | |
'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA' | |
); | |
const TOKEN_METADATA_PROGRAM_ID = new web3.PublicKey( | |
'metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s' | |
); | |
/** | |
* Creator Struct Offsets | |
* See https://github.com/metaplex-foundation/metaplex-program-library/blob/master/token-metadata/program/src/state.rs#L400-L405 | |
* See https://github.com/metaplex-foundation/metaplex-program-library/blob/master/token-metadata/program/src/state.rs#L58-L60 | |
* | |
* 0 - Public key | |
* 32 - Verified | |
* 33 - Share | |
* 34 - End of Creator | |
*/ | |
/** | |
* Data Struct Offsets | |
* See https://github.com/metaplex-foundation/metaplex-program-library/blob/master/token-metadata/program/src/state.rs#L95-L106 | |
* See https://github.com/metaplex-foundation/metaplex-program-library/blob/master/token-metadata/program/src/state.rs#L40-L49 | |
* | |
* 0 - Name's length | |
* 4 - Name | |
* 36 - Symbol's length | |
* 40 - Symbol | |
* 50 - Uri's length | |
* 54 - Uri | |
* 254 - Seller Fee Basis Points | |
* 256 - If the Array of Creator exists | |
* 257 - Length of the Array of Creator | |
* 261 - Array of Creator (max of 5) | |
* 431 - End of Data | |
*/ | |
/** | |
* Metadata Struct Offsets | |
* See https://github.com/metaplex-foundation/metaplex-program-library/blob/master/token-metadata/program/src/state.rs#L221-L238 | |
* See https://github.com/metaplex-foundation/metaplex-program-library/blob/master/token-metadata/program/src/state.rs#L27-L38 | |
* | |
* 0 - Key | |
* 1 - Update Authority Public Key | |
* 33 - Mint Public Key | |
* 65 - Data | |
* 496 - primary sale | |
* 497 - mutable | |
* 498 - nonce (pretty sure this only needs to be 2) | |
* 507 - collection | |
* 541 - uses | |
* 559 - token standard | |
* 561 - Padding | |
* 679 - End of Metadata | |
*/ | |
const TOKEN_ADDRESS_OFFSET = 0; | |
const UPDATE_AUTHORITY_OFFSET = 1; | |
const PUBLIC_KEY_LENGTH = 32; | |
const readPublicKey = (buffer: Buffer, offset: number) => | |
base58.encode(buffer.slice(offset, offset + PUBLIC_KEY_LENGTH)); | |
const connection = new web3.Connection( | |
web3.clusterApiUrl('devnet'), | |
'confirmed' | |
); | |
async function getTokenProgramTokenAccountsByOwners( | |
walletPublicKey: web3.PublicKey | |
) { | |
const tokens = await connection.getTokenAccountsByOwner(walletPublicKey, { | |
programId: TOKEN_PROGRAM_ID | |
}); | |
return tokens.value; | |
} | |
const getTokenAddress = (buffer: Buffer) => | |
readPublicKey(buffer, TOKEN_ADDRESS_OFFSET); | |
async function getTokenAddressFromAssociatedToken( | |
associatedToken: web3.PublicKey | |
) { | |
const accountInfo = await connection.getAccountInfo(associatedToken); | |
if (!accountInfo) return undefined; | |
const buffer = Buffer.from(accountInfo.data); | |
const tokenAddress = getTokenAddress(buffer); | |
return new web3.PublicKey(tokenAddress); | |
} | |
const getUpdateAuthority = (buffer: Buffer) => | |
readPublicKey(buffer, UPDATE_AUTHORITY_OFFSET); | |
async function getUpdateAuthorityFromTokenAddress(token: web3.PublicKey) { | |
const [programAddress] = await web3.PublicKey.findProgramAddress( | |
[ | |
Buffer.from('metadata'), | |
TOKEN_METADATA_PROGRAM_ID.toBuffer(), | |
token.toBuffer() | |
], | |
TOKEN_METADATA_PROGRAM_ID | |
); | |
const accountInfo = await connection.getAccountInfo(programAddress); | |
if (!accountInfo) return undefined; | |
const buffer = Buffer.from(accountInfo.data); | |
return getUpdateAuthority(buffer); | |
} | |
async function getTokensOfOwnerFromWallet( | |
walletPublicKey: web3.PublicKey, | |
ownerPublicKey: web3.PublicKey | |
) { | |
const tokens = await getTokenProgramTokenAccountsByOwners(walletPublicKey); | |
return asyncFilter(tokens, async ({ pubkey }) => { | |
try { | |
const tokenAddress = await getTokenAddressFromAssociatedToken(pubkey); | |
if (!tokenAddress) return false; | |
const updateAuthority = await getUpdateAuthorityFromTokenAddress( | |
tokenAddress | |
); | |
return ownerPublicKey.toString() === updateAuthority; | |
} catch (e) { | |
return false; | |
} | |
}); | |
} | |
getTokensOfOwnerFromWallet( | |
new web3.PublicKey('...'), // Wallet Public Key | |
new web3.PublicKey('...'), // Owner Public Key | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment