metaplex-foundation / js

A JavaScript SDK for interacting with Metaplex's programs
361 stars 185 forks source link

Fail to retrieve newly created NFT on mainnet #344

Open craigbroadbear opened 2 years ago

craigbroadbear commented 2 years ago

On mainnet-beta, happens about 30% of the time, using same wallet each time. Happens 0% of the time on devnet.

Using @metaplex-foundation/js 0.17.0

Code:

  const printResults = await mx
        .nfts()
        .printNewEdition({
            originalMint: new web3.PublicKey(masterNftAddress),
            newOwner: new web3.PublicKey(newOwnerAddress)
        })

Exception: `file:///C:/dev/ebook/ebook-server/node_modules/@metaplex-foundation/js/dist/esm/types/Account.mjs:28 throw new AccountNotFoundError(account.publicKey, name, { ^ AccountNotFoundError [MetaplexError]: Account Not Found

Source: SDK
Problem: The account of type [MintAccount] was not found at the provided address [DGPjXZXL7MTgktMV7cuEhkyoi4MsNwnkPn9x7Q3khCCB].
Solution: Ensure the provided address is correct and that an account exists at this address.
at assertAccountExists (file:///C:/dev/ebook/ebook-server/node_modules/@metaplex-foundation/js/dist/esm/types/Account.mjs:28:11)
at file:///C:/dev/ebook/ebook-server/node_modules/@metaplex-foundation/js/dist/esm/types/Account.mjs:20:7
at Object.handle (file:///C:/dev/ebook/ebook-server/node_modules/@metaplex-foundation/js/dist/esm/plugins/nftModule/operations/findNftByMint.mjs:65:25)
at runMicrotasks ()
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async Disposable.run (file:///C:/dev/ebook/ebook-server/node_modules/@metaplex-foundation/js/dist/esm/utils/Disposable.mjs:22:14)
at async Object.handle (file:///C:/dev/ebook/ebook-server/node_modules/@metaplex-foundation/js/dist/esm/plugins/nftModule/operations/printNewEdition.mjs:53:17)
at async Disposable.run (file:///C:/dev/ebook/ebook-server/node_modules/@metaplex-foundation/js/dist/esm/utils/Disposable.mjs:22:14)
at async mintNewEdition (file:///C:/dev/ebook/ebook-server/nft-create.js:72:26)
at async printNewEdition (file:///C:/dev/ebook/ebook-server/nft-print-edition.js:17:23) {
key: 'metaplex.errors.sdk.account_not_found', title: 'Account Not Found', problem: 'The account of type [MintAccount] was not found at the provided address [DGPjXZXL7MTgktMV7cuEhkyoi4MsNwnkPn9x7Q3khCCB].', solution: 'Ensure the provided address is correct and that an account exists at this address.', source: 'sdk', sourceDetails: undefined, cause: undefined, logs: undefined } } `

lorisleiva commented 2 years ago

Hi there 👋

Thanks for raising this! It's strange because from 0.17.0 I force the use of commitment: 'finalized' on the transaction's confirm option whenever we need to fetch an account after creating it.

We might also need to use that commitment level when fetching the account.

Could you try this by adding the commitment option to the second argument?

  const printResults = await mx
        .nfts()
        .printNewEdition({
            originalMint: new web3.PublicKey(masterNftAddress),
            newOwner: new web3.PublicKey(newOwnerAddress)
        }, { commitment: 'finalized' });

If that works 100% of the time, I'll update the SDK accordingly.

craigbroadbear commented 2 years ago

I tried suggestion, unfortunately it did not work. I was hopeful as the first 2 printNewEditions worked fine, but it bombed out on the third, same as before.

lorisleiva commented 2 years ago

Ugh, thanks for trying!

Could you just confirm to me that, even though it fails to fetch the NFT after printing it, the transaction was in fact successful? If you try and fetch the printed NFT again after your get the error, does that work?

craigbroadbear commented 2 years ago

The transaction failed to create the NFT. I followed up with a "findAllByOwner" and it was not in the list returned.

lorisleiva commented 2 years ago

Oh, that's interesting! I assumed this was a race condition issue but actually, it seems like it's a transaction being dropped by the blockchain. That being said, the sendAndConfirm call should fail because the transaction was not confirmed. Instead, it seems to not throw an error and, later on, when we try and fetch the NFT, it fails because it can't find it.

I don't really know how to fix this I'm afraid so any help would be greatly appreciated.

craigbroadbear commented 2 years ago

Same error, different call. This one on creating the "master" NFT.

