1

So, I am building an Iot device integrated with blockchain on a raspberry pi. I have created a smart contract and deployed it on sepolia testnet. And now for testing, I am writing a nodejs to send a function on the smart contract by passing parameters, and then call a function from the smart contract to display the sent values.

Smart contract -

// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity ^0.8.7;
 
import "@openzeppelin/contracts/access/Ownable.sol";
 
// Represents any device connected to the Raspberry Pi that you want to control
struct Appliance {
    uint256 id;
    string name;
    bool status;
    bytes1 pin;
}
 
contract HomeAutomationDemo is Ownable {
    uint256 nextId = 1;
    uint256 count = 0;
    mapping(uint256 => Appliance) public appliances;
 
    // Add a new appliance to the list
    function addAppliance(string memory name, bytes1 pin) public onlyOwner {
        appliances[nextId] = Appliance(nextId, name, false, pin);
        nextId++;
        count++;
    }
 
    // Remove an appliance from the list
    function removeAppliance(uint256 id) public onlyOwner {
        delete appliances[id];
        count--;
    }
 
    // This is the function that will be called when the user 
    // toggles the status of the appliance on the mobile app
    function toggleAppliance(uint256 id, bool status) public onlyOwner {
        appliances[id].status = !appliances[id].status;
    }
 
    // Update the details of an appliance
    function updateAppliance(uint256 id, string memory name, bytes1 pin) public onlyOwner {
        appliances[id].name = name;
        appliances[id].pin = pin;
    }
 
    // Get the details of an appliance
    function getAppliance(uint256 id) public view returns (Appliance memory) {
        return appliances[id];
    }
 
    // Get the list of appliances
    function getAppliances() public view returns (Appliance[] memory)  {
        Appliance[] memory result = new Appliance[](count);
        for (uint256 i = 0; i < nextId; i++) {
            if (appliances[i].id != 0) {
                result[i] = appliances[i];
            }
        }
        return result;
    }
 
    // Get the total number of appliances
    function getAppliancesCount() public view returns (uint256) {
        return count;
    }
 
}

Nodejs -

const web3 = require('web3');
const fs = require("fs");

