I am trying to write a smart contract and call the functions in it using JavaScript.
The relevant bits of the solidity code are:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
import "@openzeppelin/contracts/utils/Strings.sol";
contract Test {
struct CRPs {
uint256 challenge;
uint256 response;
}
mapping(address => CRPs) devChals;
uint256 nonce;
uint256 oldNonce;
function nonceGen(uint256 randVal) private view returns (uint256) {
return (uint256(keccak256(abi.encodePacked(block.difficulty, block.timestamp, randVal))) % 281474976710654) + 1; // limited to 48 bits
}
function registerDevice(address addr, uint256 _challenge, uint256 _response) public returns(uint256, uint256) {
if (devChals[addr].challenge == 0) {
devChals[addr] = CRPs(_challenge, _response);
// return true;
}
// return false;
return (devChals[addr].challenge, devChals[addr].response);
}
function firstStage(address addr) public returns(uint256, uint256, string memory) {
uint256 challenge = devChals[addr].challenge;
uint256 response = devChals[addr].response;
uint256 argVal = uint256(keccak256(abi.encodePacked(Strings.toString(challenge))));
nonce = nonceGen(argVal);
uint256 Mnode = response ^ nonce;
bytes32 Hnode = keccak256(abi.encode(Strings.toString(challenge), Strings.toString(response), Strings.toString(nonce)));
nonce = nonce+1;
oldNonce = nonce;
return (challenge, Mnode, toHex(Hnode));
}
}
The relevant bits of the JS code are:
const {ethers} = require('ethers');
const provider = new ethers.providers.JsonRpcProvider(`HTTP://127.0.0.1:7545`);
const signer = new ethers.Wallet("<wallet private key>", provider);
const address = '<hex address>'; // smart contract address
const ERC20_ABI =
[
"function registerDevice(address, uint256, uint256) public returns(uint256, uint256)",
"function firstStage(address) public returns(uint256, uint256, string)"
]
const contract = new ethers.Contract(address, ERC20_ABI, provider);
const contractWithSigner = contract.connect(signer);
const main = async() => {
var challenge = 103697318762117;
var response = 83968343435925;
const regDev = await contractWithSigner.callStatic.registerDevice(signer.getAddress(), challenge, response);
const {0: challengeReg, 1: responseReg} = regDev;
console.log(`${challengeReg} ${responseReg}\n\n`);
const firstStage = await contractWithSigner.callStatic.firstStage(signer.getAddress());
const {0: challengeFirstStage, 1: MnodeFirstStage, 2: HnodeFirstStage} = firstStage;
console.log(firstStage, challengeFirstStage, MnodeFirstStage.toNumber(), HnodeFirstStage);
}
main();
I am using Ganache to test the smart contract locally.
When calling firstStage()
using remix, I am getting the correct value of challenge. When doing the same using ethers js, the value of challenge is 0, although the other two values are correctly returned.
The values are returned correctly in registerDevice()
.
I initially was using msg.sender
as the key for the mapping, and I tried sending the address of the wallet as an argument, but the result in both the cases is the same.
Any help with be greatly appreciated.
const firstStage = await contractWithSigner.callStatic.firstStage(signer.getAddress());
callStatic does not store any state changes. You'll need to send a regular transaction to store the state changes in your contract.
However, a function invoked by transaction does not return any values outside of EVM. So if you want the values to be available outside of EVM, you can duplicate them in an event log. Event logs are available in the transaction receipt (not in the "unprocessed" transaction object), that's why the following snippet uses the transaction.wait()
method.
contract Test {
event FirstStage(uint256, uint256, string memory);
function firstStage(address addr) public returns(uint256, uint256, string memory) {
// ...
emit FirstStage(challenge, Mnode, toHex(Hnode));
return (challenge, Mnode, toHex(Hnode));
}
}
const transaction = await contractWithSigner.firstStage(signer.getAddress());
const txReceipt = await transaction.wait();
const eventLogs = txReceipt.events;