Local Ethereum & smart contract install

This guide describes how an Ethereum (short ETH) network and smart contract, not mandatory ERC20 interface Token Contract compliant, become installed on your local machine.

For separating the concern we will stepwise open five terminal window.

Bootstrap the Ethereum network

Now let’s start with the first terminal window. We use it to get the required software installed.

brew install ethereum --devel
brew install npm
npm install -g solc
npm install -g truffle

Now let us create the working directory...

mkdir ethnetwork

…and the “genesis.json” file

{ 
    "nonce" : "0x0000000000000042", 
    "mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
    "difficulty" : "0x1",
    "alloc" : {},
    "timestamp" : "0x0",
    "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
    "extraData" : "0x",
    "gasLimit" : "0xfffffffffffff",
    "config": {
        "chainId": 5777,
        "istanbulBlock": 0,
        "homesteadBlock": 0,
        "eip150Block": 0,
        "eip155Block": 0,
        "eip158Block": 0,
        "byzantiumBlock": 0,
        "constantinopleBlock": 0,
        "petersburgBlock": 0
   }
}

And now the data directory

mkdir data

Init the Ethereum ledger

geth -datadir data init genesys.json

Last but not least start the Ethereum network

geth -cache 6000 -allow-insecure-unlock --http --http.port 3334 --http.api personal,eth,net,web3  --port 8545 --networkid 58342 --nodiscover --datadir=data --maxpeers=0 --rpc.allow-unprotected-txs console 2>>geth.log 

Create Ethereum accounts

personal.newAccount()
personal.newAccount()

The result looks something like the following. Copy the hexadecimal strings for later use.

  • “0xd1cf1858211020df62cbeea8b34b9314d200d497”
  • “0x5528bdaf5c4332c0bb642e651cd4c6c8a158ef52”

Bootstrap the Ethereum smart contract

For simplification let us make the access to the accounts less restrictive …

personal.unlockAccount("0xd1cf1858211020df62cbeea8b34b9314d200d497", "12345678", 150000)
personal.unlockAccount("0x5528bdaf5c4332c0bb642e651cd4c6c8a158ef52", "12345678", 150000)

… and activate the blockchain miner

miner.setEtherbase(web3.eth.coinbase) 
miner.start(2)

In the new terminal no. 3 prepare the development environment

truffle init

Now let us create the first contract with Solidity, which is the standard language for writing Ethereum smart contracts

Sub-clause: Some nice schema of contracts are found here:

  • Voting: e.g., https://github.com/louvrefinance/louvre-finance-token/blob/main/contracts…3_Ballot.sol
  • Ownership: https://github.com/louvrefinance/louvre-finance-token/blob/main/contracts…2_Owner.sol
  • Storage: https://github.com/louvrefinance/louvre-finance-token/blob/main/contracts…1_Storage.sol

In the truffle-config.js activate the following lines:

development: {
      host: "127.0.0.1",     // Localhost (default: none)
      port: 3334,            // Standard Ethereum port (default: none)
      network_id: "*",       // Any network (default: none)
},

By doing so we change the default Ethereum port to 3334.

Now compile and deploy the contracts:

truffle compile 
truffle migrate — network localhost

and get the hash of the deployed contract:

truffle console — network localhost
Contract.address

The result looks something like the following ‘0x986c16448c5B310774CA8EEeCbC44269Dec8c856’

Interacting with the Ethereum network and smart contracts

All available endpoints are documented here. As example we ask for the gas price:

curl --location --request POST 'localhost:3334/' \
--header 'Content-Type: application/json' \
--data-raw '{
        "jsonrpc":"2.0",
        "method":"eth_gasPrice",
        "params":[],
        "id":73
}'

The result looks something like the following: {“jsonrpc”:”2.0″,”id”:73,”result”:”0x3b9aca00″}

Remix is the default Web IDE here.; source code can be found on GitHub https://github.com/ethereum/remix-project . We proceed with the plain basic version of command line and JSON (HTTP) RPC.

Interacting with a smart contract happens via the eth_call or eth_sendTransaction methods, as explained here having the following parameter:

  • The from field for the data requester
  • The to for a smart contract
  • The data defines the method with all arguments

The ABI defines how to define and encode data for the EVM.

IMHO not really useable 🙁 Let us switch to a more abstracting programming language e.g., the Web3 Javascript or Python library. We continue with the Python Web3 stuff.

First we have to install the respective library “conda install -c conda-forge web3”.

Now a first small test program:

import web3

w3 = web3.Web3(web3.HTTPProvider("http://127.0.0.1:3334"))
print(w3.clientVersion)
print(w3.eth.get_accounts())
print("Balance user 0: ",w3.eth.get_balance(w3.eth.get_accounts()[0]))
print("Balance user 1: ",w3.eth.get_balance(w3.eth.get_accounts()[1]))

The result something look like the following:

Geth/v1.10.13-stable/darwin-arm64/go1.17.2
['0xD1cF1858211020df62CbeeA8b34B9314d200d497', '0x5528BdAf5C4332c0BB642e651cd4c6C8a158EF52']
Balance user 0:  13496000000000000000000
Balance user 1:  0

Let us transfer Ether (ETH) to play with the Ethereum blockchain:

import web3

w3 = web3.Web3(web3.HTTPProvider("http://127.0.0.1:3334"))
print(w3.clientVersion)
print(w3.eth.get_accounts())
print("Balance user 0: ",w3.eth.get_balance(w3.eth.get_accounts()[0]))
print("Balance user 1: ",w3.eth.get_balance(w3.eth.get_accounts()[1]))

value = w3.toWei(10, "ether")
txn = {
  "from": w3.eth.get_accounts()[0],
  "to": w3.eth.get_accounts()[1],
  "value": value,
  "gas": 21000,
  "gasPrice": 0
}
txn_hash = w3.eth.send_transaction(txn)
w3.eth.wait_for_transaction_receipt(txn_hash)
print("Balance user 0: ",w3.eth.get_balance(w3.eth.get_accounts()[0]))
print("Balance user 1: ",w3.eth.get_balance(w3.eth.get_accounts()[1]))

The result something look like the following:

Geth/v1.10.13-stable/darwin-arm64/go1.17.2
['0xD1cF1858211020df62CbeeA8b34B9314d200d497', '0x5528BdAf5C4332c0BB642e651cd4c6C8a158EF52']
Balance user 0:  13568000000000000000000
Balance user 1:  0
Balance user 0:  13562000000000000000000
Balance user 1:  10000000000000000000

Now lets play with our smart contract, name “Contract” ID “0x986c16448c5B310774CA8EEeCbC44269Dec8c856”:

import web3

w3 = web3.Web3(web3.HTTPProvider("http://127.0.0.1:3334"))
print(w3.clientVersion)
print(w3.eth.get_accounts())
print("Balance user 0: ",w3.eth.get_balance(w3.eth.get_accounts()[0]))
print("Balance user 1: ",w3.eth.get_balance(w3.eth.get_accounts()[1]))
"""
value = w3.toWei(10, "ether")
txn = {
  "from": w3.eth.get_accounts()[0],
  "to": w3.eth.get_accounts()[1],
  "value": value,
  "gas": 21000,
  "gasPrice": 0
}
txn_hash = w3.eth.send_transaction(txn)
w3.eth.wait_for_transaction_receipt(txn_hash)
print("Balance user 0: ",w3.eth.get_balance(w3.eth.get_accounts()[0]))
print("Balance user 1: ",w3.eth.get_balance(w3.eth.get_accounts()[1]))
"""
abi = [
        {"inputs":[],"stateMutability":"nonpayable","type":"constructor"},
        {"anonymous":False,"inputs":[{"indexed":False,"internalType":"address","name":"consumer","type":"address"},
        {"indexed":False,"internalType":"uint256","name":"consumerDeposit","type":"uint256"}],"name":"ConsumptionEvent","type":"event"},
        {"anonymous":False,"inputs":[{"indexed":False,"internalType":"address","name":"consumer","type":"address"},
        {"indexed":False,"internalType":"uint256","name":"consumerDeposit","type":"uint256"}],"name":"EndConsumeServiceEvent","type":"event"},
        {"anonymous":False,"inputs":[{"indexed":False,"internalType":"address","name":"provider","type":"address"},
        {"indexed":False,"internalType":"address","name":"consumer","type":"address"}],"name":"StartConsumeServiceEvent","type":"event"},
        {"inputs":[],"name":"consumer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function","constant":True},
        {"inputs":[],"name":"provider","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function","constant":True},
        {"inputs":[],"name":"startConsuming","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"consumption","outputs":[],"stateMutability":"payable","type":"function","payable":True},
        {"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}
    ]
address = "0x3194cBDC3dbcd3E11a07892e7bA5c3394048Cc87"
# Get the contract, the cosntructor was called while deployment :( 
OurContract = w3.eth.contract(address=address, abi=abi )
tx_hash = OurContract.functions.startConsuming().transact({"from":w3.eth.get_accounts()[0]})
tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash)

print(tx_receipt)

The result looks like the following after calling the “startConsuming” function:

Geth/v1.10.13-stable/darwin-arm64/go1.17.2
['0xD1cF1858211020df62CbeeA8b34B9314d200d497', '0x5528BdAf5C4332c0BB642e651cd4c6C8a158EF52']
Balance user 0:  14494000000000000000000
Balance user 1:  10000000000000000000
AttributeDict({'blockHash': HexBytes('0xb9b1d5b85bc2983c420f2b62587eab86741ae489be0c2e1c783aa52b2a4669cf'), 'blockNumber': 7253, 'contractAddress': None, 'cumulativeGasUsed': 21064, 'effectiveGasPrice': 1000000000, 'from': '0xD1cF1858211020df62CbeeA8b34B9314d200d497', 'gasUsed': 21064, 'logs': [], 'logsBloom': HexBytes('0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'), 'status': 1, 'to': '0x3194cBDC3dbcd3E11a07892e7bA5c3394048Cc87', 'transactionHash': HexBytes('0xe46f8550670ca8f852c884ee30fa54587b16519d34023fd42bf268b7662ae106'), 'transactionIndex': 0, 'type': '0x0'})

But, how have we set the “provider” ? Lets have a look if we can read the internal “provider” variable.

import web3

w3 = web3.Web3(web3.HTTPProvider("http://127.0.0.1:3334"))
print(w3.clientVersion)
print(w3.eth.get_accounts())
print("Balance user 0: ",w3.eth.get_balance(w3.eth.get_accounts()[0]))
print("Balance user 1: ",w3.eth.get_balance(w3.eth.get_accounts()[1]))
"""
value = w3.toWei(10, "ether")
txn = {
  "from": w3.eth.get_accounts()[0],
  "to": w3.eth.get_accounts()[1],
  "value": value,
  "gas": 21000,
  "gasPrice": 0
}
txn_hash = w3.eth.send_transaction(txn)
w3.eth.wait_for_transaction_receipt(txn_hash)
print("Balance user 0: ",w3.eth.get_balance(w3.eth.get_accounts()[0]))
print("Balance user 1: ",w3.eth.get_balance(w3.eth.get_accounts()[1]))
"""
abi = [
        {"inputs":[],"stateMutability":"nonpayable","type":"constructor"},
        {"anonymous":False,"inputs":[{"indexed":False,"internalType":"address","name":"consumer","type":"address"},
        {"indexed":False,"internalType":"uint256","name":"consumerDeposit","type":"uint256"}],"name":"ConsumptionEvent","type":"event"},
        {"anonymous":False,"inputs":[{"indexed":False,"internalType":"address","name":"consumer","type":"address"},
        {"indexed":False,"internalType":"uint256","name":"consumerDeposit","type":"uint256"}],"name":"EndConsumeServiceEvent","type":"event"},
        {"anonymous":False,"inputs":[{"indexed":False,"internalType":"address","name":"provider","type":"address"},
        {"indexed":False,"internalType":"address","name":"consumer","type":"address"}],"name":"StartConsumeServiceEvent","type":"event"},
        {"inputs":[],"name":"consumer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function","constant":True},
        {"inputs":[],"name":"provider","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function","constant":True},
        {"inputs":[],"name":"startConsuming","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"consumption","outputs":[],"stateMutability":"payable","type":"function","payable":True},
        {"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}
    ]
address = "0x3194cBDC3dbcd3E11a07892e7bA5c3394048Cc87"
# Get the contract, the cosntructor was called while deployment :( 
OurContract = w3.eth.contract(address=address, abi=abi )
provider = OurContract.functions.provider().address

print(provider)

The result is:

Geth/v1.10.13-stable/darwin-arm64/go1.17.2
['0xD1cF1858211020df62CbeeA8b34B9314d200d497', '0x5528BdAf5C4332c0BB642e651cd4c6C8a158EF52']
Balance user 0:  14632000000000000000000
Balance user 1:  10000000000000000000
(python371) dirkbangel@MacBook-Air-von-Dirk ethpy %  cd /Users/dirkbangel/Development/ethpy ; /usr/bin/env /opt/anaconda3/envs/python371/bin/python /Users/dirkbangel/.vscode/extensions/ms-python.python-2021.11.1422169775/pythonFiles/lib/python/debugpy/launcher 54015 -- /Users/dirkbangel/Development/ethpy/test.py 
Geth/v1.10.13-stable/darwin-arm64/go1.17.2
['0xD1cF1858211020df62CbeeA8b34B9314d200d497', '0x5528BdAf5C4332c0BB642e651cd4c6C8a158EF52']
Balance user 0:  14668000000000000000000
Balance user 1:  10000000000000000000
0x3194cBDC3dbcd3E11a07892e7bA5c3394048Cc87

OK per default he sender and receiver of a transaction is the contract itself. Can we change the value after deployment?

This entry was posted in Uncategorized. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *