Skip to content

Commit

Permalink
feat: example web3py usage
Browse files Browse the repository at this point in the history
Signed-off-by: mateuszm-arianelabs <mateusz.marcinkowski@arianelabs.com>
  • Loading branch information
mateuszm-arianelabs committed Jul 19, 2024
1 parent 0d7e180 commit 126a707
Show file tree
Hide file tree
Showing 5 changed files with 212 additions and 0 deletions.
2 changes: 2 additions & 0 deletions tools/web3py-example/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
OPERATOR_PRIVATE_KEY=
RELAY_ENDPOINT=
1 change: 1 addition & 0 deletions tools/web3py-example/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.env
53 changes: 53 additions & 0 deletions tools/web3py-example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Web3py example
Example scripts for basic operations

### How to start
1. **Install Solidity Compiler (`solc`)**:
- Install `solc` by following the instructions in the [Solidity documentation](https://docs.soliditylang.org/en/latest/installing-solidity.html).
- On Ubuntu, you can run:
```sh
sudo apt install solc
```

2. **Set up a clean environment (with virtual env)**

```bash
# Install pip if it is not available:
$ which pip || curl https://bootstrap.pypa.io/get-pip.py | python
# Install virtualenv if it is not available:
$ which virtualenv || pip install --upgrade virtualenv
# *If* the above command displays an error, you can try installing as root:
$ sudo pip install virtualenv
# Create a virtual environment:
$ virtualenv -p python3 ~/.venv-py3
# Activate your new virtual environment:
$ source ~/.venv-py3/bin/activate
# With virtualenv active, make sure you have the latest packaging tools
$ pip install --upgrade pip setuptools
# Now we can install web3.py...
$ pip install --upgrade web3
# Install python-dotenv
$ pip install python-dotenv
# Install py-solc-x
$ pip install py-solc-x
```

Remember that each new terminal session requires you to reactivate your virtualenv, like:
```bash
$ source ~/.venv-py3/bin/activate
```

3. **Create and complete `.env` file from `.env.example`**

4. **Run script**
```bash
python scripts/test.py
```
18 changes: 18 additions & 0 deletions tools/web3py-example/contract/Greeter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//SPDX-License-Identifier: Unlicense
pragma solidity >0.5.0;

contract Greeter {
string public greeting;

constructor() public {
greeting = 'Hello';
}

function setGreeting(string memory _greeting) public {
greeting = _greeting;
}

function greet() view public returns (string memory) {
return greeting;
}
}
138 changes: 138 additions & 0 deletions tools/web3py-example/scripts/test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
#
# Hedera JSON RPC Relay - Web3py Example
#
# Copyright (C) 2022-2024 Hedera Hashgraph, LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

import os
import unittest
from dotenv import load_dotenv
from web3 import Web3
from solcx import install_solc, compile_source

def setup_environment():
# install latest solc
install_solc(version='latest')

# load values from our .env file
load_dotenv()
OPERATOR_PRIVATE_KEY = os.getenv('OPERATOR_PRIVATE_KEY')
RELAY_ENDPOINT = os.getenv('RELAY_ENDPOINT')

# connect to chain
w3 = Web3(Web3.HTTPProvider(RELAY_ENDPOINT))

# get account from pk
acc = w3.eth.account.from_key(OPERATOR_PRIVATE_KEY)

return w3, acc


def get_balance(w3, acc):
balance = w3.eth.get_balance(acc.address)
return balance


def deploy_contract(w3, acc):
# read contract from file
with open('contract/Greeter.sol', 'r') as f:
source = f.read()

# compile our Greeter contract
compiled_sol = compile_source(source, output_values=['abi', 'bin'])

# retrieve the contract interface
contract_id, contract_interface = compiled_sol.popitem()

bytecode = contract_interface['bin']
abi = contract_interface['abi']

# create web3.py contract instance
Greeter = w3.eth.contract(abi=abi, bytecode=bytecode)

# build transaction
unsent_tx_hash = Greeter.constructor().build_transaction({
"from": acc.address,
"nonce": w3.eth.get_transaction_count(acc.address),
})

# sign transaction
signed_tx = w3.eth.account.sign_transaction(unsent_tx_hash, private_key=acc.key)

# send transaction
tx_hash = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash)

# create instance of deployed contract
greeter = w3.eth.contract(
address=tx_receipt.contractAddress,
abi=abi
)

return greeter, tx_receipt.contractAddress


def contract_view_call(greeter):
greeting = greeter.functions.greet().call()
return greeting


def contract_call(w3, acc, greeter):
# build contract call transaction
unsent_call_tx_hash = greeter.functions.setGreeting('Hello2').build_transaction({
"from": acc.address,
"nonce": w3.eth.get_transaction_count(acc.address),
})

# sign transaction
signed_call_tx = w3.eth.account.sign_transaction(unsent_call_tx_hash, private_key=acc.key)

# send transaction
call_tx_hash = w3.eth.send_raw_transaction(signed_call_tx.rawTransaction)
w3.eth.wait_for_transaction_receipt(call_tx_hash)

# Verify the greeting has been updated
new_greeting = greeter.functions.greet().call()
return new_greeting


class TestGreeterContract(unittest.TestCase):

@classmethod
def setUpClass(cls):
cls.w3, cls.acc = setup_environment()
cls.greeter, cls.contract_address = deploy_contract(cls.w3, cls.acc)

def test_get_balance(self):
balance = get_balance(self.w3, self.acc)
self.assertIsInstance(balance, int, "Account balance is an integer")

def test_deploy_contract(self):
self.assertTrue(self.contract_address.startswith('0x'), "Contract address starts with '0x'")

def test_call_view(self):
greeting = contract_view_call(self.greeter)
self.assertEqual(greeting, 'Hello', "Initial greeting matches expected value")

def test_contract_call(self):
new_greeting = contract_call(self.w3, self.acc, self.greeter)
self.assertEqual(new_greeting, 'Hello2', "Updated greeting matches expected value")

final_greeting = contract_view_call(self.greeter)
self.assertEqual(final_greeting, 'Hello2', "Final greeting matches expected value after update")


if __name__ == "__main__":
unittest.main()

0 comments on commit 126a707

Please sign in to comment.