def _create_and_broadcast(self, tx: Swap) -> bool: # reacts to signed tx in the DB that are ready to be sent to secret20 signatures = [ signature.signed_tx for signature in Signatures.objects(tx_id=tx.id) ] if len(signatures ) < self.config['signatures_threshold']: # sanity check self.logger.error( msg=f"Tried to sign tx {tx.id}, without enough signatures" f" (required: {self.config['signatures_threshold']}, have: {len(signatures)})" ) return False try: signed_tx = self._create_multisig(tx.unsigned_tx, tx.sequence, signatures) scrt_tx_hash = self._broadcast(signed_tx) self.logger.info( f"Broadcasted {tx.id} successfully - {scrt_tx_hash}") tx.status = Status.SWAP_SUBMITTED tx.dst_tx_hash = scrt_tx_hash tx.save() self.logger.info(f"Changed status of tx {tx.id} to submitted") return True except (RuntimeError, OperationError) as e: self.logger.error( msg=f"Failed to create multisig and broadcast, error: {e}") tx.status = Status.SWAP_FAILED tx.save() return False
def _retry(self, tx: Swap): for signature in Signatures.objects(tx_id=tx.id): signature.delete() tx.status = Status.SWAP_UNSIGNED tx.sequence = self.sequence tx.save() self.sequence = self.sequence + 1
def _validate_and_sign(self, tx: Swap): """ Makes sure that the tx is valid and signs it :raises: ValueError """ if self._is_signed(tx): self.logger.debug( f"This signer already signed this transaction. Waiting for other signers... id:{tx.id}" ) return if not self._is_valid(tx): self.logger.error( f"Validation failed. Signer: {self.multisig.name}. Tx id:{tx.id}." ) tx.status = Status.SWAP_FAILED tx.save() raise ValueError try: signed_tx = self._sign_with_secret_cli(tx.unsigned_tx, tx.sequence) except RuntimeError as e: tx.status = Status.SWAP_FAILED tx.save() raise ValueError from e try: Signatures(tx_id=tx.id, signer=self.multisig.name, signed_tx=signed_tx).save() except OperationError as e: self.logger.error(f'Failed to save tx in database: {tx}') raise ValueError from e
def _handle(self, event: AttributeDict): """Extracts tx data from @event and add unsigned_tx to db""" if not self.contract.verify_destination(event): return amount = str(self.contract.extract_amount(event)) try: block_number, tx_hash, recipient, token = self.contract.parse_swap_event( event) if token is None: token = 'native' except ValueError: return try: s20 = self._get_s20(token) mint = mint_json(amount, tx_hash, recipient, s20.address) unsigned_tx = create_unsigned_tx(self.config.scrt_swap_address, mint, self.config.chain_id, self.config.enclave_key, self.config.swap_code_hash, self.multisig.address) tx = Swap(src_tx_hash=tx_hash, status=Status.SWAP_UNSIGNED, unsigned_tx=unsigned_tx, src_coin=token, dst_coin=s20.name, dst_address=s20.address, src_network="Ethereum", sequence=self.sequence, amount=amount) tx.save(force_insert=True) self.sequence = self.sequence + 1 self.logger.info( f"saved new Ethereum -> Secret transaction {tx_hash}, for {amount} {s20.name}" ) # SwapTrackerObject.update_last_processed(src=Source.ETH.value, update_val=block_number) except (IndexError, AttributeError, KeyError) as e: self.logger.error(f"Failed on tx {tx_hash}, block {block_number}, " f"due to error: {e}") except RuntimeError as e: self.logger.error( f"Failed to create swap tx for eth hash {tx_hash}, block {block_number}. Error: {e}" ) except NotUniqueError as e: self.logger.error( f"Tried to save duplicate TX, might be a catch up issue - {e}") # return block_number, tx_hash, recipient, s20 SwapTrackerObject.update_last_processed('Ethereum', block_number)
def _broadcast_and_save(self, msg: message.Submit, swap: Swap, swap_id: str): try: tx_hash = self._broadcast_transaction(msg) swap.dst_tx_hash = tx_hash swap.status = Status.SWAP_SUBMITTED self.pending_txs.append(swap_id) except (ValueError, TransactionNotFound) as e: self.logger.critical(f"Failed to broadcast transaction for msg {repr(msg)}: {e}") finally: try: swap.save() except (DuplicateKeyError, NotUniqueError): pass
def _set_retry(tx: Swap): tx.status = Status.SWAP_RETRY tx.save()
def _handle_swap(self, swap_data: str, src_token: str, dst_token: str): swap_json = swap_query_res(swap_data) # this is an id, and not the TX hash since we don't actually know where the TX happened, only the id of the # swap reported by the contract swap_id = get_swap_id(swap_json) dest_address = swap_json['destination'] self.logger.info(f'{swap_json}') amount = int(swap_json['amount']) if dst_token == 'native': data, tx_dest, tx_amount, tx_token, fee = self._tx_native_params( amount, dest_address) else: self.erc20.address = dst_token data, tx_dest, tx_amount, tx_token, fee = self._tx_erc20_params( amount, dest_address, dst_token) if not self._validate_fee(amount, fee): self.logger.error("Tried to swap an amount too low to cover fee") swap = Swap(src_network="Secret", src_tx_hash=swap_id, unsigned_tx=data, src_coin=src_token, dst_coin=dst_token, dst_address=dest_address, amount=str(amount), dst_network="Ethereum", status=Status.SWAP_FAILED) try: swap.save() except (DuplicateKeyError, NotUniqueError): pass return msg = message.Submit( tx_dest, tx_amount, # if we are swapping token, no ether should be rewarded int(swap_json['nonce']), tx_token, fee, data) # todo: check we have enough ETH swap = Swap(src_network="Secret", src_tx_hash=swap_id, unsigned_tx=data, src_coin=src_token, dst_coin=dst_token, dst_address=dest_address, amount=str(amount), dst_network="Ethereum", status=Status.SWAP_FAILED) try: tx_hash = self._broadcast_transaction(msg) swap.dst_tx_hash = tx_hash swap.status = Status.SWAP_SUBMITTED except (ValueError, TransactionNotFound) as e: self.logger.critical( f"Failed to broadcast transaction for msg {repr(msg)}: {e}") finally: try: swap.save() except (DuplicateKeyError, NotUniqueError): pass