Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save johnshearing/706340a4c68fa50f8709c91590a0d406 to your computer and use it in GitHub Desktop.
Save johnshearing/706340a4c68fa50f8709c91590a0d406 to your computer and use it in GitHub Desktop.

Everything about working with the Ethereum blockchain is found here at DecypherTV videos

These are notes for the seventh video.

This video is an introduction to working with smart contracts from the NodeJS console.

The first part of these notes shows how to deploy a smart contract through a geth node running locally.
Geth unlocks an account in order to accomplish this.
These notes follow Jordan's video pretty closely except that a local blockchain is used instead of TestRPC.

The second part of these notes shows the use of Jordan's decypher command line utility to deploy a contract to the Public Ethereum Block chain using INFURA. Using this method it is necessary to provide the private key when logging into the decypher utility but the key is only used to sign transaction instructions before sending.
The operations show are:

  • Deploying a smart contract with a constructor
  • Reading a public variable from the smart contract
  • Changing the public variable
  • Reading the variable again to see that it has been changed.

Finally, the third part of these notes will examine the code in the Decypher utility with node-inspector to see how to deploy a contract with a constructor and then change its state by sending a secure signed transactions to BlockCypher or INFURA and also to a local node on a test blockchain.

Install Node on your computer

Go to nodejs.org and install the latest version of Node.
This will also install NPM.

Setup the Project Folder Environment

Create a new project folder.
Then at the OS command line, navigate your project directory.

Type the following at the OS command line and press the Enter button.
Then follow the prompts to create a package.json file.
npm init

Install the Solidity compiler package.
Type the following at the OS command line and press the Enter button.
npm install solc --save

Install Ethereum's JavaScript API.
Type the following at the OS command line and press the Enter button.
npm install web3 --save

Install Ethereum's utility library.
Type the following at the OS command line and press the Enter button.
npm install ethereumjs-util --save

Install Ethereum's transaction library
Type the following at the OS command line and press the Enter button.
npm install ethereumjs-tx --save

These will install the latest versions of the packages.
Only web3 and solc are necessary for this video but there is no harm in having all the packages we have been working with so far.

Assuming you have the OS command line console open in the project folder:
Type node and press the "Enter" button.
You are no longer interacting with your operating system but rather with the NodeJS JavaScript command line interpreter.

Make Node aware of the web3 library.
Type the following into the node console and press the Enter button.

var Web3 = require("web3");

Create an instance of the web3 library within Node and connect it to your running blockchain.
Paste the following into the Node console and press the Enter button.

var web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"))

Type web3 at the node command line and press the Enter button to see the API and confirm that Node can use the library.

Make Node aware of the solc library.
Type the following into the Node console and press the Enter button.

var solc = require("solc");

Create a new file in your project directory called HelloWorld.sol. I prefer to use the VS_Code IDE for this because it has code completion and linting for the solidity language but you can use any text editor you like. Next type or paste in the following code into the text editor and save the file.

contract HelloWorld 
{
   function displayMessage() constant returns (string) 
   {
      return "Hello from a smart contract"; 
   }
}

Back at the Node console type the following JavaScript but do not press enter yet,

var source = ``

At the end of the above line we have backticks not single quotes. These are cool because they allow evaluation of JavaScript variables in the string and they also evaluate line feeds and spaces. We are going to see backticks in action further along in this document.

Now between the backticks paste in the contract code.
Your Node command should look as shown below.

var source = 
`contract HelloWorld 
{
   function displayMessage() constant returns (string) 
   {
      return "Hello from a smart contract"; 
   }
}`

Now press the Enter button.

Let's look at the source variable.
At the Node console execute the following command.
source
The console output appears as follows:

'contract HelloWorld\n{\n    function displayMessage() constant returns (string) \n    {\n        return "Hello from a smart contract"; \n    }\n}'

It's the smart contract text with line feeds and spaces evaluated as opposed to rendered.

Now we will ask NodeJS to compile the Solidity code (our smart contract) using the solc compiler and save the object produced to a variable we will call compilerResult. We can watch Jordan do this at exactly 4 minutes into the video. He names the output variable compiled instead of compilerResult as we are doing below.
Execute the following command at the Node console:
var compilerResult = solc.compile(source)

Let's see what the compiler produced.
Execute the following command at the Node console:
compilerResult

Wow! That's a lot of information. We can see that compilerResult is an object that contains more objects within it. If you look, you can see that one of the objects inside compilerResult, the one named contracts, has only one object inside of it - the object is named: :HelloWorld and there are many objects inside of it. The things we need from the compiler are inside the object :HelloWorld. These are:

  • The interface (also called an ABI),
  • The opCodes, and
  • The byteCode.
    With these three things we will be able to send our smart contract to the blockchain and interact with it.

