chainlib/chainlib/eth/gas.py

139 lines
4.0 KiB
Python

# standard imports
import logging
# third-party imports
from hexathon import (
add_0x,
strip_0x,
)
from crypto_dev_signer.eth.transaction import EIP155Transaction
# local imports
from chainlib.hash import keccak256_hex_to_hex
from chainlib.jsonrpc import jsonrpc_template
from chainlib.eth.tx import (
TxFactory,
TxFormat,
raw,
)
from chainlib.eth.constant import (
MINIMUM_FEE_UNITS,
)
logg = logging.getLogger(__name__)
def price():
o = jsonrpc_template()
o['method'] = 'eth_gasPrice'
return o
def balance(address):
o = jsonrpc_template()
o['method'] = 'eth_getBalance'
o['params'].append(address)
o['params'].append('latest')
return o
class Gas(TxFactory):
def create(self, sender_address, recipient_address, value, tx_format=TxFormat.JSONRPC):
tx = self.template(sender_address, recipient_address, use_nonce=True)
tx['value'] = value
txe = EIP155Transaction(tx, tx['nonce'], tx['chainId'])
tx_raw = self.signer.sign_transaction_to_rlp(txe)
tx_raw_hex = add_0x(tx_raw.hex())
tx_hash_hex = add_0x(keccak256_hex_to_hex(tx_raw_hex))
o = None
if tx_format == TxFormat.JSONRPC:
o = raw(tx_raw_hex)
elif tx_format == TxFormat.RLP_SIGNED:
o = tx_raw_hex
return (tx_hash_hex, o)
class RPCGasOracle:
def __init__(self, conn, code_callback=None, min_price=1):
self.conn = conn
self.code_callback = code_callback
self.min_price = min_price
def get_gas(self, code=None):
gas_price = 0
if self.conn != None:
o = price()
r = self.conn.do(o)
n = strip_0x(r)
gas_price = int(n, 16)
fee_units = MINIMUM_FEE_UNITS
if self.code_callback != None:
fee_units = self.code_callback(code)
if gas_price < self.min_price:
logg.debug('adjusting price {} to set minimum {}'.format(gas_price, self.min_price))
gas_price = self.min_price
return (gas_price, fee_units)
class RPCPureGasOracle(RPCGasOracle):
def __init__(self, conn, code_callback=None):
super(RPCPureGasOracle, self).__init__(conn, code_callback=code_callback, min_price=0)
class OverrideGasOracle(RPCGasOracle):
def __init__(self, price=None, limit=None, conn=None, code_callback=None):
self.conn = None
self.code_callback = None
self.limit = limit
self.price = price
price_conn = None
if self.limit == None or self.price == None:
if self.price == None:
price_conn = conn
logg.debug('override gas oracle with rpc fallback; price {} limit {}'.format(self.price, self.limit))
super(OverrideGasOracle, self).__init__(price_conn, code_callback)
def get_gas(self, code=None):
r = None
fee_units = None
fee_price = None
rpc_results = super(OverrideGasOracle, self).get_gas(code)
if self.limit != None:
fee_units = self.limit
if self.price != None:
fee_price = self.price
if fee_price == None:
if rpc_results != None:
fee_price = rpc_results[0]
logg.debug('override gas oracle without explicit price, setting from rpc {}'.format(fee_price))
else:
fee_price = MINIMUM_FEE_PRICE
logg.debug('override gas oracle without explicit price, setting default {}'.format(fee_price))
if fee_units == None:
if rpc_results != None:
fee_units = rpc_results[1]
logg.debug('override gas oracle without explicit limit, setting from rpc {}'.format(fee_units))
else:
fee_units = MINIMUM_FEE_UNITS
logg.debug('override gas oracle without explicit limit, setting default {}'.format(fee_units))
return (fee_price, fee_units)
DefaultGasOracle = RPCGasOracle