148 lines
4.4 KiB
Python
148 lines
4.4 KiB
Python
import json
|
|
import time
|
|
from threading import Thread
|
|
|
|
from web3.utils.events import get_event_data
|
|
|
|
from ..utils.getWeb3 import getWeb3
|
|
|
|
|
|
class Contract(object):
|
|
'''Base class for interfacing with a contract'''
|
|
|
|
def __init__(self, keystore, address, abi_file, endpoint):
|
|
w3 = getWeb3(endpoint)
|
|
with open(abi_file) as f:
|
|
abi = json.load(f)['abiDefinition']
|
|
contract = w3.eth.contract(abi=abi, address=address)
|
|
self.w3 = w3
|
|
|
|
if keystore is not None:
|
|
self.account = self.to_account(keystore)
|
|
self.contract = contract
|
|
|
|
def to_account(self, data):
|
|
account = self.w3.eth.account.privateKeyToAccount(data)
|
|
del data
|
|
return account
|
|
|
|
def sign_and_send(self, func, args, value=0, gas=1000000):
|
|
''' Expecting all arguments in 1 array '''
|
|
signed_tx = self._sign_function_call(
|
|
func,
|
|
args,
|
|
value,
|
|
# may need to change gas
|
|
gas,
|
|
)
|
|
|
|
try:
|
|
tx_hash, gas_used = self._send_raw_tx(signed_tx)
|
|
except Exception as e:
|
|
print('FAILURE: ', e)
|
|
info = 'Failed: {}, Args: {}'.format(func.__name__, args)
|
|
print(info)
|
|
return tx_hash, gas_used
|
|
|
|
def send_transaction(self, to, value):
|
|
signed_tx = self._sign_transaction(to, value)
|
|
return self._send_raw_tx(signed_tx)
|
|
|
|
def _sign_transaction(self, to, value):
|
|
gas = 21000
|
|
gasPrice = self.w3.toWei('10', 'gwei')
|
|
|
|
raw_tx = {
|
|
'chainId': int(self.w3.version.network),
|
|
'to': self.w3.toChecksumAddress(to),
|
|
'value': value,
|
|
'gas': gas,
|
|
'gasPrice': gasPrice,
|
|
'nonce': self.w3.eth.getTransactionCount(self.account.address),
|
|
}
|
|
|
|
# print(raw_tx)
|
|
|
|
signed_tx = self.account.signTransaction(raw_tx)
|
|
|
|
return signed_tx
|
|
|
|
def _sign_function_call(self, func, args, value, gas):
|
|
"""
|
|
Takes reading and timestamp and creates a
|
|
raw transaction call to `ping` at the target contract
|
|
TODO: Add option to modify gas
|
|
"""
|
|
# Build the raw transaction
|
|
raw_tx = func(*args).buildTransaction(
|
|
{
|
|
'gas': gas,
|
|
'value': value,
|
|
'nonce': self.w3.eth.getTransactionCount(self.account.address),
|
|
}
|
|
)
|
|
raw_tx['to'] = self.w3.toChecksumAddress(raw_tx['to'])
|
|
|
|
# Sign the transaction with the meter's private key
|
|
signed_tx = self.account.signTransaction(raw_tx)
|
|
|
|
return signed_tx
|
|
|
|
def _send_raw_tx(self, signed_tx):
|
|
tx_hash = self.w3.eth.sendRawTransaction(signed_tx.rawTransaction)
|
|
gas_used = self.waitForTxReceipt(tx_hash)['gasUsed']
|
|
return tx_hash, gas_used
|
|
|
|
def waitForTxReceipt(self, tx):
|
|
receipt = self.w3.eth.getTransactionReceipt(tx)
|
|
while receipt is None:
|
|
time.sleep(1) # Block time avg
|
|
receipt = self.w3.eth.getTransactionReceipt(tx)
|
|
return receipt
|
|
|
|
def get_event_data(self, event_name, tx_hash):
|
|
tx_logs = self.w3.eth.getTransactionReceipt(tx_hash)['logs']
|
|
event_abi = self.contract._find_matching_event_abi(event_name)
|
|
matched = []
|
|
for log in tx_logs:
|
|
try:
|
|
d = get_event_data(event_abi, log)
|
|
except Exception as e:
|
|
continue
|
|
matched.append(d)
|
|
return matched
|
|
|
|
def watch_event(
|
|
self,
|
|
event_name,
|
|
callback,
|
|
interval,
|
|
fromBlock=0,
|
|
toBlock='latest',
|
|
filters=None,
|
|
):
|
|
event_filter = self.install_filter(
|
|
event_name, fromBlock, toBlock, filters
|
|
)
|
|
Thread(
|
|
target=self.watcher,
|
|
args=(event_filter, callback, interval),
|
|
daemon=True,
|
|
).start()
|
|
return event_filter
|
|
|
|
def watcher(self, event_filter, callback, interval):
|
|
while True:
|
|
for event in event_filter.get_new_entries():
|
|
callback(event)
|
|
time.sleep(interval)
|
|
|
|
def install_filter(
|
|
self, event_name, fromBlock=0, toBlock='latest', filters=None
|
|
):
|
|
event = getattr(self.contract.events, event_name)
|
|
eventFilter = event.createFilter(
|
|
fromBlock=fromBlock, toBlock=toBlock, argument_filters=filters
|
|
)
|
|
return eventFilter
|