Oddly, we cannot access the interface, opCodes, and byteCode of the :HelloWorld object in the normal way. At 4 minutes and 24 seconds into the video we can see that Jordan is able to access the :HelloWorld object by executing the following command. compiled.contracts.HelloWorld
That doesn't work for me. I need to execute the following in order to get the same result: compilerResult.contracts[':HelloWorld']

At 4' 36" into the video: Jordan retrives the bytecode with the following command.
compiled.contracts.HelloWorld.bytecode
That doesn't work for me - I was required to execute the following command in order to get the same result.
compilerResult.contracts[':HelloWorld'].bytecode
The following will also work:
compilerResult["contracts"][":HelloWorld"]["bytecode"]
Single quotes or double quotes will work

I can see interface and opCodes in a similar manner as shown below.
compilerResult.contracts[':HelloWorld'].interface
compilerResult.contracts[':HelloWorld'].opcodes

I think the compiler does not inject those extra characters for Jordan because he is using an older version of the compiler. This difference provides us with an opportunity to see how to loop through the contracts object and get the interface, opCodes, and byteCode from any number of smart contracts inside. This will be especially helpful if we are compiling Solidity code that contains more than one smart contract.

It's easy to grab hold of the contracts object -
Type compilerResult.contracts at the Node console and press enter.
The contracts object and all the objects inside the contracts object are displayed.

Grabbing hold of the :HelloWorld object which is inside of the contracts object cannot be done the same way. This (I think) is because the Node console can't deal with the colon which is actually part of the name.
Try it. Paste compilerResult.contracts.':HelloWorld' into the Node console and press the Enter button.
The Node console outputs the following error message:
SyntaxError: Unexpected string
So we will need some special code to grab the :HelloWorld object if we hope to get at the interface, opCodes, and byteCode

Below is that special code
It's called the "for/in loop" or "for/in statement".
It's a JavaScript statement.
The for/in statement loops through the properties of an object without the need to call those properties by name.
Paste the following block of code into the Node console and press the Enter button.

for (var contractObject in compilerResult.contracts) 
{
    var helloWorldByteCode = compilerResult.contracts[contractObject].bytecode;
    var helloWorldOpCode = compilerResult.contracts[contractObject].opcodes; 
    var helloWorld_ABI = compilerResult.contracts[contractObject].interface;
    
    console.log(`This is the byteCode ${helloWorldByteCode}
    `);
    console.log(`This is the opCode ${helloWorldOpCode}
    `);    
    console.log(`This is the interface also known as the ABI ${helloWorld_ABI}
    `);          
}

As you can see, the console returned the bytecode, opcodes, and the interface. This works because one of the properties of the contracts object is the :HelloWorld object. The above command was able to find the :HelloWorld object, without referring to it by that difficult name, and then grabbed the bytecode, opcodes, and interface inside, saving each to its own variable. With just a bit of tweaking we could use this code to grab the interface, opcodes, and bytecode when the Solidity source code contains more than one smart contract. This is the real value of the above code.

Also, notice how the backtick was used in the console.log() functions to render the data inside our new variables.

We need to get the interface or ABI from the string representation we have now into a JavaScript data structure in order to be used in the next step.
Execute the following command at the Node console.
var abi_as_json = JSON.parse(helloWorld_ABI)

Now let's see what this looks like.
Execute the following command at the Node console.
abi_as_json

Next we need to instantiate a web3 contract object and pass in the abi as a JSON data structure.
Execute the following command at the Node console.
var web3ContractObject = web3.eth.contract(abi_as_json)

Have a look at the web3ContractObject
Execute the following command at the Node console.
web3ContractObject

Estimate the amount of gas required to deploy the contract.
Paste the contract code into the solidity online compiler.
This will estimate the gas required to send a transaction.
Look on the right at the section called Web3 deploy

Find out the current gas price
Execute the following command at the Node console.
var currentGasPrice = web3.eth.gasPrice

Look at the currentGasPrice
Execute the following command at the Node console and note the gas price. currentGasPrice
The currentGasPrice should be around 20000000000

You might be using TestRPC but I prefer to establish a local block chain as shown here
Assuming a local blockchain is established on your computer...
Start your local blockchain running using the following command in a new instance of the OS command line interpreter.
The --networkid parameter, the --identity parameter, and the --datadir parameter, will be different for you of course.
The lower --networkid numbers between 1-9 are used when you want to connect to various test networks or the main Ethereum blockchain.
So don't use single digit numbers for the --networkid parameter unless you are sure you want to connect to one of these.

