Skip to content

Commit

Permalink
Add command to resend pending txs
Browse files Browse the repository at this point in the history
  • Loading branch information
Uxio0 committed Mar 13, 2020
1 parent 39190c1 commit 0dfe791
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 2 deletions.
49 changes: 49 additions & 0 deletions safe_relay_service/relay/management/commands/resend_txs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
from django.core.management.base import BaseCommand

from safe_relay_service.gas_station.gas_station import GasStationProvider

from ...models import EthereumTx, SafeMultisigTx
from ...services import TransactionServiceProvider


class Command(BaseCommand):
help = 'Resend txs using a higher gas price. Use this command to allow stuck txs go through'

def add_arguments(self, parser):
# Positional arguments
parser.add_argument('--gas-price', help='Resend all txs below this gas-price using that gas price')
parser.add_argument('--safe-tx-hash', help='Resend tx with tx hash')

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.tx_service = TransactionServiceProvider()

def resend(self, gas_price: int, multisig_tx: SafeMultisigTx):
#TODO Refactor, do it on the service
if multisig_tx.ethereum_tx.gas_price < gas_price:
assert multisig_tx.ethereum_tx.block_id is None, 'Block is present!'
self.stdout.write(self.style.NOTICE(
f"{multisig_tx.ethereum_tx_id} tx gas price is {multisig_tx.ethereum_tx.gas_price} < {gas_price}. "
f"Resending with new gas price {gas_price}"
))
safe_tx = multisig_tx.get_safe_tx(self.tx_service.ethereum_client)
tx_hash, tx = safe_tx.execute(tx_sender_private_key=self.tx_service.tx_sender_account.key,
tx_gas_price=gas_price, tx_nonce=multisig_tx.ethereum_tx.nonce)
multisig_tx.ethereum_tx = EthereumTx.objects.create_from_tx(tx, tx_hash)
multisig_tx.save(update_fields=['ethereum_tx'])
else:
self.stdout.write(self.style.NOTICE(
f"{multisig_tx.ethereum_tx_id} tx gas price is {multisig_tx.ethereum_tx.gas_price} > {gas_price}. "
f"Nothing to do here"
))

def handle(self, *args, **options):
gas_price = options['gas_price'] or GasStationProvider().get_gas_prices().fast
safe_tx_hash = options['safe_tx_hash']

for multisig_tx in self.tx_service.get_pending_multisig_transactions(older_than=60):
if safe_tx_hash:
if multisig_tx.safe_tx_hash == safe_tx_hash:
self.resend(gas_price, multisig_tx)
else:
self.resend(gas_price, multisig_tx)
5 changes: 3 additions & 2 deletions safe_relay_service/relay/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from hexbytes import HexBytes
from model_utils.models import TimeStampedModel

from gnosis.eth import EthereumClient
from gnosis.eth.constants import ERC20_721_TRANSFER_TOPIC
from gnosis.eth.django.models import (EthereumAddressField, Sha3HashField,
Uint256Field)
Expand Down Expand Up @@ -409,8 +410,8 @@ def __str__(self):
return '{} - {} - Safe {}'.format(self.ethereum_tx.tx_hash, SafeOperation(self.operation).name,
self.safe.address)

def get_safe_tx(self) -> SafeTx:
return SafeTx(None, self.safe_id, self.to, self.value, self.data.tobytes() if self.data else b'',
def get_safe_tx(self, ethereum_client: Optional[EthereumClient] = None) -> SafeTx:
return SafeTx(ethereum_client, self.safe_id, self.to, self.value, self.data.tobytes() if self.data else b'',
self.operation, self.safe_tx_gas, self.data_gas, self.gas_price, self.gas_token,
self.refund_receiver,
signatures=self.signatures.tobytes() if self.signatures else b'',
Expand Down
2 changes: 2 additions & 0 deletions safe_relay_service/relay/services/transaction_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,8 @@ def get_pending_multisig_transactions(self, older_than: int) -> List[SafeMultisi
Q(ethereum_tx__block=None) | Q(ethereum_tx=None)
).filter(
created__lte=timezone.now() - timedelta(seconds=older_than),
).select_related(
'ethereum_tx'
)

# TODO Refactor and test
Expand Down
21 changes: 21 additions & 0 deletions safe_relay_service/relay/tests/test_resend_txs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from datetime import timedelta

from django.core.management import call_command
from django.test import TestCase
from django.utils import timezone

from gnosis.eth.tests.ethereum_test_case import EthereumTestCaseMixin

from ..management.commands import resend_txs
from .factories import SafeMultisigTxFactory


class TestResendTxsCommand(EthereumTestCaseMixin, TestCase):
def test_resend_txs(self):
multisig_tx = SafeMultisigTxFactory(ethereum_tx__block=None)
call_command(resend_txs.Command())
multisig_tx.created = timezone.now() - timedelta(days=1)
multisig_tx.save()
with self.assertRaises(ValueError):
call_command(resend_txs.Command(), gas_price=multisig_tx.ethereum_tx.gas_price + 1)
#TODO More testing

0 comments on commit 0dfe791

Please sign in to comment.