PolarSPARC

Using Anvil and Solidity with Python


Bhaskar S *UPDATED*07/19/2025


Overview

Ethereum is a popular decentralized, open source blockchain platform that powers the second largest cryptocurreny called Ether (ETH). In addition, the platform offers a virtual machine environment that allows one to deploy and execute Decentralized Applications (dApps) called Smart Contracts.

Anvil, which is part of the Foundry smart contract development toolchain, is a command-line interface for running a locally hosted Ethereum blockchain that enables one to develop and test Ethereum distributed applications (dApps) in a rapid fasion. It includes support for all the popular json-rpc API calls related to Ethereum and enables one to develop, deploy, and test dApps in a safe and deterministic environment.

Smart Contracts in Ethereum are built using a programing language called Solidity, which is a high-level, contract-oriented, statically typed, and bytecode compiled language.

For this article, we will show how to setup a development environment for building, deploying, and testing Ethereum dApps. For the demonstration, we will use some aspects of the motor vehicle use-case that is described in the introductory article Introduction to Blockchain.


Setup and Installation

The setup will be on a Ubuntu 24.04 LTS based Linux desktop.

Ensure that the desktop has Docker installed and setup. Else, please refer to the article Introduction to Docker for additional help.

In addition, ensure that the Python programming language (version 3.11 or above) is installed.

We will setup a directory structure for Solidity by executing the following commands in a terminal window:


$ mkdir -p /tmp/solidity/build


At the time of this article, the current stable version of Foundry docker image was v1.2.3.

To pull and download the docker image for Foundry, execute the following command:


$ docker pull ghcr.io/foundry-rs/foundry:v1.2.3


The following would be the typical output:


Output.1

v1.2.3: Pulling from foundry-rs/foundry
89dc6ea4eae2: Already exists 
a881c24c3b81: Pull complete 
3e820f25c72f: Pull complete 
99991c961832: Pull complete 
Digest: sha256:d9133dae61c19383b72695dc7eeca29d1e7a89f1f1b5fdfd8900c660b46b4303
Status: Downloaded newer image for ghcr.io/foundry-rs/foundry:v1.2.3
ghcr.io/foundry-rs/foundry:v1.2.3

Next, it is time to pull the solidity compiler image. At the time of this article, the current stable version of solidity docker image was 0.8.30.

To pull and download the docker image for Solidity, execute the following command:


$ docker pull ethereum/solc:0.8.30


The following would be the typical output:


Output.2

0.8.30: Pulling from ethereum/solc
8c126d3498dd: Pull complete 
Digest: sha256:b116bf835554d40c501feab0b2c943a8c5eec003b804bcc5b326b85c93da00c2
Status: Downloaded newer image for ethereum/solc:0.8.30
docker.io/ethereum/solc:0.8.30

The next step is to install the Python module Web3, which is the library for interacting with Ethereum. In order to do that, execute the following command:


$ python -m pip install web3


It is now time to launch anvil. Open a new terminal and execute the following command to start a local Ethereum blockchain environment:


$ docker run --rm --name anvil --network=host --add-host=host.docker.internal:host-gateway ghcr.io/foundry-rs/foundry:v1.2.3 anvil


The following would be the typical output:


