mirror of
https://github.com/autistic-symposium/web3-starter-py.git
synced 2025-05-17 22:20:22 -04:00
clean up and add web3
This commit is contained in:
parent
a0cfa09ec4
commit
fc8d690a51
29 changed files with 3043 additions and 31 deletions
1
web3_python_toolkit/scripts/utils/__init__.py
Normal file
1
web3_python_toolkit/scripts/utils/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
# -*- encoding: utf-8 -*-
|
25
web3_python_toolkit/scripts/utils/arithmetics.py
Normal file
25
web3_python_toolkit/scripts/utils/arithmetics.py
Normal file
|
@ -0,0 +1,25 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
# arithmetics.py
|
||||
# This class implements math methods used by the other classes.
|
||||
# author: steinkirch
|
||||
|
||||
|
||||
from decimal import Decimal, getcontext
|
||||
from utils.strings import log_error
|
||||
|
||||
|
||||
def div(dividend, divisor) -> Decimal:
|
||||
"""Return higher precision division."""
|
||||
|
||||
if divisor == 0:
|
||||
log_error('Found a zero division error. Returning 0.')
|
||||
return 0
|
||||
return to_decimal(dividend) / to_decimal(divisor)
|
||||
|
||||
|
||||
def to_decimal(value, precision=None) -> Decimal:
|
||||
"""Return Decimal value for higher (defined) precision."""
|
||||
|
||||
precision = precision or 22
|
||||
getcontext().prec = precision
|
||||
return Decimal(value)
|
149
web3_python_toolkit/scripts/utils/os.py
Normal file
149
web3_python_toolkit/scripts/utils/os.py
Normal file
|
@ -0,0 +1,149 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
# This class implements OS/file system util methods used by the other classes.
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import copy
|
||||
import logging
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from dotenv import load_dotenv
|
||||
|
||||
|
||||
def set_logging(log_level) -> None:
|
||||
"""Set logging level according to .env config."""
|
||||
|
||||
if log_level == 'info':
|
||||
logging.basicConfig(level=logging.INFO, format='%(message)s')
|
||||
|
||||
elif log_level == 'error':
|
||||
logging.basicConfig(level=logging.ERROR, format='%(message)s')
|
||||
|
||||
elif log_level == 'debug':
|
||||
logging.basicConfig(level=logging.DEBUG, format='%(message)s')
|
||||
|
||||
else:
|
||||
print(f'Logging level {log_level} is not available. Setting to ERROR')
|
||||
logging.basicConfig(level=logging.ERROR, format='%(message)s')
|
||||
|
||||
|
||||
def load_config(keys) -> dict:
|
||||
"""Load and set environment variables."""
|
||||
|
||||
env_file = Path('..') / '.env'
|
||||
if not os.path.isfile(env_file):
|
||||
exit_with_error('Please create an .env file')
|
||||
|
||||
load_dotenv(env_file)
|
||||
env_vars = {}
|
||||
|
||||
try:
|
||||
for key in keys:
|
||||
env_vars[key] = os.getenv(key)
|
||||
set_logging(os.getenv("LOG_LEVEL"))
|
||||
return env_vars
|
||||
except KeyError as e:
|
||||
exit_with_error(f'Cannot extract env variables: {e}. Exiting.')
|
||||
|
||||
|
||||
def log_error(string) -> None:
|
||||
"""Print STDOUT error using the logging library."""
|
||||
|
||||
logging.error('🚨 %s', string)
|
||||
|
||||
|
||||
def log_info(string) -> None:
|
||||
"""Print STDOUT info using the logging library."""
|
||||
|
||||
logging.info('✅ %s', string)
|
||||
|
||||
|
||||
def log_debug(string) -> None:
|
||||
"""Print STDOUT debug using the logging library."""
|
||||
|
||||
logging.debug('🟨 %s', string)
|
||||
|
||||
|
||||
def open_json(filepath) -> dict:
|
||||
"""Load and parse a json file."""
|
||||
|
||||
try:
|
||||
with open(filepath, 'r', encoding='utf-8') as infile:
|
||||
return json.load(infile)
|
||||
|
||||
except (IOError, FileNotFoundError, TypeError) as e:
|
||||
exit_with_error(f'Failed to parse: "{filepath}": {e}')
|
||||
|
||||
|
||||
def format_path(dir_path, filename) -> str:
|
||||
"""Format a OS full filepath."""
|
||||
|
||||
return os.path.join(dir_path, filename)
|
||||
|
||||
|
||||
def format_output_file(name) -> str:
|
||||
"""Format the name for the result file."""
|
||||
|
||||
return f'{name}.json'
|
||||
|
||||
|
||||
def save_json(destination, data) -> None:
|
||||
"""Save data from memory to a json destination in disk."""
|
||||
|
||||
try:
|
||||
with open(destination, 'w', encoding='utf-8') as outfile:
|
||||
json.dump(data, outfile, indent=4)
|
||||
|
||||
except (IOError, TypeError) as e:
|
||||
log_error(f'Could not save {destination}: {e}')
|
||||
|
||||
|
||||
def create_dir(result_dir) -> None:
|
||||
"""Check whether a directory exists and create it if needed."""
|
||||
|
||||
try:
|
||||
if not os.path.isdir(result_dir):
|
||||
os.mkdir(result_dir)
|
||||
|
||||
except OSError as e:
|
||||
log_error(f'Could not create {result_dir}: {e}')
|
||||
|
||||
|
||||
def set_output(env_vars, input_file) -> str:
|
||||
"""Create an output destination to save solutions."""
|
||||
|
||||
try:
|
||||
output_dir = env_vars['OUTPUT_DIR']
|
||||
create_dir(output_dir)
|
||||
|
||||
output_str = input_file.split('_')[1].split('.json')[0]
|
||||
output_file_str = env_vars['OUTPUT_FILE_STR']
|
||||
output_file = output_file_str.format(output_str)
|
||||
return format_path(output_dir, output_file)
|
||||
|
||||
except (TypeError, KeyError) as e:
|
||||
exit_with_error(f'Could not format output file: {e}')
|
||||
|
||||
|
||||
def deep_copy(dict_to_clone) -> dict:
|
||||
"""Deep copy (not reference copy) to a dict."""
|
||||
|
||||
return copy.deepcopy(dict_to_clone)
|
||||
|
||||
|
||||
def exit_with_error(message) -> None:
|
||||
"""Log an error message and halt the program."""
|
||||
|
||||
log_error(message)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def run_exec(command) -> None:
|
||||
"""Exec a bash commnad (remember: not safe!)."""
|
||||
|
||||
try:
|
||||
this_command = subprocess.run(command)
|
||||
log_info(f'Exit code: {this_command.returncode}')
|
||||
except Exception as e:
|
||||
log_error(f'Error running {command}: {e}')
|
31
web3_python_toolkit/scripts/utils/plots.py
Normal file
31
web3_python_toolkit/scripts/utils/plots.py
Normal file
|
@ -0,0 +1,31 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
# This class implements plot scripts
|
||||
# author: steinkirch
|
||||
|
||||
import pandas as pd
|
||||
from utils.os import exit_with_error
|
||||
|
||||
|
||||
def open_csv(filepath) -> dict:
|
||||
"""Load and parse a csv file."""
|
||||
|
||||
try:
|
||||
return pd.read_csv(filepath)
|
||||
except (IOError, FileNotFoundError, TypeError) as e:
|
||||
exit_with_error(f'Failed to parse: "{filepath}": {e}')
|
||||
|
||||
|
||||
def save_csv(destination, data, index=False) -> None:
|
||||
"""Save data from memory to a csv destination in disk."""
|
||||
|
||||
try:
|
||||
data.to_csv(destination, index=index)
|
||||
|
||||
except (IOError, TypeError) as e:
|
||||
log_error(f'Could not save {destination}: {e}')
|
||||
|
||||
|
||||
def plot_bar(y, x) -> None:
|
||||
"""Simplest plot for two sets."""
|
||||
df = pd.DataFrame(y, index=x)
|
||||
df.plot.bar(rot=0, subplots=True)
|
40
web3_python_toolkit/scripts/utils/strings.py
Normal file
40
web3_python_toolkit/scripts/utils/strings.py
Normal file
|
@ -0,0 +1,40 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
# This class implements string methods used by the other classes.
|
||||
# author: steinkirch
|
||||
|
||||
from pprint import PrettyPrinter
|
||||
|
||||
from utils.os import log_error
|
||||
from utils.arithmetics import to_decimal
|
||||
|
||||
|
||||
def to_decimal_str(value) -> str:
|
||||
"""Format a reserve amount to a suitable string."""
|
||||
|
||||
return str(to_decimal(value))
|
||||
|
||||
|
||||
def to_wei_str(value, decimals=None) -> str:
|
||||
"""Parse an order string to wei value."""
|
||||
|
||||
decimals = decimals or 18
|
||||
try:
|
||||
return str(value)[:-decimals] + '_' + str(value)[-decimals:]
|
||||
except ValueError as e:
|
||||
log_error(f'Cannot convert to wei: {e}')
|
||||
|
||||
|
||||
def to_solution(value) -> str:
|
||||
"""Format decimal wei with an underscore for easier reading."""
|
||||
|
||||
return to_wei_str(to_decimal_str(value))
|
||||
|
||||
|
||||
def pprint(data, indent=None) -> None:
|
||||
"""Print dicts and data in a suitable format"""
|
||||
|
||||
print()
|
||||
indent = indent or 4
|
||||
pp = PrettyPrinter(indent=indent)
|
||||
pp.pprint(data)
|
||||
print()
|
67
web3_python_toolkit/scripts/utils/web3_wrapper.py
Normal file
67
web3_python_toolkit/scripts/utils/web3_wrapper.py
Normal file
|
@ -0,0 +1,67 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
# This class implements an (ongoing) wrapper for web3 libs.
|
||||
# author: steinkirch
|
||||
|
||||
from web3 import Web3, HTTPProvider, WebsocketProvider, IPCProvider
|
||||
from web3.middleware import geth_poa_middleware
|
||||
from utils.os import log_info
|
||||
|
||||
|
||||
class Web3Wrapper():
|
||||
|
||||
def __init__(self, mode, network):
|
||||
self.mode = mode
|
||||
self.network = network
|
||||
|
||||
self.w3 = None
|
||||
self.pair_contract = None
|
||||
|
||||
self._setup()
|
||||
|
||||
##################
|
||||
# PRIVATE METHODS
|
||||
##################
|
||||
def _setup(self) -> None:
|
||||
self._get_web3_object()
|
||||
|
||||
def _get_web3_object(self) -> None:
|
||||
log_info(f'Setting mode {self.mode} for {self.network}')
|
||||
if self.mode == 'http' or self.mode == 'local_http':
|
||||
self.w3 = Web3(HTTPProvider(self.network))
|
||||
elif self.mode == 'ws' or self.mode == 'local_ws':
|
||||
self.w3 = Web3(WebsocketProvider(self.network))
|
||||
elif self.mode == 'ipc' or self.mode == 'local_ipc':
|
||||
self.w3 = Web3(IPCProvider(self.network))
|
||||
else:
|
||||
log_info(f'Provider type is invalid: {self.mode}. Fix .env.')
|
||||
|
||||
|
||||
##################
|
||||
# BLOCK METHODS
|
||||
##################
|
||||
def get_block(self, block_number='latest') -> dict:
|
||||
return dict(self.w3.eth.get_block(block_number))
|
||||
|
||||
|
||||
##################
|
||||
# LP PAIR METHODS
|
||||
##################
|
||||
def get_pair_contract(self, address, abi) -> str:
|
||||
self.pair_contract = self.w3.eth.contract(address=address, abi=abi)
|
||||
|
||||
def inject_middleware(self, layer=0) -> None:
|
||||
self.w3.middleware_onion.inject(geth_poa_middleware,
|
||||
layer=layer)
|
||||
|
||||
def get_reserves(self, block) -> list:
|
||||
return self.pair_contract.functions.getReserves().call({}, block)[:2]
|
||||
|
||||
|
||||
##############
|
||||
# TX METHODS
|
||||
##############
|
||||
def get_tx(self, tx) -> dict:
|
||||
return dict(self.w3.eth.get_transaction(tx))
|
||||
|
||||
def get_tx_receipt(self, tx) -> dict:
|
||||
return dict(self.w3.eth.get_transaction_receipt(tx))
|
Loading…
Add table
Add a link
Reference in a new issue