const CONTRACT_ABI = `[
        {
                "anonymous": false,
                "inputs": [
                        {
                                "indexed": true,
                                "internalType": "address",
                                "name": "previousOwner",
                                "type": "address"
                        },
                        {
                                "indexed": true,
                                "internalType": "address",
                                "name": "newOwner",
                                "type": "address"
                        }
                ],
                "name": "OwnershipTransferred",
                "type": "event"
        },
        {
                "inputs": [
                        {
                                "internalType": "string",
                                "name": "name",
                                "type": "string"
                        },
                        {
                                "internalType": "bytes1",
                                "name": "pin",
                                "type": "bytes1"
                        }
                ],
                "name": "addAppliance",
                "outputs": [],
                "stateMutability": "nonpayable",
                "type": "function"
        },
        {
                "inputs": [
                        {
                                "internalType": "uint256",
                                "name": "",
                                "type": "uint256"
                        }
                ],
                "name": "appliances",
                "outputs": [
                        {
                                "internalType": "uint256",
                                "name": "id",
                                "type": "uint256"
                        },
                        {
                                "internalType": "string",
                                "name": "name",
                                "type": "string"
                        },
                        {
                                "internalType": "bool",
                                "name": "status",
                                "type": "bool"
                        },
                        {
                                "internalType": "bytes1",
                                "name": "pin",
                                "type": "bytes1"
                        }
                ],
                "stateMutability": "view",
                "type": "function"
        },
        {
                "inputs": [
                        {
                                "internalType": "uint256",
                                "name": "id",
                                "type": "uint256"
                        }
                ],
                "name": "getAppliance",
                "outputs": [
                        {
                                "components": [
                                        {
                                                "internalType": "uint256",
                                                "name": "id",
                                                "type": "uint256"
                                        },
                                        {
                                                "internalType": "string",
                                                "name": "name",
                                                "type": "string"
                                        },
                                        {
                                                "internalType": "bool",
                                                "name": "status",
                                                "type": "bool"
                                        },
                                        {
                                                "internalType": "bytes1",
                                                "name": "pin",
                                                "type": "bytes1"
                                        }
                                ],
                                "internalType": "struct Appliance",
                                "name": "",
                                "type": "tuple"
                        }
                ],
                "stateMutability": "view",
                "type": "function"
        },
        {
                "inputs": [],
                "name": "getAppliances",
                "outputs": [
                        {
                                "components": [
                                        {
                                                "internalType": "uint256",
                                                "name": "id",
                                                "type": "uint256"
                                        },
                                        {
                                                "internalType": "string",
                                                "name": "name",
                                                "type": "string"
                                        },
                                        {
                                                "internalType": "bool",
                                                "name": "status",
                                                "type": "bool"
                                        },
                                        {
                                                "internalType": "bytes1",
                                                "name": "pin",
                                                "type": "bytes1"
                                        }
                                ],
                                "internalType": "struct Appliance[]",
                                "name": "",
                                "type": "tuple[]"
                        }
                ],
                "stateMutability": "view",
                "type": "function"
        },
        {
                "inputs": [],
                "name": "getAppliancesCount",
                "outputs": [
                        {
                                "internalType": "uint256",
                                "name": "",
                                "type": "uint256"
                        }
                ],
                "stateMutability": "view",
                "type": "function"
        },
        {
                "inputs": [],
                "name": "owner",
                "outputs": [
                        {
                                "internalType": "address",
                                "name": "",
                                "type": "address"
                        }
                ],
                "stateMutability": "view",
                "type": "function"
        },
        {
                "inputs": [
                        {
                                "internalType": "uint256",
                                "name": "id",
                                "type": "uint256"
                        }
                ],
                "name": "removeAppliance",
                "outputs": [],
                "stateMutability": "nonpayable",
                "type": "function"
        },
        {
                "inputs": [],
                "name": "renounceOwnership",
                "outputs": [],
                "stateMutability": "nonpayable",
                "type": "function"
        },
        {
                "inputs": [
                        {
                                "internalType": "uint256",
                                "name": "id",
                                "type": "uint256"
                        },
                        {
                                "internalType": "bool",
                                "name": "status",
                                "type": "bool"
                        }
                ],
                "name": "toggleAppliance",
                "outputs": [],
                "stateMutability": "nonpayable",
                "type": "function"
        },
        {
                "inputs": [
                        {
                                "internalType": "address",
                                "name": "newOwner",
                                "type": "address"
                        }
                ],
                "name": "transferOwnership",
                "outputs": [],
                "stateMutability": "nonpayable",
                "type": "function"
        },
        {
                "inputs": [
                        {
                                "internalType": "uint256",
                                "name": "id",
                                "type": "uint256"
                        },
                        {
                                "internalType": "string",
                                "name": "name",
                                "type": "string"
                        },
                        {
                                "internalType": "bytes1",
                                "name": "pin",
                                "type": "bytes1"
                        }
                ],
                "name": "updateAppliance",
                "outputs": [],
                "stateMutability": "nonpayable",
                "type": "function"
        }
]`;
const CONTRACT_ADDRESS = "0x13d9FA79D364070510B410c2FaC1976F21E3e218";

const web3js = new web3(new web3.providers.HttpProvider("https://sepolia.infura.io/v3/9e644c118b7c44068674e2d12a776536"));

var myAddress = '0x46Be881Fa6935a8FC969A4ddDFC74d625c558996';
var privateKey = Buffer.from('PRIVATE-KEY', 'hex')
var contractABI =JSON.parse(CONTRACT_ABI);
var contractAddress = CONTRACT_ADDRESS;


contract = new web3js.eth.Contract(contractABI,contractAddress);



// first populate an appliance
let setAppliance = contract.methods.addAppliance("fan", 1).send;

console.log(setAppliance);

let applianceOnOff = contract.methods.getAppliances;
console.log(applianceOnOff);

And this is what I get when running the nodejs -