On closer inspection, the NFTs are being created.

You can see the error below the mint is JC3LqP7MQyzd9Dgyt8kNAvYwaEXmZsdXS8g8vMyyoHgY, which does exist on the blockchain.

const masterNft = await mx
        .nfts()
        .create({
            name,
            symbol,
            uri: masterUri,
            sellerFeeBasisPoints,
            collection: collectionNft.nft.address,
            collectionAuthority: wallet,
            maxSupply: collectionSize,
            creators: [
                {
                    address: creatorPubKey,
                    share: 100,
                }
            ]

        })

Exception:


file:///C:/dev/ebook/ebook-server/node_modules/@metaplex-foundation/js/dist/esm/types/Account.mjs:28
throw new AccountNotFoundError(account.publicKey, name, {
^
Found
   >> Source: SDK
   >> Problem: The account of type [MintAccount] was not found at the provided address [JC3LqP7MQyzd9Dgyt8kNAvYwaEXmZsdXS8g8vMyyoHgY].
   >> Solution: Ensure the provided address is correct and that an account exists at this address.                                                                                                                                                                                                                                                                                                                                                                                       at assertAccountExists (file:///C:/dev/ebook/ebook-server/node_modules/@metaplex-foundation/js/dist/esm/types/Account.mjs:28:11) 
   at file:///C:/dev/ebook/ebook-server/node_modules/@metaplex-foundation/js/dist/esm/types/Account.mjs:20:7  
   at Object.handle (file:///C:/dev/ebook/ebook-server/node_modules/@metaplex-foundation/js/dist/esm/plugins/nftModule/operations/findNftByMint.mjs:65:25)
   at processTicksAndRejections (node:internal/process/task_queues:96:5)
   at async Disposable.run (file:///C:/dev/ebook/ebook-server/node_modules/@metaplex-foundation/js/dist/esm/utils/Disposable.mjs:22:14)
   at async Object.handle (file:///C:/dev/ebook/ebook-server/node_modules/@metaplex-foundation/js/dist/esm/plugins/nftModule/operations/createNft.mjs:65:17)
   at async Disposable.run (file:///C:/dev/ebook/ebook-server/node_modules/@metaplex-foundation/js/dist/esm/utils/Disposable.mjs:22:14)
   at async masterMint (file:///C:/dev/ebook/ebook-server/nft-create.js:39:23) 
   at async dropMaster (file:///C:/dev/ebook/ebook-server/drop-master.js:58:16) 
   at async makeDrop (file:///C:/dev/ebook/ebook-server/scripts/make-drop.js:30:5) {      
   key: 'metaplex.errors.sdk.account_not_found',  
   title: 'Account Not Found',   
   problem: 'The account of type [MintAccount] was not found at the provided address [JC3LqP7MQyzd9Dgyt8kNAvYwaEXmZsdXS8g8vMyyoHgY].',   
   solution: 'Ensure the provided address is correct and that an account exists at this address.',  
   source: 'sdk', 
   sourceDetails: undefined, 
   cause: undefined,  
   logs: undefined                                                                                                                                                                                                                        }                                                                     
craigbroadbear commented 2 years ago

Ok I fixed the problem, but its not ideal.

I edited the file: @metaplex-foundation\js\dist\esm\plugins\nftModule\operations\findNftByMint.mjs

Adding a pause of 1 second before the findNftByMintOperationHandler.handle function did anything. i.e.

const findNftByMintOperationHandler = {
  handle: async (operation, metaplex, scope) => {
      await new Promise( (resolve) => setTimeout(resolve, 1000));
     ...

I then minted 40 or so NFTs without any problems. This lets me move on for now, but would appreciate someone take a closer look at this.

amilz commented 2 years ago

In case this is helpful:

i just updated an nft mint script i use to 0.17.2 and got this same error when running await METAPLEX.nfts().create({...}) on devnet:

  key: 'metaplex.errors.sdk.account_not_found',
  title: 'Account Not Found',
  problem: 'The account of type [MintAccount] was not found at the provided address [68yHYGLHkkj791jRxED6GUgaDm1dwkLAtMA6qknFQqg8].',
  solution: 'Ensure the provided address is correct and that an account exists at this address.',
  source: 'sdk',
  sourceDetails: undefined,
  cause: undefined,
  logs: undefined

The NFT is minted despite this error--so the issue is in finding the NFT rather than confirming its mint. I got the error 3 times in about 10. Also 1x returned a blockhash error:

  key: 'metaplex.errors.rpc.failed_to_send_transaction',
  title: 'Failed to Send Transaction',
  problem: 'The transaction could not be sent successfully to the network.',
  solution: 'Check the error below for more information.',
  source: 'rpc',
  sourceDetails: undefined,
  cause: SendTransactionError: failed to send transaction: Transaction simulation failed: Blockhash not found

I added { commitment: 'finalized' } per your suggestion and had these results in 10 tries:

Success: 7 Account Error: 2 (NFT minted each time despite the error) Blockhash Error: 1

mjzuppe commented 2 years ago

@lorisleiva Hello! If helpful, these errors started occurring for me in the past week or so while on V.0.15 I just upgraded and it still intermittently occurs (less often then more often). However, it's typically happening when running printNewAddition

If you have any direction for me to help test, let me know.

amilz commented 2 years ago

Occasionally hitting a similar issue minting from a candy machine using SDK:

        let { nft, response } = await METAPLEX.candyMachines().mint({
            candyMachine,
            collectionUpdateAuthority: WALLET.publicKey,
          },{commitment:'finalized'});

NFT is actually getting minted, but there's an issue fetching the NFT. This is not happening 100% of the time.

CandyMachineBotTaxError [MetaplexError]: Candy Machine V3 > Candy Machine Bot Tax
>> Source: Plugin > Candy Machine V3
>> Problem: The NFT couldn't be fetched after being minted. This is most likely due to a bot tax that occured during minting. When someone tries to mint an NFT from a Candy Machine which cannot be minted from, the program will succeed and charge a small tax to fight against bots.
lorisleiva commented 2 years ago

Hey all 👋

Thanks for all the reports. I honestly have no idea why, using the finalized commitment, we would end up in a race condition where the created account is not yet accessible via the blockchain.

mjzuppe commented 2 years ago

As an FYI, the rate of this error seem to be increasing. I did attempt @craigbroadbear 's patch as well as a few others to no avail at the moment.

craigbroadbear commented 2 years ago

My fix was for esm type modules only.

lorisleiva commented 2 years ago

Hi there 👋

I think the problem is that there might be a delay between the time the transaction is finalized on the network and the time the NFT is indexed by an RPC like Quicknode.

I'm leaning to change the JS SDK's API such that operations either write or read but not both. It's a little less convenient but more predictable. It won't fix the race condition issue if you're fetching the NFT immediately after creating it but will push the problem to userland instead though.

In the meantime, you can achieve the same behaviour by using transaction builders instead.

const transactionBuilder = await metaplex.nfts().builders().create({ ... });
const { mintAddress } = transactionBuilder.getContext();
await metaplex.rpc().sendAndConfirm(transactionBuilder);

// Then, optionally fetch the NFT afterwards after sleeping for 2 seconds. 🤢
await new Promise(resolve => setTimeout(resolve, 2000))
const nft = await metaplex.nfts().findByMint({ mintAddress });
craigbroadbear commented 2 years ago

Why not put the sleep in the library code so it all works as intended? Yes its ugly, but is that a reason not to do it when it fixes the issue?

mjzuppe commented 2 years ago

My team's experience with our implementation and testing is that this call fails at high rates on both private and public RPC routes. In addition, our current workaround we've been using for over a month now to query and get a reliable response for NFT address is far more drastic than a timeout of 2 seconds. We have retry logic making attempts up to 10 seconds apart and sometimes it takes several cycles to get a proper response.

mikemaccana commented 1 year ago

~I'm trying trying to implement the workaround from https://github.com/metaplex-foundation/js/issues/344#issuecomment-1325265657 but on await metaplex.rpc().sendAndConfirm(transactionBuilder); I get Property 'sendAndConfirm' does not exist on type 'RpcClient'. in metaplex 0.18.3~

Update: it's sendAndConfirmTransaction() now

mikemaccana commented 1 year ago

Also @lorisleiva I think @craigbroadbear's suggestion was good - releasing a workaround in the actual @metaplex-foundation/js would make metaplex just work, rather than each metaplex user having to encounter the bug and apply the workaround.

LucasMoskun commented 1 year ago

Bump +1 running into the same issue. I'm using commitment finalized, transaction is succeeding, but create function is non-deterministically returning The account of type [MintAccount] was not found at the provided address

JuanRdBO commented 1 year ago

Bump +1 running into the same issue. I'm using commitment finalized, transaction is succeeding, but create function is non-deterministically returning The account of type [MintAccount] was not found at the provided address

I have the same error with the newest SDK. Has anyone been able to fix this? Seems it's been happing for some time now