Reputation: 842
I'm trying to consume a function from a smart contract using ethers.js. The function retrieve the info of a user logged before (with the help of other function). This is the function snippet.
function getUser(address _userAddress)
public
onlyAuthCaller
returns (
string memory name,
string memory info,
string memory role,
)
{
User memory tmpData = userDetails[_userAddress];
return (
tmpData.name,
tmpData.info,
tmpData.role
);
}
With React, I'm rendering a button to get user info, as follow:
const GetUser = () => {
const askUser = async () => {
const provider = new ethers.providers.Web3Provider(window.ethereum);
const account = await window.ethereum.request({
method: "eth_requestAccounts",
});
const signer = provider.getSigner();
const erc20 = new ethers.Contract(
ContractAddress,
ContractABI.abi,
signer
);
try {
const user = await erc20.getUser(account[0]);
console.log(user);
} catch (error) {
console.log("ERROR AT GETTING USER: ", error);
}
};
return (
<div>
<Button type="submit" variant="contained" onClick={askUser}>
GET USER
</Button>
</div>
);
};
I wonder why I'm not getting the return result of the smart contract getUser
function, I expected that info at const user
after awaiting the function. Instead on const user
, I'm having the transaction metadata, as follow:
{hash: '0x24818569ec29d328b66f58736750a420a5a3bd8e28a72a6a0f72fd8ba5e088d8', type: 2, accessList: null, blockHash: null, blockNumber: null, …}
accessList: null
blockHash: null
blockNumber: null
chainId: 0
confirmations: 0
creates: null
data: "0x6f77926b00000000000000000000000086b2b772014a87730928c7e54f4762d2c09ea4e5"
from: "0x86b2b772014A87730928c7e54F4762d2c09eA4e5"
gasLimit: BigNumber {_hex: '0xd15f', _isBigNumber: true}
gasPrice: BigNumber {_hex: '0x73a20d0c', _isBigNumber: true}
hash: "0x24818569ec29d328b66f58736750a420a5a3bd8e28a72a6a0f72fd8ba5e088d8"
maxFeePerGas: BigNumber {_hex: '0x73a20d0c', _isBigNumber: true}
maxPriorityFeePerGas: BigNumber {_hex: '0x73a20d00', _isBigNumber: true}
nonce: 5
r: "0x6a8fed76397e03a2fc564d18e1ec12abdf39a38fbe825df990f744bb50fc4a8b"
s: "0x66e9b4513047b65aac724dc6fb07d069967f6ca6fd8cd5fe85f6dbe495864765"
to: "0x9719E9dC77A7eDD3825844c77a68c896d4a7BB2b"
transactionIndex: null
type: 2
v: 0
value: BigNumber {_hex: '0x00', _isBigNumber: true}
wait: confirmations => {…}
length: 1
name: ""
arguments: (…)
caller: (…)
[[FunctionLocation]]: index.ts:336
[[Prototype]]: ƒ ()
[[Scopes]]: Scopes[4]
[[Prototype]]: Object
When I tried my contract's functions on Remix IDE, all worked as expected. For instance, at Remix I get this answer, in which the data retrieved by the function is on decoded output
.
status true Transaction mined and execution succeed
transaction hash 0x206af46a0f8e6bcc04ae632c85da005c901d8fc82f650e8d40a445f6988adcc2
from 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4
to SupplychainUser.getUser(address) 0xD7ACd2a9FD159E69Bb102A1ca21C9a3e3A5F771B
gas 61639 gas
transaction cost 53599 gas
execution cost 53599 gas
input 0x6f7...35cb2
decoded input {
"address _userAddress": "0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2"
}
decoded output {
"0": "string: name Carl Bertz",
"1": "string: info 0987654321",
"2": "string: role processor",
}
logs []
val 0 wei
I would like the same but with React, so how can I have the returned data from the contract getUser
function?
Upvotes: 6
Views: 8002
Reputation: 4091
The reason you are getting the transaction metadata when calling getUser
is because the getUser
function is not a view
function.
Not being a view
function causes the blockchain to create a transaction for that specific function call which has to be validated by the blockchain and is not available at the moment you execute your getUser
function.
For that type of situation, the recommended approach is to use events i.e. emitting an event with the information you need and, on the react side, listen for the event.
Consider making the getUser
function a view
function, as it does not change the state of the contract:
function getUser(address _userAddress)
public
view
onlyAuthCaller
returns (
string memory name,
string memory info,
string memory role,
)
{
User memory tmpData = userDetails[_userAddress];
return (
tmpData.name,
tmpData.info,
tmpData.role
);
}
Upvotes: 2
Reputation: 199
This is to answer a question in one of the comments: The EVM supports several kinds of "calls", which you can see in the Yul documentation here (https://docs.soliditylang.org/en/latest/yul.html#evm-dialect). A static call tells the EVM to expect no state changing instructions will be run (i.e., to enforce a call into a "view" function). The other call types are relevant to other types of calls (e.g., delegatecall() is at the core of supporting proxies).
call(g, a, v, in, insize, out, outsize)
call contract at address a with input mem[in…(in+insize)) providing g gas and v wei and output area mem[out…(out+outsize)) returning 0 on error (eg. out of gas) and 1 on success See more
callcode(g, a, v, in, insize, out, outsize)
identical to call but only use the code from a and stay in the context of the current contract otherwise See more
delegatecall(g, a, in, insize, out, outsize)
identical to callcode but also keep caller and callvalue See more
staticcall(g, a, in, insize, out, outsize)
identical to call(g, a, 0, in, insize, out, outsize) but do not allow state modifications See more
Upvotes: 1
Reputation: 605
you want to be using callStatic on your contract, something like
await erc20.callStatic.getUser(account[0])
so that you call
the function, instead of running a send
which executes the transaction (costing gas)
Upvotes: 2