hashgraph / hedera-sdk-js

Hedera™ Hashgraph SDK for JavaScript/TypeScript
https://docs.hedera.com/guides/docs/sdks
Apache License 2.0
257 stars 132 forks source link

Issue with failing TokenUpdateNftsTransaction #2530

Open justynspooner opened 2 weeks ago

justynspooner commented 2 weeks ago

Hey team.

We're seeing an issue with failing transactions in the Hedera WalletConnect repo as described here: https://github.com/hashgraph/hedera-wallet-connect/issues/265

It looks as if the error is stemming from this line:

https://github.com/hashgraph/hedera-sdk-js/blob/ba47014e129cca1125e1a143b677ed9bba43439c/src/token/TokenUpdateNftsTransaction.js#L104

Before we continue further investigations, could someone please verify that this line is correct. It appears as if it's not accessing the correct data key. I'm not familiar enough with this code, but it feels like the data key should be this instead:

body.tokenUpdateNfts

as defined here:

https://github.com/hashgraph/hedera-sdk-js/blob/ba47014e129cca1125e1a143b677ed9bba43439c/src/token/TokenUpdateNftsTransaction.js#L195-L197

Any input would be appreciated as it's hard to see what else is wrong from the Hedera WalletConnect side. Many thanks!

ivaylogarnev-limechain commented 1 week ago

Hi Justyn,

I hope you're doing well! I took some time to investigate the issue and cloned Nadine's repo to see if I could reproduce the problem. I was able to replicate the same behavior. However, I also tested the functionality in the SDK environment (please see the example below) and it appeared to work without any issues at first glance.

It’s worth noting that this feature is currently being utilized successfully by other users and wallets, and it seems to be functioning correctly in those contexts. If you haven’t been able to find the issue elsewhere, we will investigate deeper into it. Just let me know how you’d like to proceed :))

SDK environment example:

import {
    Client,
    PrivateKey,
    AccountId,
    TransactionId,
    TokenType,
    TokenCreateTransaction,
    TokenMintTransaction,
    LocalProvider,
    TokenUpdateNftsTransaction,
    Wallet,
} from "@hashgraph/sdk";

import dotenv from "dotenv";
dotenv.config();

async function main() {
    if (process.env.OPERATOR_ID == null || process.env.OPERATOR_KEY == null) {
        throw new Error(
            "Environment variables OPERATOR_ID, and OPERATOR_KEY are required.",
        );
    }
    const operatorId = AccountId.fromString(process.env.OPERATOR_ID);
    const operatorKey = PrivateKey.fromStringED25519(process.env.OPERATOR_KEY);

    const client = Client.forTestnet().setOperator(operatorId, operatorKey);

    const provider = new LocalProvider();

    const wallet = new Wallet(
        process.env.OPERATOR_ID,
        process.env.OPERATOR_KEY,
        provider,
    );

    const createNft = async () => {
        const supplyKey = PrivateKey.generateED25519();
        const metadataKey = PrivateKey.generateED25519();

        const tokenCreateTx = await new TokenCreateTransaction()
            .setTokenName("TestToken")
            .setTokenSymbol("TEST")
            .setTokenType(TokenType.NonFungibleUnique)
            .setTreasuryAccountId(wallet.accountId.toString())
            .setAutoRenewAccountId(wallet.accountId.toString())
            .setAutoRenewPeriod(7776000)
            .setSupplyKey(supplyKey.publicKey)
            .setMetadataKey(metadataKey.publicKey)
            .setTransactionId(
                TransactionId.generate(wallet.accountId.toString()),
            )
            .freezeWithSigner(wallet);

        const txResult1 = await tokenCreateTx.executeWithSigner(wallet);
        const nftCreateRx = await txResult1.getReceipt(client);
        const tokenId = nftCreateRx.tokenId;

        console.log("NFT created! Token Id" + tokenId);
        console.log("MetadataKey:" + metadataKey);
        return { tokenId, metadataKey, supplyKey };
    };

    const mintNft = async (tokenId, supplyKey) => {
        const metadataBytes = new TextEncoder().encode(
            "QmbokN1onYuUHCUagbUApmd3zsw2xgN1yoJ8ouHnb1qApu",
        );
        const tokenMintTx = new TokenMintTransaction()
            .setTokenId(tokenId)
            .addMetadata(metadataBytes);

        await tokenMintTx.freezeWithSigner(wallet);
        console.log("Signing NFT with MetadataKey....");
        const signedTokenMintTx = await tokenMintTx.sign(supplyKey);
        console.log("NFT signed!");

        const txResult = await signedTokenMintTx.executeWithSigner(wallet);
        const nftMintRx = await txResult.getReceipt(client);
        const supply = nftMintRx.totalSupply;
        console.log(
            `NFT minted with TokenId: ${tokenId}. New total supply is ${supply}`,
        );

        return txResult ? txResult.transactionId : null;
    };

    const metadataUpdate = async (
        tokenId,
        serialNumber,
        newMetadata,
        metadataKey,
    ) => {
        const newMetadataUri = new TextEncoder().encode(newMetadata);

        const updateTransaction = new TokenUpdateNftsTransaction()
            .setTokenId(tokenId)
            .setSerialNumbers([serialNumber])
            // @ts-ignore
            .setMetadata(new TextEncoder().encode(newMetadataUri));

        await updateTransaction.freezeWithSigner(wallet);
        const signedUpdateTx = await updateTransaction.sign(metadataKey);

        const txResult = await signedUpdateTx.executeWithSigner(wallet);
    };

    try {
        const newMetadata = "The input text field ";
        // Create the NFT and get tokenId and metadataKey
        console.log("Creating NFT...");
        const { tokenId, metadataKey, supplyKey } = await createNft();

        // Mint the NFT using the tokenId from the create step
        console.log("Minting NFT...");
        const serialNumber = await mintNft(tokenId, supplyKey);

        // Update the metadata after minting the NFT
        console.log("Updating metadata...");
        await metadataUpdate(tokenId, serialNumber, newMetadata, metadataKey);

        console.log(
            "NFT creation, minting, and metadata update completed successfully.",
        );
    } catch (error) {
        console.error(error);
    }

    client.close();
}

void main();