./geth --networkid 1100 --identity JohnsComputer --nodiscover --nat none --datadir C:\Users\John\BlockChain\TestChainDataFiles --rpc --rpcapi "db,eth,net,web3" --rpccorsdomain "http://localhost:8545" console

Next open another new instance of the OS command line interpeter.
Navigate to your project directory using the cd command.
Then execute the following command to open geth's Javascript console.
./geth attach

Next at the geth's Javascript console start the miner using the following command.
miner.start(1)

Now unlock your account at geth's JavaScript console using the following command.
personal.unlockAccount(eth.coinbase, "myPassWord")

Next we will deploy the smart contract to the blockchain.
Execute the following command at the Node console.

var deployed = 
   web3ContractObject.new(
      {
         from: web3.eth.accounts[0],
         data: helloWorldByteCode,
         gas: 4700000,
         gasPrice: 20000000000  
      }, 
      (error) => 
         {
            if(error) { console.log(error) }
         }
   )

The following command is the same as above but is more verbose because it returns the contract if no error occurs.

var deployed = 
   web3ContractObject.new(
      {
         from: web3.eth.accounts[0],
         data: helloWorldByteCode,
         gas: 4700000,
         gasPrice: 20000000000  
      }, 
      (error, contract) => 
         {
            if(!error) { console.log(contract) }
            else { console.log(error) }
         }
   )

You can see the contract any time you want by executing the following at the Node console.
deployed
Notice that one of the items returned is the transaction hash.
This can be used as shown below to get information about the transaction.
web3.eth.getTransaction('0x0062f028ece9745c60ff7f9553d21cb89637213d13a0c97da539bb2fe877bcb2')

Wait a few seconds till a new block is mined and then check to see if the transaction went through. Execute the following command at the Node console. deployed.address If the console returns a public address then the contract was mined and is now part of the blockchain.

If we record the public address of the contract then we can work with it again at any time in the future by issuing the following commands. This is a good review.

var source = 
`contract HelloWorld 
{
   function displayMessage() constant returns (string) 
   {
      return "Hello from a smart contract"; 
   }
}`

var compilerResult = solc.compile(source)

for (var contractObject in compilerResult.contracts) 
{ 
    var helloWorld_ABI = compilerResult.contracts[contractObject].interface;        
}

var abi_as_json = JSON.parse(helloWorld_ABI)

var web3ContractObject = web3.eth.contract(abi_as_json)

web3ContractObject.at('0x7b741c___TheContractAddress___c1621861')
Remember that web3ContractObject is the variable name for an instantiation of the web3 contract object with the ABI passed into it. The significance is that in order to interact with a contract that has been deployed, one needs to have both the address and the ABI.

Now let's call the display message function in the smart contract we deployed.
This was the whole point of the exercise - to see if we could deploy a smart contract and then interact with it.
Execute the following command at the Node console.
deployed.displayMessage.call()
The Node console should output "Hello from a smart contract"

WooHoo!

A note on the keyword constant

Jordan points out at the end of the video that the constant keyword should be used if the function does not write changes to the blockchain. The advantage is faster processing because the result can be returned without querying the network.

Part 2 Deploying a contract using the Decypher command line utility.

The second part of these notes shows the use of Jordan's decypher command line utility to deploy a contract to the Public Ethereum Block chain using INFURA. Using this method it is necessary to provide the private key when logging into the decypher utility but the key is only used to sign transaction instructions before sending.
The operations show are:

  • Deploying a smart contract with a constructor
  • Reading a public variable from the smart contract
  • Changing the public variable
  • Reading the variable again to see that it has been changed.
Setting up the environment

Install the Decypher command line utility
At the OS console execute the following command.
npm install -g decyphertv

Sign up with INFURA and get an access token.

Deploying the contract

Start the Decypher command line utility.
At the OS console execute the following command.
decypher -m remote -e https://mainnet.infura.io/8KFs...MyInfuraAccessToken...ukQ

You will be prompted for a private key.
This will be used only to create a signed transaction. It is not sent out of your computer.
I used the following private key:
d9003665080effb22d48eba386bfc78e20311c9e91d584226e469d7d6e41e3d3
The following is the public address or public key generated from the private key above.
0x28c67d97663f41e04c4e804d818bdd7fa4692509
This account will be charged for the transaction costs.
Never give out your private key like I just did or anyone will be able to take your ether. Use the above account if you would like to practice deploying smart contracts but please leave some of the ether for other people to use.
Notice that there is no 0x preceding the string and there are no quotes surrounding it.

