visual-identity/plasma_cash/contract_binds/base/contract.py

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