'UseDapp - useCall - How to get return value from Solidity function?
I am still quite new with react and solidity and don't know where to start. I can update a String and want to print it out after I changed it. But I want the String from the Blockchain. The return value is still 'undefined'.
My index.ts file:
export function MyString(contract: Contract) {
const { state, send } = useContractFunction(contract, "setString", {});
return { state, send };
}
export function GetMyString(contract: Contract) {
const { value, error } = useCall({
contract: contract,
method: 'getString',
args: []
}) ?? {};
if (error) {
console.log("Error: ", error.message);
return undefined;
}
console.log("value", value);
return value;
}
My react component:
import React, { useState } from 'react';
import { useEthers } from "@usedapp/core";
import { ethers } from "ethers";
import myStringContractAbi from "./abi/myString.json";
import { myStringContractAddress } from "./contracts";
import { Contract } from "@ethersproject/contracts";
import { MyString, GetMyString } from "./hooks"
function App() {
const { activateBrowserWallet, account, deactivate } = useEthers();
const simpleContractInterface = new ethers.utils.Interface(myStringContractAbi);
const contract = new Contract(myStringContractAddress, simpleContractInterface, ethers.getDefaultProvider(42));
const { send, state } = MyString(contract);
const value = GetMyString(contract);
const [text, setText] = useState<string>("")
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const newText = event.target.value === "" ? "" : String(event.target.value)
setText(newText)
}
const sendString = async () => {
send(text);
}
const getContractString = async () => {
console.log("APP VALUE", value);
alert(value);
}
React.useEffect(() => {
state.status === 'Exception' && console.log('State of set String: ', state.errorMessage);
state.status === 'Success' && console.log('Set String successfully!');
})
return (
<div>
{!account && <button onClick={() => activateBrowserWallet()}> Connect </button>}
{account && (
<div>
<button onClick={deactivate}>Disconnect</button>
</div>
)}
<div>
{account ? (
<div>My Account Address: {account} </div>
) : (
<div>Kein Account verbunden!</div>
)}
</div>
<input onChange={handleChange} />
<button onClick={sendString}>Call Function</button>
<button onClick={getContractString}>Call Get String Function</button>
</div>
);
}
export default App;
Here is my Contract:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract MyString {
string public myString;
constructor() {
myString = "Not set yet";
}
function setString(string memory _myString) public {
myString = _myString;
}
function getString() public view returns (string memory) {
return myString;
}
}
Can anybody help me? Do you need other info? Thanks
Solution 1:[1]
It might be because the account
that you are passing to getMessages(String(account))
is not same type as address
type in solidity.
In fact, to store the sender's address, you do not have to pass the account as function parameter. Etherum EVM already detect the caller address as msg.sender
.
IN solidity:
function getMessages() public view returns (string[] memory)
{
return messages[msg.sender];
}
in react side just call the function without passing a parameter.
const handleLoad = () => {
getMessages()
}
Solution 2:[2]
I recommend you to see about promises in js, but long history short to see the return value you should wait for it this can be done this way const {state, send} = await useContractFunction(...)
, but in order to use the await keyword the function where you use it should have the keyword async async (postContract: Contract) =>
, remember that all read and writes to the blockchain uses promises
Solution 3:[3]
I've seen that same error when missing await
keyword, we should always define it like that so the code waits for the smart contract function execution. I suggest you to add await
before calling your smart contract function.
Solution 4:[4]
If you want to get any value, you can not use useContractFunction
. Use useCall
instead of thats or use ethers
library.
Example useCall
import { useCall } from '@usedapp/core';
import { Contract } from '@ethersproject/contracts'; // comes with `@usedapp/core`
import {utils} from 'ethers';
const CONTRACT_ADDR = "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853";
const Interface = new utils.Interface(ABI); // ABI is array
const ContractInstance = new Contract(CONTRACT_ADDR, Interface);
// Custom contract payable function
const useTokenBalance = () => {
const { value, error } = useCall({
contract: ContractInstance,
method: 'TokenBalance',
args: [] /* params */
});
if(error) {
return undefined
}
console.log("value", value);
return value;
};
function App() {
//...
const TokenBalance = useTokenBalance();
const getTokenBalance = async () => {
alert(TokenBalance);
}
}
API Documentation: https://usedapp-docs.netlify.app/docs/api%20reference/hooks/#usecall
How to use ethers
library in useDApp
hook framework;
import { useEthers } from '@usedapp/core';
import { Contract as CTR } from 'ethers';
const CONTRACT_ADDR = "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853";
function App() {
//...
const { account, library } = useEthers();
const getTokenBalance = async () => {
const signer = library.getSigner();
const ctr = new CTR(CONTRACT_ADDR, ABI, signer);
let tx = await ctr.TokenBalance(/* params */);
alet(tx);
}
}
API Documentation: https://docs.ethers.io/v5/api/contract/contract/#Contract--creating
Tested versions;
^5.6.5
ethers^1.0.2
@usedapp/core
Update
Fully source code;
import './App.css';
import { useEthers, useContractFunction, useCall } from '@usedapp/core';
import {useState, useEffect, useCallback } from 'react';
import {utils, constants, BigNumber, Contract as CTR} from 'ethers';
import { Contract } from '@ethersproject/contracts'
const ENDPOINT = "http://127.0.0.1:4000";
const CONTRACT_ADDR = "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9";
const ABI = require('./Post.json').abi;
const Interface = new utils.Interface(ABI);
const Instance = new Contract(CONTRACT_ADDR, Interface);
const useAddMessage = () => {
const { state, send, event } = useContractFunction(
Instance,
'addMessage',
{}
);
return { state, send, event };
};
function App() {
const { state: addMessageState, send: addMessage } = useAddMessage();
const { activateBrowserWallet, account, library, chainId } = useEthers();
const [ownerBalance, set_ownerBalance] = useState(0);
const [contractInstance, set_contractInstance] = useState(null);
const [msg, setMsg] = useState("");
const [chainMsgs, setChainMsgs] = useState([]);
// Error watching
useEffect(() => {
addMessageState.status === 'Exception' &&
console.log('addMessageState: ', addMessageState.errorMessage);
addMessageState.status === 'Success' &&
console.log('Successfully added message to chain');
}, [addMessageState]);
useEffect(() => {
if(account){
library.getBalance(account).then(balance => {
set_ownerBalance(utils.formatUnits(balance, "ether"));
});
set_contractInstance(new CTR(CONTRACT_ADDR, ABI, library.getSigner()));
}
}, [account]);
const addMsg = async () => {
if(msg.length > 0){
let addedMessage = await addMessage(msg);
// when below function correctly worked, that triggered `Error watching` statement
}
};
const getMsg = async () => {
let chainMessages = await contractInstance.getMessages({from: account});
setChainMsgs(chainMessages);
};
return (
<>
<header>
{
!account &&
<button onClick={() => activateBrowserWallet()} >Connect Wallet</button>
}
{
account &&
<>
<div style={{display: 'flex'}}>
<h1 id="msg-addr">{ellipseAddress(account, 5)}</h1>
<span style={{margin: '0 0.5rem'}}>{ownerBalance} ETH</span>
</div>
<br />
</>
}
</header>
<main>
{
account &&
<>
<section>
<input type={"text"} placeholder={"Leave trail"} value={msg} onChange={e => setMsg(e.target.value)} />
<button onClick={async () => await addMsg()}>Add Message</button>
</section>
<section>
<button onClick={async () => await getMsg()}>Get Message</button>
{
chainMsgs.length > 0 &&
<ul>
{chainMsgs.map((chainMsg, i) => {
return (
<li key={i}>{chainMsg}</li>
)
})}
</ul>
}
</section>
</>
}
</main>
</>
);
}
function ellipseAddress(address='', width=10) {
return !address ? '' : address.slice(0, width) + "..." + address.slice(-width);
}
export default App;
Check correctly package version from package.json
;
"dependencies": {
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^13.0.0",
"@testing-library/user-event": "^13.2.1",
"@usedapp/core": "^1.0.2",
"ethers": "^5.6.5",
"react": "^18.1.0",
"react-dom": "^18.1.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.0"
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Yilmaz |
Solution 2 | jhonny |
Solution 3 | Mario Saucedo |
Solution 4 |