[Function: bound _executeMethod] {
  request: [Function: bound _executeMethod]
}
[Function: bound _createTxObject]
  • When sharing code snippets online, always remember to remove any secret credentials in your code. I can see something like a private key in your code snippets. If that is a real wallet private key, DO NOT USE IT TO STORE FUNDS ANYMORE, it's compromised. – Ahmed Mohammed Oct 09 '22 at 10:32
  • @AhmedMohammed Thanks for pointing out. Its an ethereum test wallet, so I thought it wouldn't be a problem. – Martin Smith Oct 09 '22 at 12:08

1 Answers1

1

This code:

contract.methods.addAppliance("fan", 1).send;

returns a Promise in which you have to wait for its completion by using the await keyword:

await contract.methods.addAppliance("fan", web3.utils.numberToHex(1)).send({from: myAddress});

/* 
* web3.utils.numberToHex(1) converts decimal digit '1' to bytes '0x1'
* because the addAppliance function takes a 'byte1' as second parameter 
*/

For this line: contract.methods.getAppliances;, you are making a call to get a value from your contract which doesn't modify state so you have to append .call() to get that value, .send() is used when making calls tha triggers smart contract state changes.

You can then use the await keyword to wait for its completion or use a callback:

await contract.methods.getAppliances().call();
Ahmed Mohammed
  • 407
  • 5
  • 7
  • I used a callback like you suggested and I get the following error: /home/pi/node_modules/web3-eth-abi/lib/index.js:245 if (param.substring(0, 2) === '0x') { ^ TypeError: param.substring is not a function – Martin Smith Oct 09 '22 at 11:52
  • Can you use `await` instead? – Ahmed Mohammed Oct 10 '22 at 07:55
  • Tried it and but it didn't return anything. async function setAppliance(error, result) { let setAppliance = await contract.methods.addAppliance("fan", 1).send().then(function(error, result){}); console.log(setAppliance); } async function applianceOnOff(error, result) { let applianceOnOff = await contract.methods.getAppliances().call().then(function(error,result){}); console.log(applianceOnOff); } – Martin Smith Oct 10 '22 at 08:12
  • I just made an edit to the answer, adding the `await` keyword that way instead of a callback should suffice. – Ahmed Mohammed Oct 10 '22 at 13:07
  • Changed the code like you suggested and it still doesn't return anything. async function setAppliance(error, result) { let setAppliance = await contract.methods.addAppliance("fan", 1).send(); console.log(setAppliance); } async function applianceOnOff(error, result) { let applianceOnOff = await contract.methods.getAppliances().call(); console.log(applianceOnOff); } – Martin Smith Oct 10 '22 at 13:21
  • I think the calling function is working now with this code. `async function main () { let applianceOnOff = await contract.methods.getAppliances().call(); console.log(applianceOnOff); } main() .then(() => process.exit(0)) .catch(error => { console.error(error); process.exit(1); }); ` I got an array returned [],.. However, for sending parameters, its showing the param.substring error.. Can you suggest me a fix for this? – Martin Smith Oct 10 '22 at 14:12
  • I found that you need to add a `from` to the `send()`, I'll now edit the answer again. – Ahmed Mohammed Oct 10 '22 at 14:17
  • I added the edits. The `from` address is the address that is signing the transaction, also your contract take a byte parameter instead of a normal number for the `pin`, hence, the line `web3.utils.numberToHex(1)` – Ahmed Mohammed Oct 10 '22 at 14:30
  • Worked, but to an another error :( `Error: Returned error: The method eth_sendTransaction does not exist/is not avalable` I am assuming this is probably a problem caused by infura? – Martin Smith Oct 10 '22 at 14:34
  • The signer is not actually initialised properly. Add this line to get a signer from your private key then pass it to the `from` parameter in the `send` function: `const account = await web3.eth.accounts.privateKeyToAccount(privateKey [, ignoreLength ]);` – Ahmed Mohammed Oct 10 '22 at 14:41
  • ANother TypeError `TypeError: Cannot read property 'accounts' of undefined at main (/home/pi/sepolia/test2.js:272:32)` Code - `const account = await web3.eth.accounts.privateKeyToAccount('//private-key//'); let setAppliance = await contract.methods.addAppliance("fan", web3.utils.numberToHex(1)).send({from: account}); ` – Martin Smith Oct 10 '22 at 15:06