-
Notifications
You must be signed in to change notification settings - Fork 44
How to get NFT token metadata by mint? #22
Comments
Finally figured out how to get it. Code should look something like this. $mint = "4EbYVzfS5ad5wYcAdoaxJM7HUeiQq9RnCeSm289CUcC6";
$METADATA_PUBKEY = new PublicKey('metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s');
/** @var PublicKey $pda */
list($pda, $bump) = PublicKey::findProgramAddress([
'metadata',
$METADATA_PUBKEY,
(new PublicKey($mint)),
], $METADATA_PUBKEY);
$metadataAddress = $pda->getPublicKey()->toBase58();
$sdk = new Connection(new SolanaRpcClient(SolanaRpcClient::MAINNET_ENDPOINT));
$accountInfo = $sdk->getAccountInfo($metadataAddress);
$base64Data = $accountInfo['data'][0];
$buffer = Buffer::from(base64_decode($base64Data));
$metadata = Metadata::fromBuffer($buffer->toArray());
dd($metadata); |
I refactored the code a bit, now it is possible to fetch all NFT's by the given wallet address. (Laravel) namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Log;
use Tighten\SolanaPhpSdk\Accounts\Metadata;
use Tighten\SolanaPhpSdk\Connection;
use Tighten\SolanaPhpSdk\Programs\MetaplexProgram;
use Tighten\SolanaPhpSdk\Programs\SplTokenProgram;
use Tighten\SolanaPhpSdk\PublicKey;
use Tighten\SolanaPhpSdk\SolanaRpcClient;
use Tighten\SolanaPhpSdk\Util\Buffer;
class NftController extends Controller
{
/*
* @see https://docs.metaplex.com/architecture/deep_dive/overview
*/
public function getNfts(Request $request)
{
$walletAddress = $request->get('walletAddress');
$solanaRpcClient = new SolanaRpcClient(SolanaRpcClient::MAINNET_ENDPOINT);
$splTokenProgram = new SplTokenProgram($solanaRpcClient);
$tokenAccounts = $splTokenProgram->getTokenAccountsByOwner($walletAddress);
if (empty($tokenAccounts['value'])) {
new Response("Nft not found", 404);
}
$tokens = $tokenAccounts['value'];
$sdk = new Connection($solanaRpcClient);
$nftMetaDatas = [];
foreach ($tokens as $token) {
try {
if (empty($token['account']['data']['parsed']['info']['mint'])) {
continue;
}
$mint = $token['account']['data']['parsed']['info']['mint'];
$metaplexProgramPublicKey = new PublicKey(MetaplexProgram::METAPLEX_PROGRAM_ID);
/** @var PublicKey $pda */
list($pda, $bump) = PublicKey::findProgramAddress([
'metadata',
$metaplexProgramPublicKey,
(new PublicKey($mint)),
], $metaplexProgramPublicKey);
$metadataAddress = $pda->getPublicKey()->toBase58();
$accountInfo = $sdk->getAccountInfo($metadataAddress);
if (empty($accountInfo['owner']) || $accountInfo['owner'] !== MetaplexProgram::METAPLEX_PROGRAM_ID) {
//This is not nft
continue;
}
if (empty($accountInfo['data'][0]) || empty($accountInfo['data'][1]) || $accountInfo['data'][1] !== 'base64') {
//Not the format we are trying to convert
continue;
}
$base64Data = $accountInfo['data'][0];
$buffer = Buffer::from(base64_decode($base64Data));
$metadata = Metadata::fromBuffer($buffer->toArray());
$nftMetaDatas[] = $metadata;
} catch (\Throwable $throwable) {
Log::error("Error while fetching NFT for wallet address $walletAddress Reason: " . $throwable->getMessage());
continue;
}
}
if (empty($nftMetaDatas)) {
new Response("Nft not found", 404);
}
return new Response($nftMetaDatas);
}
} |
Cool and thanks for the example! I'm doing it in a similar fashion but with multiple jobs running the background. One thing that I might add, is an additional check for NFTs as your code currently will return USDT & USDC token accounts as NFTs with metadata. While it's not perfect, I'm running additional checks mentioned here https://gist.github.com/creativedrewy/9bce794ff278aae23b64e6dc8f10e906#step-2-locate-nft-accounts |
@neverything thanks for sharing, seems like the biggest challenge is to not hit rate limits. Do you have any example of how do you handle it? |
Using rate limiter middleware on the jobs and some custom delays. Update: But I must admit, I haven't fully figured out how to make it run perfectly and smooth. Currently running roughly 4.5k jobs per hour in the background on Redis/Horizon. Now that the Borsh implementation with the Metadata stuff is working in this package, I'm trying to implement a more relaxed approach, especially when it comes to updating token accounts and NFT metadata. But I'm still learning (not a pro at any of this yet), hence my thread on twitter https://twitter.com/neverything/status/1478751444329238533 |
Thanks for sharing your code! Hopefully it will help others :) |
Found important detail want to also share here. if(empty($token['account']['data']['parsed']['info']['tokenAmount']['amount'])){
Log::info('This NFT does not belongs to account anymore, skip');
continue;
} |
@faridmovsumov thanks for sharing. I was wondering if there was an easy way to check this without going through all the transactions from a wallet or a NFT token account. |
Unfortunately couldn't find anything easier, there is no option to filter out this in API as far as I understood. |
Hello,
I am super happy to discover this repository and appreciate what you do here.
![image](https://user-images.githubusercontent.com/1541412/149634657-da499558-383a-49bc-b9e0-d51137cbf053.png)
I checked previous issues and found past discussions regarding metadata, but unfortunately, I couldn't make it work.
Mint is 4EbYVzfS5ad5wYcAdoaxJM7HUeiQq9RnCeSm289CUcC6 (I also tried with 3k7M6KoYQ8kkpcWqceAZokLJHfiVfHGNtU7an5BGPh3C)
I am trying to get metadata with the following code
But it is throwing the following error
Tighten\SolanaPhpSdk\Borsh\BorshException: Expected buffer length 32 isn't within bounds in file /Users/farid/projects/web3/vendor/tightenco/solana-php-sdk/src/Borsh/BinaryReader.php on line 177
I also checked this token on Solscan and it looks correct. Not sure if I am using the correct id.
https://solscan.io/token/4EbYVzfS5ad5wYcAdoaxJM7HUeiQq9RnCeSm289CUcC6#metadata
Could you please help me to understand what is wrong here?
The text was updated successfully, but these errors were encountered: