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?