1

I have to pass token amount as argument on a function call. The amount is dynamic and coming from a text box when a user click button (for partial amount submission). There is another button that allow to submit all available token amount.

So if total token amount is 5000. A user can input any amount in between 1 to 5000 in text box and click the button which invoke write function from useContractWrite hook. And then there is another button that when clicked by the user should pass 5000 as argument and invoke write function.

Below are the 2 approaches I tired, but both is not working. I have commented the respective part of my code to make my point more clear.

1st approach (Using state hook)

import { BigNumber, utils } from "ethers";
import React, { useEffect, useState } from "react";
import {
  usePrepareContractWrite,
  useContractWrite,
  useWaitForTransaction,
} from "wagmi";
import { useDebounce } from "@/hooks/usehooks";
import {
  tokenAddress,
  tokenAbi,
} from "../../constants/Token";

const submitToken: React.FC = ({tokenAmount}) => {
  const [partialAmount, setPartialAmount] = useState<string>("0");
  const [totalTokenAmount, setTotalTokenAmount] = useState<number>(tokenAmount);
  const [amountToPut, setAmountToPut] = useState<BigNumber>(
    BigNumber.from("0")
  ); // this state is argument 
  const debountAmount = useDebounce(partialAmount, 500); //rate limiting

  const {
    config,
    error: prepareError,
    isError: isPrepareError,
  } = usePrepareContractWrite({
    address: tokenAddress,
    abi: tokenAbi,
    functionName: "myWritableFun",
    args: [amountToPut], // This doesn't update when we change the `partialAmount` in function below
  });

  const {
    data,
    error: contractError,
    isError: isContractError,
    write,
  } = useContractWrite(config);

  const { isLoading, isSuccess } = useWaitForTransaction({
    hash: data?.hash,
  });

  const putPartialToken = () => {
    if (isNaN(Number(debountAmount))) {
      alert("Not a valid amount");
      return;
    }
    // we are checking this in contract too
    if (Number(debountAmount) > totalTokenAmount) {
      alert("Amount overflow!");
      return;
    }
    //the below doesn't work on first click So,basically this is where I am not sure how to set `amountToPut`
    setAmountToPut(utils.parseEther(debountAmount));    
    write?.();  
  };

  const putAllToken = async () => {    
    setAmountToPut(utils.parseEther(totalTokenAmount.toString())); 
    write?.();
  };

  return (
    <div>
      
      <p>
        Submit All token{" "}
         {/* Submit all amount, first event */}
        <button onClick={putAllToken} disabled={!write || isLoading}>
          Submit All Amount
        </button>
      </p>
      <p>
        Sumbit partial amount
        <input
          type="text"
          id="partialSubmit"
          onChange={(e) => setPartialAmount(e.target.value)}
        />
       {/* Submit partial amount, second event */}
        <button onClick={putPartialToken} disabled={!write || isLoading}>
          Submit Partial Amount
        </button>
      </p>
    </div>
  );
};
export default submitToken;

The 2nd approach (using a variable)

import { BigNumber, utils } from "ethers";
import React, { useEffect, useState } from "react";
import {
  usePrepareContractWrite,
  useContractWrite,
  useWaitForTransaction,
} from "wagmi";
import { useDebounce } from "@/hooks/usehooks";
import {
  tokenAddress,
  tokenAbi,
} from "../../constants/Token";

const submitToken: React.FC = ({tokenAmount}) => {
  const [partialAmount, setPartialAmount] = useState<string>("0");
  const [totalTokenAmount, setTotalTokenAmount] = useState<number>(tokenAmount);
  
  const debountAmount = useDebounce(partialAmount, 500); //rate limiting

  // I am using variable instead of state
  let amountToPut = utils.parseEther("0");

  const {
    config,
    error: prepareError,
    isError: isPrepareError,
  } = usePrepareContractWrite({
    address: tokenAddress,
    abi: tokenAbi,
    functionName: "myWritableFun",
    args: [amountToPut], // This doesn't update when we change the `partialAmount` in function below
  });

  const {
    data,
    error: contractError,
    isError: isContractError,
    write,
  } = useContractWrite(config);

  const { isLoading, isSuccess } = useWaitForTransaction({
    hash: data?.hash,
  });

  const putPartialToken = () => {
    if (isNaN(Number(debountAmount))) {
      alert("Not a valid amount");
      return;
    }
    // we are checking this in contract too
    if (Number(debountAmount) > totalTokenAmount) {
      alert("Amount overflow!");
      return;
    }

    //This does change the value of `amountToPut` but it is not updated in `usePrepareContractWrite` function above 
    amountToPut = utils.parseEther(debouncedStakeAmount); 
    write?.();  
  };

  const putAllToken = async () => {    
    //This does change the value of `amountToPut` but it is not updated in `usePrepareContractWrite` function above 
    amountToPut = utils.parseEther(totalTokenAmount.toString()); 
    write?.();
  };

  return (
    <div>
      
      <p>
        Submit All token{" "}
        {/* Submit all amount, first event */}
        <button onClick={putAllToken} disabled={!write || isLoading}>
          Submit All Amount
        </button>
      </p>
      <p>
        Sumbit partial amount
        <input
          type="text"
          id="partialSubmit"
          onChange={(e) => setPartialAmount(e.target.value)}
        />
        {/* Submit partial amount, second event */}
        <button onClick={putPartialToken} disabled={!write || isLoading}>
          Submit Partial Amount
        </button>
      </p>
    </div>
  );
};
export default submitToken;

Not sure how to solve this problem so that both button onclick event help to update the argument amountToPut in usePrepareContractWrite before calling write?.() ?


Update I also tried using useRef hooks. It also didn't work.

const amountToPut = useRef<BigNumber>(BigNumber.from("0"));
 const {
    config,
    error: prepareError,
    isError: isPrepareError,
  } = usePrepareContractWrite({
    address: tokenAddress,
    abi: tokenAbi,
    functionName: "myWritableFun",
    args: [amountToPut.current], // This doesn't update when we change the `partialAmount` in function below
  });
const putPartialToken = () => {
    if (isNaN(Number(debountAmount))) {
      alert("Not a valid amount");
      return;
    }
    // we are checking this in contract too
    if (Number(debountAmount) > totalTokenAmount) {
      alert("Amount overflow!");
      return;
    }
   
    amountToPut.current = utils.parseEther(debouncedStakeAmount); 
    write?.();  
  };

const putAllToken = async () => {      
    amountToPut.current = utils.parseEther(totalTokenAmount.toString()); 
    write?.();
  };
3
  • Sanjay, did you figure this out? I'm having a similar issue with a state variable that the UI is updating but isn't making it into the usePrepareContractWrite hook where I need it. 🙏
    – Jordan
    Mar 25 at 2:01
  • I think I just figured it out. I needed to instantiate the state value in the useState().
    – Jordan
    Mar 25 at 2:08
  • No. It didn't worked. Instead I settled with useContractWrite and removed usePrepareContractWrite completely. If you need to perform input data validation, its really hard.
    – sanjay dev
    Mar 26 at 15:23

1 Answer 1

1

Use actions instead of hooks: import { prepareWriteContract, writeContract } from 'wagmi/actions'

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.