Output.3

                             _   _
                            (_) | |
      __ _   _ __   __   __  _  | |
     / _` | | '_ \  \ \ / / | | | |
    | (_| | | | | |  \ V /  | | | |
     \__,_| |_| |_|   \_/   |_| |_|

    1.2.3-v1.2.3 (a813a2cee7 2025-06-08T14:04:05.664691549Z)
    https://github.com/foundry-rs/foundry

Available Accounts
==================

(0) 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 (10000.000000000000000000 ETH)
(1) 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 (10000.000000000000000000 ETH)
(2) 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC (10000.000000000000000000 ETH)
(3) 0x90F79bf6EB2c4f870365E785982E1f101E93b906 (10000.000000000000000000 ETH)
(4) 0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65 (10000.000000000000000000 ETH)
(5) 0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc (10000.000000000000000000 ETH)
(6) 0x976EA74026E726554dB657fA54763abd0C3a0aa9 (10000.000000000000000000 ETH)
(7) 0x14dC79964da2C08b23698B3D3cc7Ca32193d9955 (10000.000000000000000000 ETH)
(8) 0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f (10000.000000000000000000 ETH)
(9) 0xa0Ee7A142d267C1f36714E4a8F75612F20a79720 (10000.000000000000000000 ETH)

Private Keys
==================

(0) 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
(1) 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d
(2) 0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a
(3) 0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6
(4) 0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a
(5) 0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba
(6) 0x92db14e403b83dfe3df233f83dfa3a0d7096f21ca9b0d6d6b8d88b2b4ec1564e
(7) 0x4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1fcdbf7cbf4356
(8) 0xdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97
(9) 0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6

Wallet
==================
Mnemonic:          test test test test test test test test test test test junk
Derivation path:   m/44'/60'/0'/0/


Chain ID
==================

31337

Base Fee
==================

1000000000

Gas Limit
==================

30000000

Genesis Timestamp
==================

1752430978

Genesis Number
==================

0

Listening on 127.0.0.1:8545

This completes the setup and installation of the required components.


Hands-On with Anvil and Solidity using Python

To make API requests on our local blockchain environment, we will issue the following commands at the Python interpreter prompt:


>>> import json

>>> from web3 import Web3

>>> provider = Web3.HTTPProvider('http://127.0.0.1:8545')

>>> w3 = Web3(provider)


To check if we have successfully connected to our development environment, execute the following command at the Python interpreter prompt:


>>> w3.is_connected()


The following would be the typical output:


Output.4

True

Anvil by default, will automatically generate 10 account addresses when started. We will designate the first address for the bank, the second for the buyer, the third for the dealer, and the fourth for the dmv respectively.

To assign the second address to the buyer and the third to the dealer, execute the following commands at the Python interpreter prompt:


>>> buyer = '0x70997970C51812dc3A010C7d01b50e0d17dc79C8'

>>> dealer = '0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC'


To verify if an Ethereum account address is valid, execute the following command at the Python interpreter prompt:


>>> w3.is_address(buyer)


The following would be the typical output:


Output.5

True

Ethereum provides built-in support for address checksum as a way to indicate if an address is valid and not. To verify if an Ethereum account address is checksummed address, execute the following command at the Python interpreter prompt:


>>> w3.is_checksum_address(dealer)


The following would be the typical output:


Output.6

True

To retrieve the details of the genesis block (the very first block with number 0), execute the following commands at the Python interpreter prompt:


>>> json.loads(w3.to_json(w3.eth.get_block(0)))

>>> json.dumps(genesis_block, indent=2)


The following would be the typical output:


Output.7

{
  "hash": "0x2d1e41d042d7572bc6f1d9897d300031bb2288cdf76f85d6b98d039a9e8158d7",
  "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
  "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
  "miner": "0x0000000000000000000000000000000000000000",
  "stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
  "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
  "receiptsRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
  "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  "difficulty": 0,
  "number": 0,
  "gasLimit": 30000000,
  "gasUsed": 0,
  "timestamp": 1752435682,
  "extraData": "0x",
  "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
  "nonce": "0x0000000000000000",
  "baseFeePerGas": 1000000000,
  "withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
  "blobGasUsed": 0,
  "excessBlobGas": 0,
  "parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
  "totalDifficulty": 0,
  "size": 582,
  "uncles": [],
  "transactions": [],
  "withdrawals": []
}

To display account balance for the buyer account in ETH, execute the following command at the Python interpreter prompt:


>>> w3.from_wei(w3.eth.get_balance(buyer), 'ether')


The following would be the typical output:


Output.8

10000

We will now send a transaction to transfer 5 ETH from the buyer to the dealer. In order to do this we will need to create a transaction message. Enter the following data at the Python interpreter prompt to create the transaction message structure:


>>> buyer_dealer_txn = {

... 'from': w3.to_checksum_address(buyer),

... 'to': w3.to_checksum_address(dealer),

... 'value': w3.to_wei(5, 'ether'),

... 'gas': 90000,

... 'gasPrice': 18000000000,

... 'nonce': 0,

... 'chainId': 31337

...}


Before sending the transaction message buyer_dealer_txn, it needs to be digitally signed using the private key of the buyer. To do that, execute the following commands at the Python interpreter prompt:


>>> buyer_priv_key = '0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d'

>>> signed_txn = w3.eth.account.sign_transaction(buyer_dealer_txn, buyer_priv_key)


To send the signed transaction to transfer 5 ETH from the buyer to the dealer, execute the following command at the Python interpreter prompt:


>>> txn = w3.eth.send_raw_transaction(signed_txn.raw_transaction)


The executed method will return a transaction hash as a hex string (similar to a transaction id).

To display all the details of the just sent transaction on the local blockchain, execute the following commands at the Python interpreter prompt:


>>> txn_json = json.loads(w3.to_json(w3.eth.get_transaction(txn)))

>>> json.dumps(txn_json, indent=2)


The following would be the typical output:


Output.9

{
  "type": 0,
  "chainId": 31337,
  "nonce": 0,
  "gasPrice": 18000000000,
  "gas": 90000,
  "to": "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC",
  "value": 5000000000000000000,
  "input": "0x",
  "r": "0x78dcf90fc7eb826935ce967459c0452f61351a56556498693786f914ff559d90",
  "s": "0x4a5b66370f10ad28907eb217c8e79fda0d868fff560a150be7f988e2ba87ad9f",
  "v": 62710,
  "hash": "0x4d622c58834c7ee12a5bab930c7f8db713f9bdb0db0dc83dca4ba8937ff73f93",
  "blockHash": "0x2a4df840953aa8b38c8e923466c94b297361856fce0751cf123b77d556245a99",
  "blockNumber": 1,
  "transactionIndex": 0,
  "from": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8"
}

To display the block number of the latest block in our local blockchain, execute the following command at the Python interpreter prompt:


>>> w3.eth.block_number


The following would be the typical output:


Output.10

1

To retrieve the details of the latest block from our local blockchain, execute the following commands at the Python interpreter prompt:


>>> latest_block = json.loads(w3.to_json(w3.eth.get_block('latest')))

>>> json.dumps(latest_block, indent=2)


The following would be the typical output:


Output.11

{
  "hash": "0x1efe6d13ad7a163f989035d077c495e3fc7a498caba2fe25766ac99fa0ab0ff8",
  "parentHash": "0xd92492ef174bc24325a48f4d84dedbd0cb0758c070e9419ccfad3ee642c36a0d",
  "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
  "miner": "0x0000000000000000000000000000000000000000",
  "stateRoot": "0x486cba9c6cea8b7037cd5e3aab7bfd5b40dedfb4ceb8bb29e95135664f2312cf",
  "transactionsRoot": "0xa32ddc9712c0e510d27386362dd9a4a51a7409f60bc011a4fc33f3c77f84e653",
  "receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
  "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  "difficulty": 0,
  "number": 1,
  "gasLimit": 30000000,
  "gasUsed": 21000,
  "timestamp": 1752437074,
  "extraData": "0x",
  "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
  "nonce": "0x0000000000000000",
  "baseFeePerGas": 1000000000,
  "withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
  "blobGasUsed": 0,
  "excessBlobGas": 0,
  "parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
  "totalDifficulty": 0,
  "size": 698,
  "uncles": [],
  "transactions": [
    "0x4d622c58834c7ee12a5bab930c7f8db713f9bdb0db0dc83dca4ba8937ff73f93"
  ],
  "withdrawals": []
}

To display account balance for the buyer account, execute the following command at the Python interpreter prompt:


>>> w3.from_wei(w3.eth.get_balance(buyer), 'ether')


The following would be the typical output:


Output.12

9994.999622

Similarly, to display account balance for the dealer account, execute the following command at the Python interpreter prompt:


>>> w3.from_wei(w3.eth.get_balance(dealer), 'ether')


The following would be the typical output:


Output.13

10005

As is evident from the Output.12 and Output.13 above, the account balance for the buyer has decreased, while the account balance for the dealer has increased.

EXCELLENT !!! Our local Ethereum blockchain environment is working as expected.

Moving on to writing, deploying, and testing a simple smart contract using Solidity.

The following is the code for the simple smart contract located in the directory /tmp/solidity:


Vehicle.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

contract Vehicle {
    string _color;
    string _vin;
    uint _cost;
    address _dealer;
    address _owner;

    // Will be initiated by the dealer
    constructor(string memory color, string memory vin, uint cost) {
        _color = color;
        _vin = vin;
        _cost = cost;
        _dealer = msg.sender;
    }
    
    function getColor() public view returns (string memory) {
        return _color;
    }
    
    function getVin() public view returns (string memory) {
        return _vin;
    }
    
    function getCost() public view returns (uint) {
        return _cost;
    }
    
    function getDealer() public view returns (address) {
        return _dealer;
    }
    
    function getOwner() public view returns (address) {
        return _owner;
    }
    
    // Will be initiated by the buyer
    function buyVehicle(uint amount) public payable {
        require(msg.sender != _dealer);
        
        // Equivalent to checking _owner == null
        require(_owner == address(0));
        
        require(amount >= _cost);
        
        _owner = msg.sender;
    }
}

To compile Vehicle.sol using the docker image for Solidity, execute the following command:


$ docker run --rm --name solc -u $(id -u $USER):$(id -g $USER) -v /tmp/solidity:/home/solc ethereum/solc:0.8.30 -o /home/solc/build --abi --bin --gas /home/solc/Vehicle.sol


The following would be the typical output:


Output.14

======= home/solc/Vehicle.sol:Vehicle =======
Gas estimation:
construction:
   infinite + 295200 = infinite
external:
   buyVehicle(uint256):	31084
   getColor():	infinite
   getCost():	2498
   getDealer():	2559
   getOwner():	2515
   getVin():	infinite

On successful compilation, there will be two files generated, namely, Vehicle.abi and Vehicle.bin located in the directory /tmp/solidity/build.

The option --gas passed to the Solidity compiler enables one to get an estimate on the amount of gas required to create/deploy a contract and to interact with the various functions.

The option --abi passed to the Solidity compiler enables the generation of the Vehicle.abi file, which serves as the standard interface that details all of the publicly accessible contract methods.

The following is a pretty version of the contents of the file Vehicle.abi:


Vehicle.abi
[
  {
    "inputs": [
      {
        "internalType": "string",
        "name": "color",
        "type": "string"
      },
      {
        "internalType": "string",
        "name": "vin",
        "type": "string"
      },
      {
        "internalType": "uint256",
        "name": "cost",
        "type": "uint256"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "buyVehicle",
    "outputs": [],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "getColor",
    "outputs": [
      {
        "internalType": "string",
        "name": "",
        "type": "string"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "getCost",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "getDealer",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "getOwner",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "getVin",
    "outputs": [
      {
        "internalType": "string",
        "name": "",
        "type": "string"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  }
]

The option --bin passed to the Solidity compiler enables the generation of the Vehicle.bin binary file that contains the hex-encoded bytecode for the specified smart contract source file. This bytecode will be executed by the Ethereum Virtual Machine (EVM), which serves as a runtime environment for smart contracts.

The following is a pretty version of the contents of the file Vehicle.bin:


Vehicle.bin
608060405234801561000f575f5ffd5b50604051610b4e380380610b4e8339818101604052810190610031919061021f565b825f908161003f91906104ae565b5081
6001908161004f91906104ae565b50806002819055503360035f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffff
ffffffffffffffffffffffffffff16021790555050505061057d565b5f604051905090565b5f5ffd5b5f5ffd5b5f5ffd5b5f5ffd5b5f601f19601f83011690509190
50565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6100fe826100b8565b810181811067ffffff
ffffffffff8211171561011d5761011c6100c8565b5b80604052505050565b5f61012f61009f565b905061013b82826100f5565b919050565b5f67ffffffffffffff
ff82111561015a576101596100c8565b5b610163826100b8565b9050602081019050919050565b8281835e5f83830152505050565b5f61019061018b84610140565b
610126565b9050828152602081018484840111156101ac576101ab6100b4565b5b6101b7848285610170565b509392505050565b5f82601f8301126101d3576101d2
6100b0565b5b81516101e384826020860161017e565b91505092915050565b5f819050919050565b6101fe816101ec565b8114610208575f5ffd5b50565b5f815190
50610219816101f5565b92915050565b5f5f5f60608486031215610236576102356100a8565b5b5f84015167ffffffffffffffff811115610253576102526100ac56
5b5b61025f868287016101bf565b935050602084015167ffffffffffffffff8111156102805761027f6100ac565b5b61028c868287016101bf565b92505060406102
9d8682870161020b565b9150509250925092565b5f81519050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f5260
2260045260245ffd5b5f60028204905060018216806102f557607f821691505b602082108103610308576103076102b1565b5b50919050565b5f819050815f526020
5f209050919050565b5f6020601f8301049050919050565b5f82821b905092915050565b5f6008830261036a7fffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffff8261032f565b610374868361032f565b95508019841693508086168417925050509392505050565b5f819050919050565b5f6103af6103
aa6103a5846101ec565b61038c565b6101ec565b9050919050565b5f819050919050565b6103c883610395565b6103dc6103d4826103b6565b84845461033b565b82
5550505050565b5f5f905090565b6103f36103e4565b6103fe8184846103bf565b505050565b5b81811015610421576104165f826103eb565b600181019050610404
565b5050565b601f821115610466576104378161030e565b61044084610320565b8101602085101561044f578190505b61046361045b85610320565b830182610403
565b50505b505050565b5f82821c905092915050565b5f6104865f198460080261046b565b1980831691505092915050565b5f61049e8383610477565b9150826002
028217905092915050565b6104b7826102a7565b67ffffffffffffffff8111156104d0576104cf6100c8565b5b6104da82546102de565b6104e5828285610425565b
5f60209050601f831160018114610516575f8415610504578287015190505b61050e8582610493565b865550610575565b601f1984166105248661030e565b5f5b82
81101561054b57848901518255600182019150602085019450602081019050610526565b868310156105685784890151610564601f891682610477565b8355505b60
01600288020188555050505b505050505050565b6105c48061058a5f395ff3fe608060405260043610610054575f3560e01c806336ce092014610058578063893d20
e8146100745780639a86139b1461009e5780639cf6d1af146100c8578063bd3e19d4146100f2578063bf20f9401461011c575b5f5ffd5b6100726004803603810190
61006d91906103f6565b610146565b005b34801561007f575f5ffd5b50610088610247565b6040516100959190610460565b60405180910390f35b3480156100a957
5f5ffd5b506100b261026f565b6040516100bf91906104e9565b60405180910390f35b3480156100d3575f5ffd5b506100dc6102fe565b6040516100e99190610460
565b60405180910390f35b3480156100fd575f5ffd5b50610106610326565b6040516101139190610518565b60405180910390f35b348015610127575f5ffd5b5061
013061032f565b60405161013d91906104e9565b60405180910390f35b60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffff
ffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff160361019e575f5ffd5b5f73ffffffffffffffffffffffffff
ffffffffffffff1660045f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161461
01f6575f5ffd5b600254811015610204575f5ffd5b3360045f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffff
ffffffffffffffffffffffffff16021790555050565b5f60045f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60605f80
5461027d9061055e565b80601f01602080910402602001604051908101604052809291908181526020018280546102a99061055e565b80156102f45780601f106102
cb576101008083540402835291602001916102f4565b820191905f5260205f20905b8154815290600101906020018083116102d757829003601f168201915b505050
5050905090565b5f60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b5f600254905090565b60606001805461033e90
61055e565b80601f016020809104026020016040519081016040528092919081815260200182805461036a9061055e565b80156103b55780601f1061038c57610100
8083540402835291602001916103b5565b820191905f5260205f20905b81548152906001019060200180831161039857829003601f168201915b5050505050905090
565b5f5ffd5b5f819050919050565b6103d5816103c3565b81146103df575f5ffd5b50565b5f813590506103f0816103cc565b92915050565b5f6020828403121561
040b5761040a6103bf565b5b5f610418848285016103e2565b91505092915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f
61044a82610421565b9050919050565b61045a81610440565b82525050565b5f6020820190506104735f830184610451565b92915050565b5f81519050919050565b
5f82825260208201905092915050565b8281835e5f83830152505050565b5f601f19601f8301169050919050565b5f6104bb82610479565b6104c58185610483565b
93506104d5818560208601610493565b6104de816104a1565b840191505092915050565b5f6020820190508181035f83015261050181846104b1565b905092915050
565b610512816103c3565b82525050565b5f60208201905061052b5f830184610509565b92915050565b7f4e487b7100000000000000000000000000000000000000
0000000000000000005f52602260045260245ffd5b5f600282049050600182168061057557607f821691505b60208210810361058857610587610531565b5b509190
5056fea2646970667358221220c91c1c5963ac474103a239e769c36b528376e076c917fe97cee411cfe9b5111464736f6c634300081e0033

To load the contents of Vehicle.abi into a variable called abi, execute the following commands at the Python interpreter prompt:


>>> with open('/tmp/solidity/build/Vehicle.abi', 'r') as f_abi:

>>> ... v_abi = json.loads(f_abi.read())


Similarly, load the contents of Vehicle.bin into a variable called bin by executing the following commands at the Python interpreter prompt:


>>> with open('/tmp/solidity/build/Vehicle.bin', 'r') as f_bin:

>>> ... v_bin = f_bin.read()


For our use-case, the dealer first deploys the contract of a vehicle. For that, we first need to set the default account to the dealer account. In addition, we will set a value of 5 ETHs as the cost of the vehicle. Execute the following commands at the Python interpreter prompt:


>>> cost = w3.to_wei(5, 'ether')

>>> w3.eth.default_account = dealer


The second step is to instantiate the smart contract as an object by executing the following command at the Python interpreter prompt:


>>> vehicle = w3.eth.contract(abi=v_abi, bytecode=v_bin)


The third step is to deploy the smart contract to our local Ethereum blockchain environment by executing the following command at the Python interpreter prompt:


>>> txn_hash = vehicle.constructor('Blue', 'V12345C0001', cost).transact()


The deployment of the smart contract will return a transaction hash (a handle to the transaction on the blockchain).

The fourth step is to wait for the receipt of the deployment of the smart contract into our local Ethereum blockchain environment. This is typically to wait for the transaction to be processed by a miner. In our local environment, there is no mining involved and all the transactions are processed immediately . In order to retrieve the transaction receipt, execute the following command at the Python interpreter prompt:


>>> txn_receipt = w3.eth.wait_for_transaction_receipt(txn_hash)


To display the transaction receipt details for the just deployed smart contract, execute the following commands at the Python interpreter prompt:


>>> txn_receipt_json = json.loads(w3.to_json(txn_receipt))

>>> json.dumps(txn_receipt_json, indent=2)


The following would be the typical output:


Output.15

{
  "type": 2,
  "status": 1,
  "cumulativeGasUsed": 486071,
  "logs": [],
  "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  "transactionHash": "0xb267747e0739ee1cdccc5ea34bb46bda66634ead3dee927ea3ac238367306c69",
  "transactionIndex": 0,
  "blockHash": "0xc43681527404912349233a5b7b54f31ace4d08b23e92222f2112f14a00865a53",
  "blockNumber": 2,
  "gasUsed": 486071,
  "effectiveGasPrice": 875175000,
  "blobGasPrice": 1,
  "from": "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC",
  "to": null,
  "contractAddress": "0x663F3ad617193148711d28f5334eE4Ed07016602"
}

Now that we have successfully deployed the smart contract into our local Ethereum blockchain environment, we need to get the instance of that contract in order to interact with it.

To get the instance of the smart contract from our local blockchain environment into a variable called vehicle_contract, execute the following command at the Python interpreter prompt:


>>> vehicle_contract = w3.eth.contract(address=txn_receipt.contractAddress, abi=v_abi)


To invoke the method getVin() on the contract instance, execute the following command at the Python interpreter prompt:


>>> vin = vehicle_contract.functions.getVin().call()

>>> vin


The following would be the typical output:


Output.16

V12345C0001

Similarly, to invoke the method getOwner() on the contract instance, execute the following command at the Python interpreter prompt:


>>> owner = vehicle_contract.functions.getOwner().call()

>>> owner


The following would be the typical output:


Output.17

0x0000000000000000000000000000000000000000

The zero (0) address in Output.17 above indicates the vehicle is not yet purchased by any buyer.

Now for the exciting part - the buyer will transact with the smart contract deployed by the dealer to buy the vehicle. In order to do this we will need to create a transaction message. Enter the following data at the Python interpreter prompt to create the transaction message structure for buying the vehicle:


>>> gas_price = 1000000

>>> buyer_txn = { 'from': w3.to_checksum_address(buyer), 'value': cost, 'gas': gas_price }


The buyer will use the transaction message buyer_txn to transact with the smart contract to buy the vehicle by executing the following command at the Python interpreter prompt:


>>> buyer_txn_hash = vehicle_contract.functions.buyVehicle(cost).transact(buyer_txn)


The interaction with the smart contract (by the buyer) will return a transaction hash.

To wait for the receipt of the transaction related to the interaction with the smart contract, execute the following command at the Python interpreter prompt:


>>> buyer_txn_receipt = w3.eth.wait_for_transaction_receipt(buyer_txn_hash)


To display the transaction receipt details related to the interaction with the smart contract, execute the following commands at the Python interpreter prompt:


>>> buyer_txn_receipt_json = json.loads(w3.to_json(buyer_txn_receipt))

>>> json.dumps(buyer_txn_receipt_json, indent=2)


The following would be the typical output:


Output.18

{
  "type": 2,
  "status": 1,
  "cumulativeGasUsed": 48248,
  "logs": [],
  "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  "transactionHash": "0x47c55de11bcd09b643a872c2e1f95d0bff9ed0c21ce9421311f8ef1ee63fb196",
  "transactionIndex": 0,
  "blockHash": "0x805ca37b10bc8099d815a26144de84bcfb1ef5388fac702c13398c2115cce0e2",
  "blockNumber": 3,
  "gasUsed": 48248,
  "effectiveGasPrice": 769323102,
  "blobGasPrice": 1,
  "from": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
  "to": "0x663F3ad617193148711d28f5334eE4Ed07016602",
  "contractAddress": null
}

Now invoke the method getOwner() on the contract instance, execute the following command at the Python interpreter prompt:


>>> owner = vehicle_contract.functions.getOwner().call()

>>> owner


The following would be the typical output:


Output.19

0x70997970C51812dc3A010C7d01b50e0d17dc79C8

The presence of the buyer's address in Output.19 above indicates the vehicle has been successfully purchased by the buyer.

To display account balance for the buyer account, execute the following command at the Python interpreter prompt:


>>> w3.from_wei(w3.eth.get_balance(buyer), 'ether')


The following would be the typical output:


Output.20

9989.999584881698974704

As is evident from the Output.20 above, the account balance for the buyer has decreased by the cost of the vehicle and some gas.

BINGO !!! We have successfully built, deployed, and transacted with a smart contract in our local Ethereum blockchain environment.


References

Anvil Overview

Solidity Documentation

Web3.py Documentation



© PolarSPARC