Rehabilitate transfer, approve
Signed-off-by: nolash <dev@holbrook.no>
This commit is contained in:
parent
c245a29a6b
commit
3f61a2007e
@ -161,7 +161,7 @@ class NonceReservation(SessionBase):
|
||||
o = q.first()
|
||||
|
||||
if o == None:
|
||||
raise IntegrityError('nonce {} for key {} address {}: {}'.format(nonce, key, address))
|
||||
raise IntegrityError('nonce {} for key {} address {}'.format(nonce, key, address))
|
||||
SessionBase.release_session(session)
|
||||
|
||||
session.delete(o)
|
||||
|
@ -46,80 +46,8 @@ from cic_eth.queue.tx import (
|
||||
register_tx,
|
||||
)
|
||||
|
||||
#logg = logging.getLogger(__name__)
|
||||
logg = logging.getLogger()
|
||||
celery_app = celery.current_app
|
||||
#celery_app.log.setup_task_loggers(loglevel=logging.DEBUG)
|
||||
#celery_app.log.redirect_stdouts_to_logger(logg, loglevel=logging.DEBUG)
|
||||
|
||||
|
||||
#class AccountTxFactory(TxFactory):
|
||||
# """Factory for creating account index contract transactions
|
||||
# """
|
||||
# def add(
|
||||
# self,
|
||||
# address,
|
||||
# chain_spec,
|
||||
# uuid,
|
||||
# session=None,
|
||||
# ):
|
||||
# """Register an Ethereum account address with the on-chain account registry
|
||||
#
|
||||
# :param address: Ethereum account address to add
|
||||
# :type address: str, 0x-hex
|
||||
# :param chain_spec: Chain to build transaction for
|
||||
# :type chain_spec: cic_registry.chain.ChainSpec
|
||||
# :returns: Unsigned "AccountRegistry.add" transaction in standard Ethereum format
|
||||
# :rtype: dict
|
||||
# """
|
||||
#
|
||||
# c = self.registry.get_contract(chain_spec, 'AccountRegistry')
|
||||
# f = c.function('add')
|
||||
# tx_add_buildable = f(
|
||||
# address,
|
||||
# )
|
||||
# gas = c.gas('add')
|
||||
# tx_add = tx_add_buildable.buildTransaction({
|
||||
# 'from': self.address,
|
||||
# 'gas': gas,
|
||||
# 'gasPrice': self.gas_price,
|
||||
# 'chainId': chain_spec.chain_id(),
|
||||
# 'nonce': self.next_nonce(uuid, session=session),
|
||||
# 'value': 0,
|
||||
# })
|
||||
# return tx_add
|
||||
#
|
||||
#
|
||||
# def gift(
|
||||
# self,
|
||||
# address,
|
||||
# chain_spec,
|
||||
# uuid,
|
||||
# session=None,
|
||||
# ):
|
||||
# """Trigger the on-chain faucet to disburse tokens to the provided Ethereum account
|
||||
#
|
||||
# :param address: Ethereum account address to gift to
|
||||
# :type address: str, 0x-hex
|
||||
# :param chain_spec: Chain to build transaction for
|
||||
# :type chain_spec: cic_registry.chain.ChainSpec
|
||||
# :returns: Unsigned "Faucet.giveTo" transaction in standard Ethereum format
|
||||
# :rtype: dict
|
||||
# """
|
||||
#
|
||||
# c = self.registry.get_contract(chain_spec, 'Faucet')
|
||||
# f = c.function('giveTo')
|
||||
# tx_add_buildable = f(address)
|
||||
# gas = c.gas('add')
|
||||
# tx_add = tx_add_buildable.buildTransaction({
|
||||
# 'from': self.address,
|
||||
# 'gas': gas,
|
||||
# 'gasPrice': self.gas_price,
|
||||
# 'chainId': chain_spec.chain_id(),
|
||||
# 'nonce': self.next_nonce(uuid, session=session),
|
||||
# 'value': 0,
|
||||
# })
|
||||
# return tx_add
|
||||
|
||||
|
||||
def unpack_register(data):
|
||||
@ -183,16 +111,11 @@ def create(self, password, chain_str):
|
||||
o = new_account()
|
||||
a = conn.do(o)
|
||||
|
||||
#try:
|
||||
# a = c.w3.eth.personal.new_account(password)
|
||||
#except FileNotFoundError:
|
||||
# pass
|
||||
if a == None:
|
||||
raise SignerError('create account')
|
||||
logg.debug('created account {}'.format(a))
|
||||
|
||||
# Initialize nonce provider record for account
|
||||
#session = SessionBase.create_session()
|
||||
session = self.create_session()
|
||||
Nonce.init(a, session=session)
|
||||
session.commit()
|
||||
@ -217,7 +140,6 @@ def register(self, account_address, chain_spec_dict, writer_address=None):
|
||||
chain_spec = ChainSpec.from_dict(chain_spec_dict)
|
||||
|
||||
session = self.create_session()
|
||||
#session = SessionBase.create_session()
|
||||
if writer_address == None:
|
||||
writer_address = AccountRole.get_address('ACCOUNT_REGISTRY_WRITER', session=session)
|
||||
|
||||
@ -243,7 +165,6 @@ def register(self, account_address, chain_spec_dict, writer_address=None):
|
||||
(tx_hash_hex, tx_signed_raw_hex) = account_registry.add(account_registry_address, writer_address, account_address, tx_format=TxFormat.RLP_SIGNED)
|
||||
# TODO: if cache task fails, task chain will not return
|
||||
cache_task = 'cic_eth.eth.account.cache_account_data'
|
||||
cache_task = None
|
||||
|
||||
# add transaction to queue
|
||||
register_tx(tx_hash_hex, tx_signed_raw_hex, chain_spec, queue, cache_task=cache_task, session=session)
|
||||
|
@ -10,6 +10,10 @@ from chainlib.chain import ChainSpec
|
||||
from chainlib.status import Status as TxStatus
|
||||
from chainlib.connection import RPCConnection
|
||||
from chainlib.eth.erc20 import ERC20
|
||||
from chainlib.eth.tx import (
|
||||
TxFormat,
|
||||
unpack,
|
||||
)
|
||||
from cic_eth_registry.erc20 import ERC20Token
|
||||
from hexathon import strip_0x
|
||||
|
||||
@ -20,15 +24,18 @@ from cic_eth.db.models.base import SessionBase
|
||||
from cic_eth.eth import RpcClient
|
||||
from cic_eth.error import TokenCountError, PermanentTxError, OutOfGasError, NotLocalTxError
|
||||
from cic_eth.queue.tx import register_tx
|
||||
from cic_eth.eth.gas import create_check_gas_task
|
||||
from cic_eth.eth.gas import (
|
||||
create_check_gas_task,
|
||||
MaxGasOracle,
|
||||
)
|
||||
#from cic_eth.eth.factory import TxFactory
|
||||
from cic_eth.eth.util import unpack_signed_raw_tx
|
||||
from cic_eth.ext.address import translate_address
|
||||
from cic_eth.task import (
|
||||
CriticalSQLAlchemyTask,
|
||||
CriticalWeb3Task,
|
||||
CriticalSQLAlchemyAndSignerTask,
|
||||
)
|
||||
from cic_eth.eth.nonce import CustodialTaskNonceOracle
|
||||
|
||||
celery_app = celery.current_app
|
||||
logg = logging.getLogger()
|
||||
@ -87,7 +94,7 @@ logg = logging.getLogger()
|
||||
# def transfer(
|
||||
# self,
|
||||
# token_address,
|
||||
# receiver_address,
|
||||
# Receiver_address,
|
||||
# value,
|
||||
# chain_spec,
|
||||
# uuid,
|
||||
@ -125,68 +132,68 @@ logg = logging.getLogger()
|
||||
# return tx_transfer
|
||||
|
||||
|
||||
def unpack_transfer(data):
|
||||
"""Verifies that a transaction is an "ERC20.transfer" transaction, and extracts call parameters from it.
|
||||
|
||||
:param data: Raw input data from Ethereum transaction.
|
||||
:type data: str, 0x-hex
|
||||
:raises ValueError: Function signature does not match AccountRegister.add
|
||||
:returns: Parsed parameters
|
||||
:rtype: dict
|
||||
"""
|
||||
data = strip_0x(data)
|
||||
f = data[:8]
|
||||
if f != contract_function_signatures['transfer']:
|
||||
raise ValueError('Invalid transfer data ({})'.format(f))
|
||||
|
||||
d = data[8:]
|
||||
return {
|
||||
'to': web3.Web3.toChecksumAddress('0x' + d[64-40:64]),
|
||||
'amount': int(d[64:], 16)
|
||||
}
|
||||
#def unpack_transfer(data):
|
||||
# """Verifies that a transaction is an "ERC20.transfer" transaction, and extracts call parameters from it.
|
||||
#
|
||||
# :param data: Raw input data from Ethereum transaction.
|
||||
# :type data: str, 0x-hex
|
||||
# :raises ValueError: Function signature does not match AccountRegister.add
|
||||
# :returns: Parsed parameters
|
||||
# :rtype: dict
|
||||
# """
|
||||
# data = strip_0x(data)
|
||||
# f = data[:8]
|
||||
# if f != contract_function_signatures['transfer']:
|
||||
# raise ValueError('Invalid transfer data ({})'.format(f))
|
||||
#
|
||||
# d = data[8:]
|
||||
# return {
|
||||
# 'to': web3.Web3.toChecksumAddress('0x' + d[64-40:64]),
|
||||
# 'amount': int(d[64:], 16)
|
||||
# }
|
||||
|
||||
|
||||
def unpack_transferfrom(data):
|
||||
"""Verifies that a transaction is an "ERC20.transferFrom" transaction, and extracts call parameters from it.
|
||||
|
||||
:param data: Raw input data from Ethereum transaction.
|
||||
:type data: str, 0x-hex
|
||||
:raises ValueError: Function signature does not match AccountRegister.add
|
||||
:returns: Parsed parameters
|
||||
:rtype: dict
|
||||
"""
|
||||
data = strip_0x(data)
|
||||
f = data[:8]
|
||||
if f != contract_function_signatures['transferfrom']:
|
||||
raise ValueError('Invalid transferFrom data ({})'.format(f))
|
||||
|
||||
d = data[8:]
|
||||
return {
|
||||
'from': web3.Web3.toChecksumAddress('0x' + d[64-40:64]),
|
||||
'to': web3.Web3.toChecksumAddress('0x' + d[128-40:128]),
|
||||
'amount': int(d[128:], 16)
|
||||
}
|
||||
|
||||
|
||||
def unpack_approve(data):
|
||||
"""Verifies that a transaction is an "ERC20.approve" transaction, and extracts call parameters from it.
|
||||
|
||||
:param data: Raw input data from Ethereum transaction.
|
||||
:type data: str, 0x-hex
|
||||
:raises ValueError: Function signature does not match AccountRegister.add
|
||||
:returns: Parsed parameters
|
||||
:rtype: dict
|
||||
"""
|
||||
data = strip_0x(data)
|
||||
f = data[:8]
|
||||
if f != contract_function_signatures['approve']:
|
||||
raise ValueError('Invalid approval data ({})'.format(f))
|
||||
|
||||
d = data[8:]
|
||||
return {
|
||||
'to': web3.Web3.toChecksumAddress('0x' + d[64-40:64]),
|
||||
'amount': int(d[64:], 16)
|
||||
}
|
||||
#def unpack_transferfrom(data):
|
||||
# """Verifies that a transaction is an "ERC20.transferFrom" transaction, and extracts call parameters from it.
|
||||
#
|
||||
# :param data: Raw input data from Ethereum transaction.
|
||||
# :type data: str, 0x-hex
|
||||
# :raises ValueError: Function signature does not match AccountRegister.add
|
||||
# :returns: Parsed parameters
|
||||
# :rtype: dict
|
||||
# """
|
||||
# data = strip_0x(data)
|
||||
# f = data[:8]
|
||||
# if f != contract_function_signatures['transferfrom']:
|
||||
# raise ValueError('Invalid transferFrom data ({})'.format(f))
|
||||
#
|
||||
# d = data[8:]
|
||||
# return {
|
||||
# 'from': web3.Web3.toChecksumAddress('0x' + d[64-40:64]),
|
||||
# 'to': web3.Web3.toChecksumAddress('0x' + d[128-40:128]),
|
||||
# 'amount': int(d[128:], 16)
|
||||
# }
|
||||
#
|
||||
#
|
||||
#def unpack_approve(data):
|
||||
# """Verifies that a transaction is an "ERC20.approve" transaction, and extracts call parameters from it.
|
||||
#
|
||||
# :param data: Raw input data from Ethereum transaction.
|
||||
# :type data: str, 0x-hex
|
||||
# :raises ValueError: Function signature does not match AccountRegister.add
|
||||
# :returns: Parsed parameters
|
||||
# :rtype: dict
|
||||
# """
|
||||
# data = strip_0x(data)
|
||||
# f = data[:8]
|
||||
# if f != contract_function_signatures['approve']:
|
||||
# raise ValueError('Invalid approval data ({})'.format(f))
|
||||
#
|
||||
# d = data[8:]
|
||||
# return {
|
||||
# 'to': web3.Web3.toChecksumAddress('0x' + d[64-40:64]),
|
||||
# 'amount': int(d[64:], 16)
|
||||
# }
|
||||
|
||||
|
||||
@celery_app.task(base=CriticalWeb3Task)
|
||||
@ -197,8 +204,8 @@ def balance(tokens, holder_address, chain_spec_dict):
|
||||
:type tokens: list of str, 0x-hex
|
||||
:param holder_address: Token holder address
|
||||
:type holder_address: str, 0x-hex
|
||||
:param chain_str: Chain spec string representation
|
||||
:type chain_str: str
|
||||
:param chain_spec_dict: Chain spec string representation
|
||||
:type chain_spec_dict: str
|
||||
:return: List of balances
|
||||
:rtype: list of int
|
||||
"""
|
||||
@ -218,7 +225,7 @@ def balance(tokens, holder_address, chain_spec_dict):
|
||||
|
||||
|
||||
@celery_app.task(bind=True, base=CriticalSQLAlchemyAndSignerTask)
|
||||
def transfer(self, tokens, holder_address, receiver_address, value, chain_str):
|
||||
def transfer(self, tokens, holder_address, receiver_address, value, chain_spec_dict):
|
||||
"""Transfer ERC20 tokens between addresses
|
||||
|
||||
First argument is a list of tokens, to enable the task to be chained to the symbol to token address resolver function. However, it accepts only one token as argument.
|
||||
@ -242,29 +249,31 @@ def transfer(self, tokens, holder_address, receiver_address, value, chain_str):
|
||||
# we only allow one token, one transfer
|
||||
if len(tokens) != 1:
|
||||
raise TokenCountError
|
||||
|
||||
chain_spec = ChainSpec.from_chain_str(chain_str)
|
||||
|
||||
queue = self.request.delivery_info['routing_key']
|
||||
|
||||
# retrieve the token interface
|
||||
t = tokens[0]
|
||||
chain_spec = ChainSpec.from_dict(chain_spec_dict)
|
||||
queue = self.request.delivery_info.get('routing_key')
|
||||
|
||||
c = RpcClient(chain_spec, holder_address=holder_address)
|
||||
registry = safe_registry(c.w3)
|
||||
rpc = RPCConnection.connect(chain_spec, 'default')
|
||||
rpc_signer = RPCConnection.connect(chain_spec, 'signer')
|
||||
|
||||
txf = TokenTxFactory(holder_address, c, registry=registry)
|
||||
session = self.create_session()
|
||||
nonce_oracle = CustodialTaskNonceOracle(holder_address, self.request.root_id, session=session)
|
||||
gas_oracle = self.create_gas_oracle(rpc, MaxGasOracle.gas)
|
||||
c = ERC20(signer=rpc_signer, gas_oracle=gas_oracle, nonce_oracle=nonce_oracle, chain_id=chain_spec.chain_id())
|
||||
(tx_hash_hex, tx_signed_raw_hex) = c.transfer(t['address'], holder_address, receiver_address, value, tx_format=TxFormat.RLP_SIGNED)
|
||||
cache_task = 'cic_eth.eth.erc20.cache_transfer_data'
|
||||
|
||||
session = SessionBase.create_session()
|
||||
tx_transfer = txf.transfer(t['address'], receiver_address, value, chain_spec, self.request.root_id, session=session)
|
||||
(tx_hash_hex, tx_signed_raw_hex) = sign_and_register_tx(tx_transfer, chain_str, queue, cache_task='cic_eth.eth.token.otx_cache_transfer', session=session)
|
||||
register_tx(tx_hash_hex, tx_signed_raw_hex, chain_spec, queue, cache_task=cache_task, session=session)
|
||||
session.commit()
|
||||
session.close()
|
||||
|
||||
gas_budget = tx_transfer['gas'] * tx_transfer['gasPrice']
|
||||
gas_pair = gas_oracle.get_gas(tx_signed_raw_hex)
|
||||
gas_budget = gas_pair[0] * gas_pair[1]
|
||||
logg.debug('transfer tx {} {} {}'.format(tx_hash_hex, queue, gas_budget))
|
||||
|
||||
s = create_check_gas_and_send_task(
|
||||
s = create_check_gas_task(
|
||||
[tx_signed_raw_hex],
|
||||
chain_str,
|
||||
chain_spec,
|
||||
holder_address,
|
||||
gas_budget,
|
||||
[tx_hash_hex],
|
||||
@ -275,7 +284,7 @@ def transfer(self, tokens, holder_address, receiver_address, value, chain_str):
|
||||
|
||||
|
||||
@celery_app.task(bind=True, base=CriticalSQLAlchemyAndSignerTask)
|
||||
def approve(self, tokens, holder_address, spender_address, value, chain_str):
|
||||
def approve(self, tokens, holder_address, spender_address, value, chain_spec_dict):
|
||||
"""Approve ERC20 transfer on behalf of holder address
|
||||
|
||||
First argument is a list of tokens, to enable the task to be chained to the symbol to token address resolver function. However, it accepts only one token as argument.
|
||||
@ -299,29 +308,30 @@ def approve(self, tokens, holder_address, spender_address, value, chain_str):
|
||||
# we only allow one token, one transfer
|
||||
if len(tokens) != 1:
|
||||
raise TokenCountError
|
||||
|
||||
chain_spec = ChainSpec.from_chain_str(chain_str)
|
||||
|
||||
queue = self.request.delivery_info['routing_key']
|
||||
|
||||
# retrieve the token interface
|
||||
t = tokens[0]
|
||||
chain_spec = ChainSpec.from_dict(chain_spec_dict)
|
||||
queue = self.request.delivery_info.get('routing_key')
|
||||
|
||||
c = RpcClient(chain_spec, holder_address=holder_address)
|
||||
registry = safe_registry(c.w3)
|
||||
rpc = RPCConnection.connect(chain_spec, 'default')
|
||||
rpc_signer = RPCConnection.connect(chain_spec, 'signer')
|
||||
|
||||
txf = TokenTxFactory(holder_address, c, registry=registry)
|
||||
session = self.create_session()
|
||||
nonce_oracle = CustodialTaskNonceOracle(holder_address, self.request.root_id, session=session)
|
||||
gas_oracle = self.create_gas_oracle(rpc, MaxGasOracle.gas)
|
||||
c = ERC20(signer=rpc_signer, gas_oracle=gas_oracle, nonce_oracle=nonce_oracle, chain_id=chain_spec.chain_id())
|
||||
(tx_hash_hex, tx_signed_raw_hex) = c.approve(t['address'], holder_address, spender_address, value, tx_format=TxFormat.RLP_SIGNED)
|
||||
cache_task = 'cic_eth.eth.erc20.cache_approve_data'
|
||||
|
||||
session = SessionBase.create_session()
|
||||
tx_transfer = txf.approve(t['address'], spender_address, value, chain_spec, self.request.root_id, session=session)
|
||||
(tx_hash_hex, tx_signed_raw_hex) = sign_and_register_tx(tx_transfer, chain_str, queue, cache_task='cic_eth.eth.token.otx_cache_approve', session=session)
|
||||
register_tx(tx_hash_hex, tx_signed_raw_hex, chain_spec, queue, cache_task=cache_task, session=session)
|
||||
session.commit()
|
||||
session.close()
|
||||
|
||||
gas_budget = tx_transfer['gas'] * tx_transfer['gasPrice']
|
||||
gas_pair = gas_oracle.get_gas(tx_signed_raw_hex)
|
||||
gas_budget = gas_pair[0] * gas_pair[1]
|
||||
|
||||
s = create_check_gas_and_send_task(
|
||||
s = create_check_gas_task(
|
||||
[tx_signed_raw_hex],
|
||||
chain_str,
|
||||
chain_spec,
|
||||
holder_address,
|
||||
gas_budget,
|
||||
[tx_hash_hex],
|
||||
@ -356,34 +366,11 @@ def resolve_tokens_by_symbol(token_symbols, chain_str):
|
||||
return tokens
|
||||
|
||||
|
||||
@celery_app.task(base=CriticalSQLAlchemyTask)
|
||||
def otx_cache_transfer(
|
||||
tx_hash_hex,
|
||||
tx_signed_raw_hex,
|
||||
chain_str,
|
||||
):
|
||||
"""Generates and commits transaction cache metadata for an ERC20.transfer or ERC20.transferFrom transaction
|
||||
|
||||
:param tx_hash_hex: Transaction hash
|
||||
:type tx_hash_hex: str, 0x-hex
|
||||
:param tx_signed_raw_hex: Raw signed transaction
|
||||
:type tx_signed_raw_hex: str, 0x-hex
|
||||
:param chain_str: Chain spec string representation
|
||||
:type chain_str: str
|
||||
:returns: Transaction hash and id of cache element in storage backend, respectively
|
||||
:rtype: tuple
|
||||
"""
|
||||
chain_spec = ChainSpec.from_chain_str(chain_str)
|
||||
tx_signed_raw_bytes = bytes.fromhex(tx_signed_raw_hex[2:])
|
||||
tx = unpack_signed_raw_tx(tx_signed_raw_bytes, chain_spec.chain_id())
|
||||
(txc, cache_id) = cache_transfer_data(tx_hash_hex, tx)
|
||||
return txc
|
||||
|
||||
|
||||
@celery_app.task(base=CriticalSQLAlchemyTask)
|
||||
def cache_transfer_data(
|
||||
tx_hash_hex,
|
||||
tx,
|
||||
tx_signed_raw_hex,
|
||||
chain_spec_dict,
|
||||
):
|
||||
"""Helper function for otx_cache_transfer
|
||||
|
||||
@ -394,19 +381,23 @@ def cache_transfer_data(
|
||||
:returns: Transaction hash and id of cache element in storage backend, respectively
|
||||
:rtype: tuple
|
||||
"""
|
||||
tx_data = unpack_transfer(tx['data'])
|
||||
logg.debug('tx data {}'.format(tx_data))
|
||||
logg.debug('tx {}'.format(tx))
|
||||
chain_spec = ChainSpec.from_dict(chain_spec_dict)
|
||||
tx_signed_raw_bytes = bytes.fromhex(strip_0x(tx_signed_raw_hex))
|
||||
tx = unpack(tx_signed_raw_bytes, chain_spec.chain_id())
|
||||
|
||||
tx_data = ERC20.parse_transfer_request(tx['data'])
|
||||
recipient_address = tx_data[0]
|
||||
token_value = tx_data[1]
|
||||
|
||||
session = SessionBase.create_session()
|
||||
tx_cache = TxCache(
|
||||
tx_hash_hex,
|
||||
tx['from'],
|
||||
tx_data['to'],
|
||||
recipient_address,
|
||||
tx['to'],
|
||||
tx['to'],
|
||||
tx_data['amount'],
|
||||
tx_data['amount'],
|
||||
token_value,
|
||||
token_value,
|
||||
session=session,
|
||||
)
|
||||
session.add(tx_cache)
|
||||
@ -416,34 +407,11 @@ def cache_transfer_data(
|
||||
return (tx_hash_hex, cache_id)
|
||||
|
||||
|
||||
@celery_app.task(base=CriticalSQLAlchemyTask)
|
||||
def otx_cache_approve(
|
||||
tx_hash_hex,
|
||||
tx_signed_raw_hex,
|
||||
chain_str,
|
||||
):
|
||||
"""Generates and commits transaction cache metadata for an ERC20.approve transaction
|
||||
|
||||
:param tx_hash_hex: Transaction hash
|
||||
:type tx_hash_hex: str, 0x-hex
|
||||
:param tx_signed_raw_hex: Raw signed transaction
|
||||
:type tx_signed_raw_hex: str, 0x-hex
|
||||
:param chain_str: Chain spec string representation
|
||||
:type chain_str: str
|
||||
:returns: Transaction hash and id of cache element in storage backend, respectively
|
||||
:rtype: tuple
|
||||
"""
|
||||
chain_spec = ChainSpec.from_chain_str(chain_str)
|
||||
tx_signed_raw_bytes = bytes.fromhex(tx_signed_raw_hex[2:])
|
||||
tx = unpack_signed_raw_tx(tx_signed_raw_bytes, chain_spec.chain_id())
|
||||
(txc, cache_id) = cache_approve_data(tx_hash_hex, tx)
|
||||
return txc
|
||||
|
||||
|
||||
@celery_app.task(base=CriticalSQLAlchemyTask)
|
||||
def cache_approve_data(
|
||||
tx_hash_hex,
|
||||
tx,
|
||||
tx_signed_raw_hex,
|
||||
chain_spec_dict,
|
||||
):
|
||||
"""Helper function for otx_cache_approve
|
||||
|
||||
@ -454,19 +422,23 @@ def cache_approve_data(
|
||||
:returns: Transaction hash and id of cache element in storage backend, respectively
|
||||
:rtype: tuple
|
||||
"""
|
||||
tx_data = unpack_approve(tx['data'])
|
||||
logg.debug('tx data {}'.format(tx_data))
|
||||
logg.debug('tx {}'.format(tx))
|
||||
chain_spec = ChainSpec.from_dict(chain_spec_dict)
|
||||
tx_signed_raw_bytes = bytes.fromhex(strip_0x(tx_signed_raw_hex))
|
||||
tx = unpack(tx_signed_raw_bytes, chain_spec.chain_id())
|
||||
|
||||
tx_data = ERC20.parse_approve_request(tx['data'])
|
||||
recipient_address = tx_data[0]
|
||||
token_value = tx_data[1]
|
||||
|
||||
session = SessionBase.create_session()
|
||||
tx_cache = TxCache(
|
||||
tx_hash_hex,
|
||||
tx['from'],
|
||||
tx_data['to'],
|
||||
recipient_address,
|
||||
tx['to'],
|
||||
tx['to'],
|
||||
tx_data['amount'],
|
||||
tx_data['amount'],
|
||||
token_value,
|
||||
token_value,
|
||||
session=session,
|
||||
)
|
||||
session.add(tx_cache)
|
||||
|
@ -12,77 +12,83 @@ from cic_eth.db.models.base import SessionBase
|
||||
|
||||
logg = logging.getLogger()
|
||||
|
||||
|
||||
class GasOracle():
|
||||
"""Provides gas pricing for transactions.
|
||||
|
||||
:param w3: Web3 object
|
||||
:type w3: web3.Web3
|
||||
"""
|
||||
|
||||
__safe_threshold_amount_value = 2000000000 * 60000 * 3
|
||||
__refill_amount_value = __safe_threshold_amount_value * 5
|
||||
default_gas_limit = 21000
|
||||
|
||||
def __init__(self, conn):
|
||||
o = price()
|
||||
r = conn.do(o)
|
||||
b = bytes.from_hex(strip_0x(r))
|
||||
self.gas_price_current = int.from_bytes(b, 'big')
|
||||
|
||||
#self.w3 = w3
|
||||
#self.gas_price_current = w3.eth.gas_price()
|
||||
#
|
||||
#class GasOracle():
|
||||
# """Provides gas pricing for transactions.
|
||||
#
|
||||
# :param w3: Web3 object
|
||||
# :type w3: web3.Web3
|
||||
# """
|
||||
#
|
||||
# __safe_threshold_amount_value = 2000000000 * 60000 * 3
|
||||
# __refill_amount_value = __safe_threshold_amount_value * 5
|
||||
# default_gas_limit = 21000
|
||||
#
|
||||
# def __init__(self, conn):
|
||||
# o = price()
|
||||
# r = conn.do(o)
|
||||
# b = bytes.from_hex(strip_0x(r))
|
||||
# self.gas_price_current = int.from_bytes(b, 'big')
|
||||
#
|
||||
# #self.w3 = w3
|
||||
# #self.gas_price_current = w3.eth.gas_price()
|
||||
#
|
||||
#
|
||||
# def safe_threshold_amount(self):
|
||||
# """The gas balance threshold under which a new gas refill transaction should be initiated.
|
||||
#
|
||||
# :returns: Gas token amount
|
||||
# :rtype: number
|
||||
# """
|
||||
# g = GasOracle.__safe_threshold_amount_value
|
||||
# logg.warning('gas safe threshold is currently hardcoded to {}'.format(g))
|
||||
# return g
|
||||
#
|
||||
#
|
||||
# def refill_amount(self):
|
||||
# """The amount of gas tokens to send in a gas refill transaction.
|
||||
#
|
||||
# :returns: Gas token amount
|
||||
# :rtype: number
|
||||
# """
|
||||
# g = GasOracle.__refill_amount_value
|
||||
# logg.warning('gas refill amount is currently hardcoded to {}'.format(g))
|
||||
# return g
|
||||
#
|
||||
#
|
||||
# def gas_provider(self):
|
||||
# """Gas provider address.
|
||||
#
|
||||
# :returns: Etheerum account address
|
||||
# :rtype: str, 0x-hex
|
||||
# """
|
||||
# session = SessionBase.create_session()
|
||||
# a = AccountRole.get_address('GAS_GIFTER', session)
|
||||
# logg.debug('gasgifter {}'.format(a))
|
||||
# session.close()
|
||||
# return a
|
||||
#
|
||||
#
|
||||
# def gas_price(self, category='safe'):
|
||||
# """Get projected gas price to use for a transaction at the current moment.
|
||||
#
|
||||
# When the category parameter is implemented, it can be used to control the priority of a transaction in the network.
|
||||
#
|
||||
# :param category: Bid level category to return price for. Currently has no effect.
|
||||
# :type category: str
|
||||
# :returns: Gas price
|
||||
# :rtype: number
|
||||
# """
|
||||
# #logg.warning('gas price hardcoded to category "safe"')
|
||||
# #g = 100
|
||||
# #return g
|
||||
# return self.gas_price_current
|
||||
|
||||
|
||||
def safe_threshold_amount(self):
|
||||
"""The gas balance threshold under which a new gas refill transaction should be initiated.
|
||||
class MaxGasOracle:
|
||||
|
||||
:returns: Gas token amount
|
||||
:rtype: number
|
||||
"""
|
||||
g = GasOracle.__safe_threshold_amount_value
|
||||
logg.warning('gas safe threshold is currently hardcoded to {}'.format(g))
|
||||
return g
|
||||
|
||||
|
||||
def refill_amount(self):
|
||||
"""The amount of gas tokens to send in a gas refill transaction.
|
||||
|
||||
:returns: Gas token amount
|
||||
:rtype: number
|
||||
"""
|
||||
g = GasOracle.__refill_amount_value
|
||||
logg.warning('gas refill amount is currently hardcoded to {}'.format(g))
|
||||
return g
|
||||
|
||||
|
||||
def gas_provider(self):
|
||||
"""Gas provider address.
|
||||
|
||||
:returns: Etheerum account address
|
||||
:rtype: str, 0x-hex
|
||||
"""
|
||||
session = SessionBase.create_session()
|
||||
a = AccountRole.get_address('GAS_GIFTER', session)
|
||||
logg.debug('gasgifter {}'.format(a))
|
||||
session.close()
|
||||
return a
|
||||
|
||||
|
||||
def gas_price(self, category='safe'):
|
||||
"""Get projected gas price to use for a transaction at the current moment.
|
||||
|
||||
When the category parameter is implemented, it can be used to control the priority of a transaction in the network.
|
||||
|
||||
:param category: Bid level category to return price for. Currently has no effect.
|
||||
:type category: str
|
||||
:returns: Gas price
|
||||
:rtype: number
|
||||
"""
|
||||
#logg.warning('gas price hardcoded to category "safe"')
|
||||
#g = 100
|
||||
#return g
|
||||
return self.gas_price_current
|
||||
def gas(code=None):
|
||||
return 8000000
|
||||
|
||||
|
||||
def create_check_gas_task(tx_signed_raws_hex, chain_spec, holder_address, gas=None, tx_hashes_hex=None, queue=None):
|
||||
|
@ -1,39 +1,39 @@
|
||||
# standard imports
|
||||
import logging
|
||||
|
||||
# local imports
|
||||
from cic_eth.eth.gas import GasOracle
|
||||
|
||||
logg = logging.getLogger()
|
||||
|
||||
|
||||
class RpcClient(GasOracle):
|
||||
"""RPC wrapper for web3 enabling gas calculation helpers and signer middleware.
|
||||
class RpcClient:
|
||||
pass
|
||||
|
||||
:param chain_spec: Chain spec
|
||||
:type chain_spec: cic_registry.chain.ChainSpec
|
||||
:param holder_address: DEPRECATED Address of subject of the session.
|
||||
:type holder_address: str, 0x-hex
|
||||
"""
|
||||
|
||||
signer_ipc_path = None
|
||||
"""Unix socket path to JSONRPC signer and keystore"""
|
||||
|
||||
web3_constructor = None
|
||||
"""Custom function to build a web3 object with middleware plugins"""
|
||||
|
||||
|
||||
def __init__(self, chain_spec, holder_address=None):
|
||||
(self.provider, w3) = RpcClient.web3_constructor()
|
||||
super(RpcClient, self).__init__(w3)
|
||||
self.chain_spec = chain_spec
|
||||
if holder_address != None:
|
||||
self.holder_address = holder_address
|
||||
logg.info('gasprice {}'.format(self.gas_price()))
|
||||
|
||||
|
||||
@staticmethod
|
||||
def set_constructor(web3_constructor):
|
||||
"""Sets the constructor to use for building the web3 object.
|
||||
"""
|
||||
RpcClient.web3_constructor = web3_constructor
|
||||
#class RpcClient(GasOracle):
|
||||
# """RPC wrapper for web3 enabling gas calculation helpers and signer middleware.
|
||||
#
|
||||
# :param chain_spec: Chain spec
|
||||
# :type chain_spec: cic_registry.chain.ChainSpec
|
||||
# :param holder_address: DEPRECATED Address of subject of the session.
|
||||
# :type holder_address: str, 0x-hex
|
||||
# """
|
||||
#
|
||||
# signer_ipc_path = None
|
||||
# """Unix socket path to JSONRPC signer and keystore"""
|
||||
#
|
||||
# web3_constructor = None
|
||||
# """Custom function to build a web3 object with middleware plugins"""
|
||||
#
|
||||
#
|
||||
# def __init__(self, chain_spec, holder_address=None):
|
||||
# (self.provider, w3) = RpcClient.web3_constructor()
|
||||
# super(RpcClient, self).__init__(w3)
|
||||
# self.chain_spec = chain_spec
|
||||
# if holder_address != None:
|
||||
# self.holder_address = holder_address
|
||||
# logg.info('gasprice {}'.format(self.gas_price()))
|
||||
#
|
||||
#
|
||||
# @staticmethod
|
||||
# def set_constructor(web3_constructor):
|
||||
# """Sets the constructor to use for building the web3 object.
|
||||
# """
|
||||
# RpcClient.web3_constructor = web3_constructor
|
||||
|
@ -1,7 +1,6 @@
|
||||
pytest==6.0.1
|
||||
pytest-celery==0.0.0a1
|
||||
pytest-mock==3.3.1
|
||||
py-eth==0.1.1
|
||||
pytest-cov==2.10.1
|
||||
eth-tester==0.5.0b3
|
||||
py-evm==0.3.0a20
|
||||
|
@ -9,6 +9,7 @@ sys.path.insert(0, root_dir)
|
||||
|
||||
from tests.fixtures_config import *
|
||||
from tests.fixtures_database import *
|
||||
from tests.fixtures_celery import *
|
||||
from tests.fixtures_role import *
|
||||
from chainlib.eth.pytest import *
|
||||
from contract_registry.pytest import *
|
||||
|
@ -11,6 +11,7 @@ logg = logging.getLogger()
|
||||
# celery fixtures
|
||||
@pytest.fixture(scope='session')
|
||||
def celery_includes():
|
||||
logg.debug('foooooooooooo ')
|
||||
return [
|
||||
# 'cic_eth.eth.bancor',
|
||||
'cic_eth.eth.erc20',
|
||||
|
@ -18,14 +18,15 @@ logg = logging.getLogger()
|
||||
@pytest.fixture(scope='function')
|
||||
def custodial_roles(
|
||||
contract_roles,
|
||||
token_roles,
|
||||
eth_accounts,
|
||||
init_database,
|
||||
):
|
||||
r = {}
|
||||
r.update(contract_roles)
|
||||
r.update({
|
||||
'DEFAULT': eth_accounts[0],
|
||||
'GAS_GIFTER': eth_accounts[3],
|
||||
'GAS_GIFTER': eth_accounts[10],
|
||||
'FOO_TOKEN_GIFTER': token_roles['FOO_TOKEN_OWNER'],
|
||||
})
|
||||
for k in r.keys():
|
||||
role = AccountRole.set(k, r[k])
|
||||
|
@ -1,22 +1,65 @@
|
||||
# standard imports
|
||||
import logging
|
||||
|
||||
# external imports
|
||||
import pytest
|
||||
import celery
|
||||
from chainlib.eth.erc20 import ERC20
|
||||
from chainlib.eth.nonce import RPCNonceOracle
|
||||
from chainlib.eth.tx import receipt
|
||||
from chainlib.eth.tx import (
|
||||
receipt,
|
||||
TxFormat,
|
||||
)
|
||||
|
||||
# local imports
|
||||
from cic_eth.queue.tx import register_tx
|
||||
|
||||
logg = logging.getLogger()
|
||||
|
||||
|
||||
def test_erc20_balance(
|
||||
def test_otx_cache_transfer(
|
||||
default_chain_spec,
|
||||
foo_token,
|
||||
token_roles,
|
||||
agent_roles,
|
||||
eth_signer,
|
||||
eth_rpc,
|
||||
celery_worker,
|
||||
init_database,
|
||||
celery_session_worker,
|
||||
):
|
||||
nonce_oracle = RPCNonceOracle(token_roles['FOO_TOKEN_OWNER'], eth_rpc)
|
||||
c = ERC20(signer=eth_signer, nonce_oracle=nonce_oracle, chain_id=default_chain_spec.chain_id())
|
||||
transfer_value = 100 * (10**6)
|
||||
(tx_hash_hex, tx_signed_raw_hex) = c.transfer(foo_token, token_roles['FOO_TOKEN_OWNER'], agent_roles['ALICE'], transfer_value, tx_format=TxFormat.RLP_SIGNED)
|
||||
register_tx(tx_hash_hex, tx_signed_raw_hex, default_chain_spec, None, session=init_database)
|
||||
|
||||
s = celery.signature(
|
||||
'cic_eth.eth.erc20.cache_transfer_data',
|
||||
[
|
||||
tx_hash_hex,
|
||||
tx_signed_raw_hex,
|
||||
default_chain_spec.asdict(),
|
||||
],
|
||||
queue=None,
|
||||
)
|
||||
t = s.apply_async()
|
||||
r = t.get()
|
||||
|
||||
assert r[0] == tx_hash_hex
|
||||
|
||||
|
||||
def test_erc20_balance_task(
|
||||
default_chain_spec,
|
||||
foo_token,
|
||||
token_roles,
|
||||
agent_roles,
|
||||
eth_signer,
|
||||
eth_rpc,
|
||||
celery_session_worker,
|
||||
):
|
||||
|
||||
nonce_oracle = RPCNonceOracle(token_roles['FOO_TOKEN_OWNER'], eth_rpc)
|
||||
c = ERC20(signer=eth_signer, nonce_oracle=nonce_oracle)
|
||||
c = ERC20(signer=eth_signer, nonce_oracle=nonce_oracle, chain_id=default_chain_spec.chain_id())
|
||||
transfer_value = 100 * (10**6)
|
||||
(tx_hash_hex, o) = c.transfer(foo_token, token_roles['FOO_TOKEN_OWNER'], agent_roles['ALICE'], transfer_value)
|
||||
eth_rpc.do(o)
|
||||
@ -41,3 +84,84 @@ def test_erc20_balance(
|
||||
r = t.get()
|
||||
assert r[0]['balance_network'] == transfer_value
|
||||
|
||||
|
||||
def test_erc20_transfer_task(
|
||||
default_chain_spec,
|
||||
foo_token,
|
||||
agent_roles,
|
||||
custodial_roles,
|
||||
eth_signer,
|
||||
eth_rpc,
|
||||
init_database,
|
||||
celery_session_worker,
|
||||
):
|
||||
|
||||
token_object = {
|
||||
'address': foo_token,
|
||||
}
|
||||
transfer_value = 100 * (10 ** 6)
|
||||
|
||||
s_nonce = celery.signature(
|
||||
'cic_eth.eth.tx.reserve_nonce',
|
||||
[
|
||||
[token_object],
|
||||
custodial_roles['FOO_TOKEN_GIFTER'],
|
||||
],
|
||||
queue=None,
|
||||
)
|
||||
s_transfer = celery.signature(
|
||||
'cic_eth.eth.erc20.transfer',
|
||||
[
|
||||
custodial_roles['FOO_TOKEN_GIFTER'],
|
||||
agent_roles['ALICE'],
|
||||
transfer_value,
|
||||
default_chain_spec.asdict(),
|
||||
],
|
||||
queue=None,
|
||||
)
|
||||
s_nonce.link(s_transfer)
|
||||
t = s_nonce.apply_async()
|
||||
r = t.get_leaf()
|
||||
|
||||
logg.debug('result {}'.format(r))
|
||||
|
||||
|
||||
def test_erc20_approve_task(
|
||||
default_chain_spec,
|
||||
foo_token,
|
||||
agent_roles,
|
||||
custodial_roles,
|
||||
eth_signer,
|
||||
eth_rpc,
|
||||
init_database,
|
||||
celery_session_worker,
|
||||
):
|
||||
|
||||
token_object = {
|
||||
'address': foo_token,
|
||||
}
|
||||
transfer_value = 100 * (10 ** 6)
|
||||
|
||||
s_nonce = celery.signature(
|
||||
'cic_eth.eth.tx.reserve_nonce',
|
||||
[
|
||||
[token_object],
|
||||
custodial_roles['FOO_TOKEN_GIFTER'],
|
||||
],
|
||||
queue=None,
|
||||
)
|
||||
s_transfer = celery.signature(
|
||||
'cic_eth.eth.erc20.approve',
|
||||
[
|
||||
custodial_roles['FOO_TOKEN_GIFTER'],
|
||||
agent_roles['ALICE'],
|
||||
transfer_value,
|
||||
default_chain_spec.asdict(),
|
||||
],
|
||||
queue=None,
|
||||
)
|
||||
s_nonce.link(s_transfer)
|
||||
t = s_nonce.apply_async()
|
||||
r = t.get_leaf()
|
||||
|
||||
logg.debug('result {}'.format(r))
|
||||
|
Loading…
Reference in New Issue
Block a user