I am studying a course on Udemy to learn more about blockchain technologies and smart contracts. This course is based on older versions of solidity, truffle, geth, node.js etc. so most of the code written is outdated. I don't have a background in Javascript so I'm learning as I go.
So far I have written and ran this code line by line with no issues:
Web3 = require('web3')
web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:7545"))
accounts = web3.eth.getAccounts()
solc = require('solc')
sourceCode = fs.readFileSync('Greetings.sol').toString()
input = {language: 'Solidity', sources: {['Greetings.sol']: {content: sourceCode}},settings:{outputSelection:{'*':{'*':['*']}}}}
output = JSON.parse(solc.compile(JSON.stringify(input)))
contractABI = output.contracts['Greetings.sol']['Greetings'].abi
byteCode = output.contracts['Greetings.sol']['Greetings'].evm['bytecode'].object
account0 = (await web3.eth.getAccounts())[0]
greetingsContract = new web3.eth.Contract([contractABI])
greetingsDeployed = greetingsContract.deploy({data: '0x' + byteCode}).send({from: account0, gas: 6721975, gasPrice: 1})
I know exactly what almost each line does but I'm at a loss as to how I will interact with my functions I've written in my 'Greetings.sol' file. The contracts appear in Ganache as contract creations with the byteCode I'm sending. In the course he mentions to use greetingsInstance = greetingsContract.at(greetingsDeployed.address)
but that is outdated. So finally, how do I create a smart contract instance and interact with my functions?
Below I have posted my greetingsContract
, greetingsDeployed
and contractABI
variables for more info.
greetingsContract:
Contract {
setProvider: [Function (anonymous)],
currentProvider: [Getter/Setter],
_requestManager: RequestManager {
provider: HttpProvider {
withCredentials: false,
timeout: 0,
headers: undefined,
agent: undefined,
connected: true,
host: 'http://localhost:7545',
httpAgent: [Agent]
},
providers: {
WebsocketProvider: [Function: WebsocketProvider],
HttpProvider: [Function: HttpProvider],
IpcProvider: [Function: IpcProvider]
},
subscriptions: Map(0) {}
},
givenProvider: null,
providers: {
WebsocketProvider: [Function: WebsocketProvider],
HttpProvider: [Function: HttpProvider],
IpcProvider: [Function: IpcProvider]
},
_provider: HttpProvider {
withCredentials: false,
timeout: 0,
headers: undefined,
agent: undefined,
connected: true,
host: 'http://localhost:7545',
httpAgent: Agent {
_events: [Object: null prototype],
_eventsCount: 2,
_maxListeners: undefined,
defaultPort: 80,
protocol: 'http:',
options: [Object: null prototype],
requests: [Object: null prototype] {},
sockets: [Object: null prototype] {},
freeSockets: [Object: null prototype] {},
keepAliveMsecs: 1000,
keepAlive: true,
maxSockets: Infinity,
maxFreeSockets: 256,
scheduling: 'lifo',
maxTotalSockets: Infinity,
totalSocketCount: 0,
[Symbol(kCapture)]: false
}
},
setRequestManager: [Function (anonymous)],
BatchRequest: [Function: bound Batch],
extend: [Function: ex] {
formatters: {
inputDefaultBlockNumberFormatter: [Function: inputDefaultBlockNumberFormatter],
inputBlockNumberFormatter: [Function: inputBlockNumberFormatter],
inputCallFormatter: [Function: inputCallFormatter],
inputTransactionFormatter: [Function: inputTransactionFormatter],
inputAddressFormatter: [Function: inputAddressFormatter],
inputPostFormatter: [Function: inputPostFormatter],
inputLogFormatter: [Function: inputLogFormatter],
inputSignFormatter: [Function: inputSignFormatter],
inputStorageKeysFormatter: [Function: inputStorageKeysFormatter],
outputProofFormatter: [Function: outputProofFormatter],
outputBigNumberFormatter: [Function: outputBigNumberFormatter],
outputTransactionFormatter: [Function: outputTransactionFormatter],
outputTransactionReceiptFormatter: [Function: outputTransactionReceiptFormatter],
outputBlockFormatter: [Function: outputBlockFormatter],
outputLogFormatter: [Function: outputLogFormatter],
outputPostFormatter: [Function: outputPostFormatter],
outputSyncingFormatter: [Function: outputSyncingFormatter]
},
utils: {
_fireError: [Function: _fireError],
_jsonInterfaceMethodToString: [Function: _jsonInterfaceMethodToString],
_flattenTypes: [Function: _flattenTypes],
randomHex: [Function: randomHex],
BN: [Function: BNwrapped],
isBN: [Function: isBN],
isBigNumber: [Function: isBigNumber],
isHex: [Function: isHex],
isHexStrict: [Function: isHexStrict],
sha3: [Function],
sha3Raw: [Function: sha3Raw],
keccak256: [Function],
soliditySha3: [Function: soliditySha3],
soliditySha3Raw: [Function: soliditySha3Raw],
encodePacked: [Function: encodePacked],
isAddress: [Function: isAddress],
checkAddressChecksum: [Function: checkAddressChecksum],
toChecksumAddress: [Function: toChecksumAddress],
toHex: [Function: toHex],
toBN: [Function: toBN],
bytesToHex: [Function: bytesToHex],
hexToBytes: [Function: hexToBytes],
hexToNumberString: [Function: hexToNumberString],
hexToNumber: [Function: hexToNumber],
toDecimal: [Function: hexToNumber],
numberToHex: [Function: numberToHex],
fromDecimal: [Function: numberToHex],
hexToUtf8: [Function: hexToUtf8],
hexToString: [Function: hexToUtf8],
toUtf8: [Function: hexToUtf8],
stripHexPrefix: [Function: stripHexPrefix],
utf8ToHex: [Function: utf8ToHex],
stringToHex: [Function: utf8ToHex],
fromUtf8: [Function: utf8ToHex],
hexToAscii: [Function: hexToAscii],
toAscii: [Function: hexToAscii],
asciiToHex: [Function: asciiToHex],
fromAscii: [Function: asciiToHex],
unitMap: [Object],
toWei: [Function: toWei],
fromWei: [Function: fromWei],
padLeft: [Function: leftPad],
leftPad: [Function: leftPad],
padRight: [Function: rightPad],
rightPad: [Function: rightPad],
toTwosComplement: [Function: toTwosComplement],
isBloom: [Function: isBloom],
isUserEthereumAddressInBloom: [Function: isUserEthereumAddressInBloom],
isContractAddressInBloom: [Function: isContractAddressInBloom],
isTopic: [Function: isTopic],
isTopicInBloom: [Function: isTopicInBloom],
isInBloom: [Function: isInBloom],
compareBlockNumbers: [Function: compareBlockNumbers],
toNumber: [Function: toNumber]
},
Method: [Function: Method]
},
clearSubscriptions: [Function (anonymous)],
options: {
address: [Getter/Setter],
jsonInterface: [Getter/Setter],
data: undefined,
from: undefined,
gasPrice: undefined,
gas: undefined
},
handleRevert: [Getter/Setter],
defaultCommon: [Getter/Setter],
defaultHardfork: [Getter/Setter],
defaultChain: [Getter/Setter],
transactionPollingTimeout: [Getter/Setter],
transactionPollingInterval: [Getter/Setter],
transactionConfirmationBlocks: [Getter/Setter],
transactionBlockTimeout: [Getter/Setter],
blockHeaderTimeout: [Getter/Setter],
defaultAccount: [Getter/Setter],
defaultBlock: [Getter/Setter],
methods: {},
events: { allEvents: [Function: bound ] },
_address: null,
_jsonInterface: [
[
[Object],
[Object],
[Object],
constant: undefined,
payable: undefined
]
]
}
greetingsDeployed:
Promise {
Contract {
setProvider: [Function (anonymous)],
currentProvider: [Getter/Setter],
_requestManager: RequestManager {
provider: [HttpProvider],
providers: [Object],
subscriptions: Map(0) {}
},
givenProvider: null,
providers: {
WebsocketProvider: [Function: WebsocketProvider],
HttpProvider: [Function: HttpProvider],
IpcProvider: [Function: IpcProvider]
},
_provider: HttpProvider {
withCredentials: false,
timeout: 0,
headers: undefined,
agent: undefined,
connected: true,
host: 'http://localhost:7545',
httpAgent: [Agent]
},
setRequestManager: [Function (anonymous)],
BatchRequest: [Function: bound Batch],
extend: [Function: ex] {
formatters: [Object],
utils: [Object],
Method: [Function: Method]
},
clearSubscriptions: [Function (anonymous)],
options: {
address: [Getter/Setter],
jsonInterface: [Getter/Setter],
data: undefined,
from: undefined,
gasPrice: undefined,
gas: undefined
},
handleRevert: [Getter/Setter],
defaultCommon: [Getter/Setter],
defaultHardfork: [Getter/Setter],
defaultChain: [Getter/Setter],
transactionPollingTimeout: [Getter/Setter],
transactionPollingInterval: [Getter/Setter],
transactionConfirmationBlocks: [Getter/Setter],
transactionBlockTimeout: [Getter/Setter],
blockHeaderTimeout: [Getter/Setter],
defaultAccount: [Getter/Setter],
defaultBlock: [Getter/Setter],
methods: {},
events: { allEvents: [Function: bound ] },
_address: '0xD41D42E01d6ceDa1A96028Ae8bc89b45723DBD8A',
_jsonInterface: [ [Array] ]
},
_events: Events <[Object: null prototype] {}> {},
emit: [Function: emit],
on: [Function: on],
once: [Function: once],
off: [Function: removeListener],
listeners: [Function: listeners],
addListener: [Function: on],
removeListener: [Function: removeListener],
removeAllListeners: [Function: removeAllListeners],
_eventsCount: 0,
[Symbol(async_id_symbol)]: 17341,
[Symbol(trigger_async_id_symbol)]: 5,
[Symbol(destroyed)]: { destroyed: false }
}
contractABI:
[
{
inputs: [],
stateMutability: 'nonpayable',
type: 'constructor',
constant: undefined,
payable: undefined
},
{
inputs: [],
name: 'getGreetings',
outputs: [ [Object] ],
stateMutability: 'view',
type: 'function',
constant: true,
payable: undefined,
signature: '0xca4c3a41'
},
{
inputs: [ [Object] ],
name: 'setGreetings',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
constant: undefined,
payable: undefined,
signature: '0x49da5de4'
},
constant: undefined,
payable: undefined
]
Greetings.sol/Solidity code:
//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.15;
contract Greetings {
string message;
constructor(){
message = "I'm ready";
}
function setGreetings(string memory _message) public {
message = _message;
}
function getGreetings() public view returns (string memory){
return message;
}
}
These both tutorials were great resources for solving your problem!
Creating & Deploying a Smart Contract using Web3js & Ganache-CLI — Part 1
Interacting with a Smart Contract through Web3.js (Tutorial)
Also PFB reference code which works. Mistakes are:
ABI should not be inside an array.
const notorizedContract = new web3.eth.Contract(contractABI);
await is not working in the global scope for me, so I have gone with then
to resolve the response, could be due to node version, but this worked for me!
solidity part (NotarizeDocument.sol)
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.15;
contract NotarizedDocument {
mapping (bytes32 => bool) documentProof;
function notarize(string memory document) public {
bytes32 signedDocument = signDoc(document);
storeDocumentProof(signedDocument);
}
function signDoc(string memory document) private pure returns (bytes32) {
return sha256(abi.encodePacked(document));
}
function storeDocumentProof(bytes32 signedDocument) private {
documentProof[signedDocument] = true;
}
function checkDocument (string memory document) public view returns (bool) {
bytes32 signedDocument = signDoc(document);
return hasProof(signedDocument);
}
function hasProof(bytes32 signedDocument) private view returns (bool) {
return documentProof[signedDocument];
}
}
javascript part
var Web3 = require("web3");
var fs = require("fs");
var web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
var accounts = web3.eth.getAccounts();
var solc = require("solc");
var sourceCode = fs
.readFileSync(`${__dirname}/contracts/NotarizeDocument.sol`)
.toString();
var input = {
language: "Solidity",
sources: { ["NotarizeDocument.sol"]: { content: sourceCode } },
settings: { outputSelection: { "*": { "*": ["*"] } } },
};
var output = JSON.parse(solc.compile(JSON.stringify(input)));
var contractABI =
output.contracts["NotarizeDocument.sol"].NotarizedDocument.abi;
var byteCode =
output.contracts["NotarizeDocument.sol"].NotarizedDocument.evm["bytecode"]
.object;
const notorizedContract = new web3.eth.Contract(contractABI);
web3.eth.getAccounts().then((accounts) => {
var account0 = accounts[0];
notorizedContract
.deploy({ data: "0x" + byteCode })
.send({ from: account0, gas: 6721975, gasPrice: 1 })
.then(async (newContractInstance) => {
notorizedContract.options.address = newContractInstance.options.address;
const response = await notorizedContract.methods
.notarize("Hello Romil!!")
.send({ from: "0x7B7DB1E2b197aDa733326381513f12639F5e0Ecb" });
// completed transaction!
console.log(response);
await notorizedContract.methods
.checkDocument("Hello Romil!!")
.call(console.log);
});
});
The second response returns true, which means the text exists!