DEV Community

Discussion on: Send React Web3 Transactions via MetaMask with useDapp

Collapse
 
jacobedawson profile image
Jacob E. Dawson

Hi George,

With useDapp, in order to call a payable function and pass along a value we are using the useContractFunction method, which will automatically forward arguments to the underlying ethers.js contract object - this means that we don't need to explicitly do anything other than just pass the value, like we do inside the call to setCount inside the Count component:

function handleSetCount() {
const _count = parseInt(input) {
if (_count) {
setCount(_count); // passing _count here, which is handled by useContractFunction and sent through to the contract by ethers under the hood
}
}
}

Collapse
 
jacobedawson profile image
Jacob E. Dawson

Btw here's the specific documentation for useContractFunction: usedapp.readthedocs.io/en/latest/c...

Thread Thread
 
georgeflatline profile image
GeorgeFlatline

Hi Jacob, thanks for the reply. I did have a read of the documentation but I am still a little stuck! I am able to call the contract with arguments when it is not expecting a value for the message but when it is expecting a value I am still not able to trigger this.

If I call it like this:

mintButton.tsx

import {useContractMethod } from "../hooks";

const { state: setMintTeaState, send: mintIt } = useContractMethod("mintTea");

function handleMint() {
    if (account) {
        mintIt(account, 1);
    }
}
Enter fullscreen mode Exit fullscreen mode

index.ts

export function useContractMethod(methodName: string) {

const { state, send } = useContractFunction(contract, methodName, {});

return { state, send };
Enter fullscreen mode Exit fullscreen mode

}

I am able to mint a token if the contract function is not expecting payment (the first 200 tokens are free). If I try beyond the first 200 I get an error in metamask, MetaMask - RPC Error: execution reverted: Value below price.

I have tried passing in a value as a third argument to mintIt() e.g.

function handleMint() {
    if (account) {
        mintIt(account, 1, ethers.utils.parseEther("50000000000000000"));
    }
}
Enter fullscreen mode Exit fullscreen mode

but when I run this metamask does not pop up a transaction as it does when I run it without the third argument.

The contract abi for the mintTea function looks like this and takes just two arguments, the address and the number to mint.

{
"inputs": [
{
"internalType": "address",
"name": "_to",
"type": "address"
},
{
"internalType": "uint256",
"name": "_count",
"type": "uint256"
}
],
"name": "mintTea",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},

and here is the mintTea and price functions from the Solidity contract:

function mintTea(address _to, uint _count) public payable saleIsOpen {
    if(msg.sender != owner()){
        require(!paused, "Paused");
    }
    require(totalSupply() + _count <= MAX_TEA, "Max limit");
    require(totalSupply() < MAX_TEA, "Sale end");
    require(_count <= 15, "Exceeds 15");
    require(msg.value >= price(_count), "Value below price");

    for(uint i = 0; i < _count; i++){
        _safeMint(_to, totalSupply());
    }
}

function price(uint _count) public view returns (uint256) {
    uint _id = totalSupply();
    // free 200
    if(_id <= 229 ){
        require(_count <= 1, "Max 1 Free Per TX");
        return 0;
    }

    return 50000000000000000 * _count; // 0.05 ETH
}
Enter fullscreen mode Exit fullscreen mode

Sorry for such a long question, I am still very new to dapp development in general so am probably missing something quite basic! Any suggestions would be greatly welcomed, I have been trying various things to get this to work for a few days now but feel like I may be missing something obvious.

Thread Thread
 
jacobedawson profile image
Jacob E. Dawson • Edited

Hi George - the value property is a special property that is added to the msg object that the contract function receives - you can pass in your arguments sequentially and add a configuration object as the last argument:

mintTea(arg1, arg2, {
value: PARSED_ETH_VALUE
});

Here's a concrete example - I updated the contract I used in this tutorial with a payable function called "takeTwoVariables":

    function takeTwoVariables(uint _valueA, uint256 _valueB) public payable {
        require(msg.value > 0);
        count = _valueA + _valueB;
    }
Enter fullscreen mode Exit fullscreen mode

Inside Count.tsx, I've imported utils from ethers (so we can parse our value):

// Count.tsx

import { utils } from "ethers";
Enter fullscreen mode Exit fullscreen mode

And I've added a new function called "handleTwoVariables":

  function handleTwoVariables() {
    const _count = parseInt(input);
    if (_count) {
      setTwoVariables(_count, 2, {
        value: utils.parseEther("0.05"),
      });
    }
  }
Enter fullscreen mode Exit fullscreen mode

I replaced "handleSetCount" with "handleTwoVariables" in the onClick handler, so when the user clicks "Set Count" we will send 0.05 ether to the payable function along with the 2 integers (_count & 2). If the value property is not set, or set to 0, the function will revert because in the contract there is now a require statement, so the value must be > 0.

So, the TLDR is that you add arguments sequentially (based on their defined order in the contract), and you add "special" properties on the msg object like "value" inside the config object.

I hope that makes sense! Feel free to ask any more questions if that isn't quite clear :)

Thread Thread
 
georgeflatline profile image
GeorgeFlatline

Thank you so much. That works perfectly and makes sense now. Thanks for taking the time to reply in such detail, it has really helped!