The console will output Starting Decypher and will give the NodeJS command prompt when it has loaded.

Create a variable to hold the smart contract source code. Paste the following code into the Decypher Command line utility and press the Enter button.

var source = `contract HelloWorld {
  string public message;

  function HelloWorld(string _message) {
    message = _message;
  }

  function displayMessage() constant returns (string) {
    return message; 
  }

  function updateMessage(string _message) {
  	message = _message;
  }
}`

I have found that the code will not compile unless all opening braces are on the same line as the function declarations.
Notice the above contract has a constructor.

Check out the source code.
At the Decypher console execute the following command.
source
You will see the source code with special characters indicating new lines.

Deploy the contract.
At the Decypher console execute the following command.
decypher.deployContract(source, ["Hello, World! from John's 1st contract"], {gas: 500000})
The second argument is the parameter for the constructor.
The third argument is optional.

The console output will be similar to the following:

> Deploying Contract HelloWorld | Transaction Hash: 0xc24565c556806773c8d665d1a1328efdeac3c4c7faea017c962d0045917f78cc  

You can search etherscan.io for the transaction hash to check the progress of that transaction and to get all kinds of useful information. You can include the 0x or not - it doesn't matter.

After the contract has been mined the console will return the address of the contract

Contract Deployed: Contract Address: 0xb16c44f0c4fb879be4cee51dbe53c58f76d23130, Block Number: 3449648, Gas Used: 412651

Search etherscan.io if you like to see information about the deployed contract.

The following command at the Decypher console provides useful information about the transaction.

web3.eth.getTransaction('0xc24565c556806773c8d665d1a1328efdeac3c4c7faea017c962d0045917f78cc')
Reading information from the contract.

The contract contains a function declared with the constant keyword. This guaranties that calling the function will not change the state of the contract (it will not overwrite any of the memory variables). The contract call method can be used to access such functions. Setting up the call is done as follows.

Compile the source code.
At the Decypher console execute the following command.
var compilerResult = solc.compile(source)

You can look at the compiled object if you want to.
At the Decypher console execute the following command.
compilerResult

Get the contract interface or ABI
At the Decypher console execute the following command.

for (var contractObject in compilerResult.contracts)
{
     var helloWorld_ABI = compilerResult.contracts[contractObject].interface;
}

Take a look and see that you have the ABI.
At the Decypher console execute the following command.
helloWorld_ABI

We need to get the interface or ABI from the string representation we have now into a JavaScript data structure in order to be used in the next step.
At the Decypher console execute the following command.
var abi_as_json = JSON.parse(helloWorld_ABI)

Take a look and see that you have the ABI in the form of a JavaScript data structure. abi_as_json

Next we need to instantiate a web3 contract object and pass in the abi as a JSON data structure.
Execute the following command at the Decypher console.
var web3ContractObject = web3.eth.contract(abi_as_json)

Have a look at the web3ContractObject
Execute the following command at the Decypher console.
web3ContractObject

Estimate the amount of gas required to deploy the contract.
Paste the contract code into the solidity online compiler.
This will estimate the gas required to send a transaction.
Look on the right at the section called Web3 deploy

Find out the current gas price
Execute the following command at the Decypher console.
var currentGasPrice = web3.eth.gasPrice

Look at the currentGasPrice
Execute the following command at the Decypher console and note the gas price. currentGasPrice
The currentGasPrice should be around 20000000000

Load a varible with the web3 contract object and load that with the state of the contract deployed on the blockchain.
Execute the following command at the Decypher console.
deployed = web3ContractObject.at('0xB16c44f0c4fB879bE4cEe51dbE53C58F76D23130')
Remember that there may be many contracts on the blockchain with the exact same source code. What differentiates them is the address.

Take and look at the deployed object if you care to
Execute the following command at the Decypher console.
deployed

Now let's call the contract and run the display Message function.
deployed.displayMessage.call()
The console should return the following:
'Hello, World! from John's 1st contract'

Change the state of the contract.

Execute the following command at the Decypher console.
decypher.callContract(deployed, "updateMessage", ["New Message!!"], {gas: 500000})
The console returns the following:
undefined
Calling Contract 0xB16c44f0c4fB879bE4cEe51dbE53C58F76D23130
Transaction Hash: 0x9e8bb2639ee5cb82355e00973e7e9ad78e83351cc57a9a9ce11e32f2790dd282

After the transaction has been mined the Decypher console will return the following:
Executed Contract Call: Block Number: 3455429, Gas Used: 19291

Check that the message public variable has been changed.
Execute the following command at the Decypher console.
deployed.displayMessage.call()
You should see the following output at the Decypher console: 'New Message!!'

