Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Getting 'undefined' as a return value when invoking a getter smartcontract function #1049

Open
bhupendra-chouhan opened this issue Sep 16, 2024 · 5 comments
Assignees

Comments

@bhupendra-chouhan
Copy link

To demonstrate the issue, I built a small full-stack dApp called "Anonymous Feedback dApp" using ReactJS, TailwindCSS, Stellar-SDK, Soroban-SDK, and Freighter Wallet.

GitHub : https://github.com/bhupendra-chouhan/Anonymous-Feedback-Soroban

Please refer to the project's README file for a detailed explanation of the issue, along with the installation/setup guide and steps to reproduce it (screenshots included).

Issue:
In the code below, there is a function named contractInt(), which is used to invoke contract functions. While it successfully stores data on-chain when invoking a setter function, the problem occurs when I try to retrieve a return value using the returnValue() method from a getter function. Instead of the expected output, I receive 'undefined'.

/* Soroban.js */

import {
  Contract,
  SorobanRpc,
  TransactionBuilder,
  Networks,
  BASE_FEE,
  nativeToScVal,
} from "@stellar/stellar-sdk";
import { userSignTransaction } from "./Freighter";

let rpcUrl = "https://soroban-testnet.stellar.org";

let contractAddress =
  "CDAN4KQKD633XF6MCOHI7Q3DJQX4E7ENCGKUBHGQKIKJWI6DVDPX54XW";

// coverting String to ScVal form
const stringToScValString = (value) => {
  return nativeToScVal(value); // XDR format conversion
};

const numberToU64 = (value) => {
  return nativeToScVal(value, { type: "u64" });
};

let params = {
  fee: BASE_FEE,
  networkPassphrase: Networks.TESTNET,
};

// Transaction Builder Function:
async function contractInt(caller, functName, values) {
  const server = new SorobanRpc.Server(rpcUrl, { allowHttp: true });
  const sourceAccount = await server.getAccount(caller);
  const contract = new Contract(contractAddress);
  let builtTransaction;

  if (values == null) {
    builtTransaction = new TransactionBuilder(sourceAccount, params)
      .addOperation(contract.call(functName))
      .setTimeout(30)
      .build();
  } else if (Array.isArray(values)) {
    builtTransaction = new TransactionBuilder(sourceAccount, params)
      .addOperation(contract.call(functName, ...values))
      .setTimeout(30)
      .build();
  } else {
    builtTransaction = new TransactionBuilder(sourceAccount, params)
      .addOperation(contract.call(functName, values))
      .setTimeout(30)
      .build();
  }

  let _buildTx = await server.prepareTransaction(builtTransaction);

  let prepareTx = _buildTx.toXDR(); // pre-encoding (converting it to XDR format)

  let signedTx = await userSignTransaction(prepareTx, "TESTNET", caller);

  let tx = TransactionBuilder.fromXDR(signedTx, Networks.TESTNET);

  try {
    let sendResponse = await server.sendTransaction(tx).catch(function (err) {
      console.error("Catch-1", err);
      return err;
    });
    if (sendResponse.errorResult) {
      throw new Error("Unable to submit transaction");
    }
    if (sendResponse.status === "PENDING") {
      let getResponse = await server.getTransaction(sendResponse.hash);
      //   we will continously checking the transaction status until it gets successfull added to the blockchain ledger or it gets rejected
      while (getResponse.status === "NOT_FOUND") {
        getResponse = await server.getTransaction(sendResponse.hash);
        await new Promise((resolve) => setTimeout(resolve, 1000));
        }
        
        console.log(`getTransaction response: ${JSON.stringify(getResponse)}`);

      if (getResponse.status === "SUCCESS") {
        // Make sure the transaction's resultMetaXDR is not empty
        if (!getResponse.resultMetaXdr) {
          throw "Empty resultMetaXDR in getTransaction response";
        }

        // Find the return value from the contract and return it
        let transactionMeta = getResponse.resultMetaXdr;
        let returnValue = transactionMeta.v3().sorobanMeta().returnValue();
        console.log(`Transaction result: ${returnValue.value()}`);
      } else {
        throw `Transaction failed: ${getResponse.resultXdr}`;
      }
    } else {
        throw sendResponse.errorResultXdr;
    }
  } catch (err) {
    // Catch and report any errors we've thrown
    console.log("Sending transaction failed");
    console.log(JSON.stringify(err));
  }
}


// Interaction Functions: Built To interact with it's respective smart contract functions:

async function sendFeedback(caller, fbData) {  
  let value = stringToScValString(fbData); //XDR format  let result;

  try {
    let result = await contractInt(caller, "send_feedback", value);
    console.log("Your Feedback ID is: ", result); // ⚠️ 'result' should be an object, but getting 'undefined'
  } catch (error) {
    console.log("Unable to create Feedback!!, ", error);
  }

  //  Converting to regular Number type:
  // let fbId = Number(result?._value?._attributes?.val?._value)
  // return fbId;
}
async function fetchFeedback(caller, fb_id) {
  let value = numberToU64(fb_id);
  let result;

    try {
        result = await contractInt(caller, "fetch_feedback", value);
        console.log(`Fetched Feedback for the feedback-Id ${fb_id} is : ${result}`); // ⚠️ 'result' should be an object, but getting 'undefined'
    } catch (error) {
        console.log("Unable to fetch Feedback!!, ", error);
    }

    //  Converting to regular string type:
    // let feedback = result?._value?._attributes?.val?._value?.toString();
    // return feedback;
}

export {
    sendFeedback,
    fetchFeedback
};

Screenshots of Issue (You can reproduce the issue by following the screenshots):

  1. Creating a Feedback and Storing it onchain by invoking the send_feedback() smartcontract function:
    image

    Result:

    • Expected output: 4
    • Output got: Undefined
      image
  2. Fetching a feedback with feedback-id 4 by invoking the fetch_feedback() smartcontract function:
    image

    Result:

    • Expected output: Feedback Number 4
    • Output got: Undefined
      image
@github-project-automation github-project-automation bot moved this to Backlog (Not Ready) in DevX Sep 16, 2024
@Shaptic
Copy link
Contributor

Shaptic commented Sep 17, 2024

There's a few things I'd suggest here:

  • Your transaction is failing, so there won't be a returnValue. You should look for errors during simulation or at the diagnostic events within the resultMetaXdr (these are within resultMetaXdr.v3().sorobanMeta()).
  • Your contractInt function doesn't return anything in the success case, so it'll be undefined regardless.
  • You can use scValToNative to convert the return value to something readable instead of digging into the ._attributes and whatnot.

@bhupendra-chouhan
Copy link
Author

Thank you for your response and suggestions, @Shaptic .

Replying to each of the points you mentioned:

  • No matter which smart contract function I invoke from the front end, whenever I click the "Sign" button of the Freighter Wallet, the transaction is successfully signed and sent to the blockchain ledger without failing. I verify whether transactions are successful by searching for the smart contract address in the Stellar block explorer (i.e., stellar.expert).

This is the newly deployed smart contract address: CBG7QFA5CWUIJ6QQQSCWS33UNV6TN3EVQHRZLR5VYJWT5X73J6Y46U7A.

You can see all the function calls and successful transactions that have occurred under this deployed smart contract address by visiting this page: https://stellar.expert/explorer/testnet/contract/CBG7QFA5CWUIJ6QQQSCWS33UNV6TN3EVQHRZLR5VYJWT5X73J6Y46U7A

  • The contractInt function doesn't return anything right now because I was first trying to print the return value in the browser console.
  • I will definitely start using scValToNative from now onwards.

@Shaptic
Copy link
Contributor

Shaptic commented Sep 18, 2024

I see, thanks for the clarifications.

The contractInt function doesn't return anything right now because I was first trying to print the return value in the browser console.

But the bug report is about you getting undefined as a return value, and you would get that as the return value even if there was success! 😆

The reason why I said your transaction failed is because of the console output in your second screenshot (Sending transaction failed). Based on the code, this occurs in your main block of submission code, but it's unclear why its occurring. Keep in mind that catch statement will catch all sorts of other errors. I would recommend running the code without that try/catch block to see what the real error is.

For example, you use the Networks.TESTNET variable in one place and the string "TESTNET" in your invocation of userSignTransaction which may be causing issues. But that's just an example. It could be anything in that block and you just call it "failed to send." Based on what you said, it seems like the transaction actually does successfully send, but you're triggering the catch elsewhere which causes no return value.

@Iwueseiter
Copy link

I am applying to this issue via OnlyDust platform.

My background and how it can be leveraged

I'm a frontend and smart contract developer. I've contributed to Projects here on onlydust
and with that experience, I would handle this task as expected. This would also be my first time
contributing to this project.

How I plan on tackling this issue

To implement this,

  1. I will create a Feedback and Storing it onchain by invoking the send_feedback() smartcontract function. I will ensure it displays the expeted output which is 4.

  2. Fetch a feedback with feedback-id 4 by invoking the fetch_feedback() smartcontract function, and ensure it displays the expected Feedback Number 4.

ETA: 48hrs

@martinvibes
Copy link

I am applying to this issue via OnlyDust platform.

My background and how it can be leveraged

hello i am a frontend dev and blockchain developer
please can i work on this issue :) and would love to be a contributor

How I plan on tackling this issue

please kindly assign me and i'll get straight to work

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Backlog (Not Ready)
Development

No branches or pull requests

5 participants