Here is the code I used to call setValue and getValue in contract Hello.sol deployed with etherjs 5.0.14 on android/windows10:
import { ContractFactory, ethers } from "ethers";
let url = "http://localhost:8545";
const provider = new ethers.providers.JsonRpcProvider(url);
const signer = provider.getSigner(); //forJsonRPC
let contract_at_address = new ethers.Contract(contract.address, abi, signer); //<<==contract.address is the deployed address. abi and signer were defined earlier.
await contract_at_address.setValue(10); //<<setter
let value = await contract_at_address.getValue(); //<<==value shall be 10
console.log("value contract at address : ", value)
But it throws error:
[Sun Nov 15 2020 23:10:11.457] WARN Possible Unhandled Promise Rejection (id: 0):
Error: call revert exception (method="getValue()", errorSignature=null, errorArgs=[null], reason=null, code=CALL_EXCEPTION, version=abi/5.0.5)
makeError@http://10.0.2.2:8081/index.bundle?platform=android&dev=true&minify=false:110810:32
throwError@http://10.0.2.2:8081/index.bundle?platform=android&dev=true&minify=false:110820:31
http://10.0.2.2:8081/index.bundle?platform=android&dev=true&minify=false:118492:68
step@http://10.0.2.2:8081/index.bundle?platform=android&dev=true&minify=false:118174:29
fulfilled@http://10.0.2.2:8081/index.bundle?platform=android&dev=true&minify=false:118058:34
tryCallOne@http://10.0.2.2:8081/index.bundle?platform=android&dev=true&minify=false:26991:16
http://10.0.2.2:8081/index.bundle?platform=android&dev=true&minify=false:27092:27
_callTimer@http://10.0.2.2:8081/index.bundle?platform=android&dev=true&minify=false:30531:17
_callImmediatesPass@http://10.0.2.2:8081/index.bundle?platform=android&dev=true&minify=false:30570:17
callImmediates@http://10.0.2.2:8081/index.bundle?platform=android&dev=true&minify=false:30787:33
__callImmediates@http://10.0.2.2:8081/index.bundle?platform=android&dev=true&minify=false:2736:35
http://10.0.2.2:8081/index.bundle?platform=android&dev=true&minify=false:2522:34
__guard@http://10.0.2.2:8081/index.bundle?platform=android&dev=true&minify=false:2719:15
flushedQueue@http://10.0.2.2:8081/index.bundle?platform=android&dev=true&minify=false:2521:21
flushedQueue@[native code]
invokeCallbackAndReturnFlushedQueue@[native code]
Here is the Hello.sol:
pragma solidity 0.7.0;
contract Hello {
address owner;
uint256 value;
event initContract(address _owner);
constructor() {
owner = msg.sender;
emit initContract(owner);
}
function setValue(uint256 _value) public {
value = _value;
}
function getValue() view public returns (uint256) {
return value;
}
}
I am following the doc for contract. The same functions are tested in truffle/ganache and they all work fine. What's missing here to call setValue (change state) and getValue (view only) functions in ethersjs?
Is this using Ganache? Are you making sure the contract is deployed before calling these? Can you do a console.log(await provider.getCode(address));?
If you use a ContractFactory to deploy the contract, you can use await contract.deployTransaction.wait() to wait until the contract has finished deploying (the deploy transaction has been mined).
Let me know if that helps...
Yes, it is ganache 2.5.4. Here is the code of deployment which runs before the code of calling function:
//deploy
const factory = new ContractFactory(abi, bytecode, signer);
console.log("factory : ", factory);
const contract = await factory.deploy();
console.log("contract deployed : ", contract);
console.log("contract address : ", contract.address);
// The transaction that the signer sent to deploy
contract.deployTransaction;
// Wait until the transaction is mined
contract.deployTransaction.wait();
// Now the contract is safe to interact with
console.log(await provider.getCode(contract.address)); returns [Mon Nov 16 2020 19:35:04.373] LOG 0x
Yeah, your contract isn't deployed yet. You are missing an await here:
// Wait until the transaction is mined (note the await)
await contract.deployTransaction.wait();
Let me know if that helps. :)
Sure await works. I copied the code from an example online. Now the deploy went through. Here is the console.log(await provider.getCode(contract.address)):
[Mon Nov 16 2020 19:41:39.843] LOG 0x6080604052348015600f57600080fd5b506004361060325760003560e01c80632096525514603757806355241077146053575b600080fd5b603d607e565b6040518082815260200191505060405180910390f35b607c60048036036020811015606757600080fd5b81019080803590602001909291905050506088565b005b6000600154905090565b806001819055505056fea2646970667358221220e77caf623174d55e2deac837c30807b692b27511779573d093d655a323bfdbe564736f6c63430007000033
The getValue returns [Mon Nov 16 2020 19:43:23.167] LOG value contract at address : {"hex": "0x00", "type": "BigNumber"}. It shall return 10 instead.
Oh, you need to wait for the transaction to setValue be mined too:
// This is the transaction use to set the value; it has not been mined yet, just accepted by the network
const setTx = await contract.setValue(10);
// This actually waits for the transaction to complete, it has been mined after this await
const setReceipt = await setTx.wait();
// Now your get should work
console.log(await contract.getValue());
Give that a go... :)
bingo! The Fastest response I can ever image! Many thanks.
[Mon Nov 16 2020 19:56:37.797] LOG value contract at address : {"hex": "0x0a", "type": "BigNumber"}
Just suggestion. More example code in doc will help readers to have better understand of the API
Yeah... More doc examples is on my backlog. :)
I'm going to start running polls on Twitter to let people vote on where they'd like my time spent. :)
In real network, it would be take minutes to have the block mined and a user can't wait for such long. Shall the await (for mined) be removed from production release? Is it possible that a transaction was accepted by the network but never gets mined?
You are correct that for most users, the UX waiting for tx to be confirmed is annoying. You can have a look at Uniswap's UX how they handle this case. They push the transaction hash to a global array or transaction hashes. There user can click on a dropdown to see confirmation status and can do some more hacks with the transaction (like increasing gas price or so). This allows user to do other tasks in the dapp while tx is being confirmed in the background.
Exactly what @zemse mentioned.
Also, in a real application, you won鈥檛 write a value to a contract and immediately read it. You would set up event listeners and let the events from you or other users trigger that.
You only need to await here because you are attempting consistent writes and reads.
A full application will have more complex interactions, but will be largely asynchronous. :)
Some users may be satisfied by submitting the transaction and take mining for granted, as long as the submission will for sure triggers mining process. await for mining to complete may be used only as user requests.
Most helpful comment
Exactly what @zemse mentioned.
Also, in a real application, you won鈥檛 write a value to a contract and immediately read it. You would set up event listeners and let the events from you or other users trigger that.
You only need to await here because you are attempting consistent writes and reads.
A full application will have more complex interactions, but will be largely asynchronous. :)