Part 3: Examine the Decypher CL Utility using the VS Code IDE in debug mode

Install the VS Code IDE

Start an instance of your OS command line interpreter.
Navigate to the folder where the Decypher utility is located.
The following path is for my computer. Your path will be different of course.
cd C:\Users\John\AppData\Roaming\npm\node_modules\decyphertv

The following command gets rid of some screen clutter if you are using PowerShell.
function prompt {'PS> '}

Start the Decypher command line utility in debug mode.
At the OS console enter the following command.
Note: the information entered after the -e (end point) flag is the personal URL you get when signing up for an account with INFURA
Jordan covered this in video 6.
Be careful - Depending on the end point parameter, you maybe talking to the main Ethereum Blockchain and using real Ether. node --inspect --debug-brk index.js -m remote -e https://mainnet.infura.io/8KFs...MyInfuraAccessToken...oukQ
This will cause the Decypher utility to stop at the very first line of code in the index.js file.

Open the VS Code IDE in the decyphertv folder.

Set break points in the files of interest. Beware - A breakpoint will trigger if it is anywhere inside a function but will not trigger if it is on the very first line of a function definition

Press the Debug button on the left side bar.

If this is the first time debuging in the VS Code IDE you will see a pull down menu at the top of the screen which says No Configurations.
Left click on the pull down and select Add Configuration
On the panel that appears select Attach via Inspector Protocol
This will add the following code snippet to the Launch.json file.

{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "node",
            "request": "attach",
            "name": "Attach (Inspector Protocol)",
            "port": 9229,
            "protocol": "inspector"
        }
    ]
}

It will add some other snippets as well. It's OK.
Save the Launch.json file and exit out of the VS Code IDE.
Then start the VS Code IDE again.
Now the Launch configuration will be effective.

Press the Debug button on the left side bar.
Now press the green arrow at the top of the screen to start debugging.

Remember, the Decypher command line utility is halted at the first line of code.
So press the green arrow at the top of the screen.
Not the one on the left but rather the one on the right.
The one on the debugging tool bar.
Now the Decypher utility will run until it needs to prompt you for your private key.

Switch back to the Decypher command line utility.
Observe that it is asking for a private key. This is not sent out onto the Internet but rather is only used locally to sign transactions.
I used the following private key:
d9003665080effb22d48eba386bfc78e20311c9e91d584226e469d7d6e41e3d3
You are welcome to use it too.
The following is the public address or public key generated from the private key above.
0x28c67d97663f41e04c4e804d818bdd7fa4692509
This account will be charged for the transaction costs.
Never give out your private key like I just did or anyone will be able to take your ether. Use the above account if you would like to practice deploying smart contracts but please leave some of the ether for other people to use.
Notice that there is no 0x preceding the string and there are no quotes surrounding it.

The console will output Starting Decypher and will give the NodeJS command prompt when it has loaded.

Create a variable to hold the smart contract source code. Paste the following code into the Decypher Command line utility and press the Enter button.

var source = `contract HelloWorld {
  string public message;

  function HelloWorld(string _message) {
    message = _message;
  }

  function displayMessage() constant returns (string) {
    return message; 
  }

  function updateMessage(string _message) {
  	message = _message;
  }
}`

I have found that the code will not compile unless all opening braces are on the same line as the function declarations.
Notice the above contract has a constructor.

Check out the source code.
At the Decypher console execute the following command.
source
You will see the source code with special characters indicating new lines.

Deploy the contract.
At the Decypher console execute the following command.
decypher.deployContract(source, ["Hello, World! from John's 3rd contract"], {gas: 500000})
The second argument is the parameter for the constructor.
The third argument is optional.

If a breakpoint has been set anywhere inside that function then the program will halt and wait for you to step through it using the VS Code IDE.

After the code has been run, the console output will be similar to the following:

> Deploying Contract HelloWorld | Transaction Hash: 0xc24565c556806773c8d665d1a1328efdeac3c4c7faea017c962d0045917f78cc  

You can search etherscan.io for the transaction hash to check the progress of that transaction and to get all kinds of useful information. You can include the 0x or not - it doesn't matter.

After the contract has been mined the console will return the address of the contract much like as shown below.

Contract Deployed: Contract Address: 0xb16c44f0c4fb879be4cee51dbe53c58f76d23130, Block Number: 3449648, Gas Used: 412651

Search etherscan.io if you like to see information able the deployed contract.

The following command at the Decypher console provides useful information about the transaction.

web3.eth.getTransaction('0xc24565c556806773c8d665d1a1328efdeac3c4c7faea017c962d0045917f78cc')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment