Skip to main content
edited tags
Link
Paul Razvan Berg
  • 18.7k
  • 6
  • 79
  • 148
Update pseudocode
Source Link
Paul Razvan Berg
  • 18.7k
  • 6
  • 79
  • 148

I'm writing a bot that monitors a DeFi protocol for liquidation opportunities. Here is someThe bot behaves like in the pseudo-code that shows how the bot worksbelow:

let pending_txs: [];

for every_new_block in Ethereum {
  // Check the state of pending txs.
  for pending_tx in pending_txs {
    let receipt = get_receipt(pending_tx);

    if receipt != null {
      // If we got a receipt from Ethereum, clear the tx from the local state.
      delete_tx_from_array(pending_tx);
    } else {
      // Otherwise, replace the tx and update it in the array.
      let new_gas_price = bump_gas_price(pending_tx.gas_price);
      let tx = send_tx(new_gas_price);
      update_tx_in_array(tx);
    }
  }

  ...
   
  // Search for new liquidation opportunities.
  if liquidation_opportunity and if !pending_tx_for_liquidation_opportunity {
    let tx = send_tx(initial_gas_price);
    add_tx_in_array(tx);
  }
}

The codeabove works well most of the times, but there it an exception:

  1. Submit tx 0x123
  2. Wait 15 seconds, check if we got a receipt from Ethereum.
  3. If we didn't, re-calculate the gas price and send a replacement tx 0xabc.
  4. By the time the replacement tx 0xabc is broadcast to the Ethereum network, the 0x123 gets mined.
  5. The bot will attempt to replace the 0xabc tx forever, because the receipt is always null for 0xabc.

When I update the tx in the array, I also override the previous tx hash (this makes the implementation simple). In other words, the script ceases to be aware of past tx hashes once it broadcasts a replacement tx, which causes an infinite regress when a racing condition occurs.

The question is whether there is any way to avoid this situation? I've only run my bot on Rinkeby, against an Infura node. Maybe it's something that happens frequently on testnets, but rarely on mainnet?

I know that one solution is to modify the code to track multiple tx hashes per liquidation opportunity, but that would greatly increase the complexity of the implementation (I'm using Rust).

I'm writing a bot that monitors a DeFi protocol for liquidation opportunities. Here is some pseudo-code that shows how the bot works:

let pending_txs: [];

for every_new_block in Ethereum {
  // Check the state of pending txs.
  for pending_tx in pending_txs {
    let receipt = get_receipt(pending_tx);

    if receipt != null {
      // If we got a receipt from Ethereum, clear the tx from the local state.
      delete_tx_from_array(pending_tx);
    } else {
      // Otherwise, replace the tx and update it in the array.
      let new_gas_price = bump_gas_price(pending_tx.gas_price);
      let tx = send_tx(new_gas_price);
      update_tx_in_array(tx);
    }
  }

  ...
   
  // Search for new liquidation opportunities.
  if liquidation_opportunity {
    let tx = send_tx(initial_gas_price);
    add_tx_in_array(tx);
  }
}

The code works well most of the times, but there it an exception:

  1. Submit tx 0x123
  2. Wait 15 seconds, check if we got a receipt from Ethereum.
  3. If we didn't, re-calculate the gas price and send a replacement tx 0xabc.
  4. By the time the replacement tx 0xabc is broadcast to the Ethereum network, the 0x123 gets mined.
  5. The bot will attempt to replace the 0xabc tx forever, because the receipt is always null for 0xabc.

When I update the tx in the array, I also override the previous tx hash (this makes the implementation simple). In other words, the script ceases to be aware of past tx hashes once it broadcasts a replacement tx, which causes an infinite regress when a racing condition occurs.

The question is whether there is any way to avoid this situation? I've only run my bot on Rinkeby, against an Infura node. Maybe it's something that happens frequently on testnets, but rarely on mainnet?

I know that one solution is to modify the code to track multiple tx hashes per liquidation opportunity, but that would greatly increase the complexity of the implementation (I'm using Rust).

I'm writing a bot that monitors a DeFi protocol for liquidation opportunities. The bot behaves like in the pseudo-code below:

let pending_txs: [];

for every_new_block in Ethereum {
  // Check the state of pending txs.
  for pending_tx in pending_txs {
    let receipt = get_receipt(pending_tx);

    if receipt != null {
      // If we got a receipt from Ethereum, clear the tx from the local state.
      delete_tx_from_array(pending_tx);
    } else {
      // Otherwise, replace the tx and update it in the array.
      let new_gas_price = bump_gas_price(pending_tx.gas_price);
      let tx = send_tx(new_gas_price);
      update_tx_in_array(tx);
    }
  }

  ...
   
  // Search for new liquidation opportunities.
  if liquidation_opportunity and if !pending_tx_for_liquidation_opportunity {
    let tx = send_tx(initial_gas_price);
    add_tx_in_array(tx);
  }
}

The above works well most of the times, but there it an exception:

  1. Submit tx 0x123
  2. Wait 15 seconds, check if we got a receipt from Ethereum.
  3. If we didn't, re-calculate the gas price and send a replacement tx 0xabc.
  4. By the time the replacement tx 0xabc is broadcast to the Ethereum network, the 0x123 gets mined.
  5. The bot will attempt to replace the 0xabc tx forever, because the receipt is always null for 0xabc.

When I update the tx in the array, I also override the previous tx hash (this makes the implementation simple). In other words, the script ceases to be aware of past tx hashes once it broadcasts a replacement tx, which causes an infinite regress when a racing condition occurs.

The question is whether there is any way to avoid this situation? I've only run my bot on Rinkeby, against an Infura node. Maybe it's something that happens frequently on testnets, but rarely on mainnet?

I know that one solution is to modify the code to track multiple tx hashes per liquidation opportunity, but that would greatly increase the complexity of the implementation (I'm using Rust).

Source Link
Paul Razvan Berg
  • 18.7k
  • 6
  • 79
  • 148

Racing condition when replacing transactions

I'm writing a bot that monitors a DeFi protocol for liquidation opportunities. Here is some pseudo-code that shows how the bot works:

let pending_txs: [];

for every_new_block in Ethereum {
  // Check the state of pending txs.
  for pending_tx in pending_txs {
    let receipt = get_receipt(pending_tx);

    if receipt != null {
      // If we got a receipt from Ethereum, clear the tx from the local state.
      delete_tx_from_array(pending_tx);
    } else {
      // Otherwise, replace the tx and update it in the array.
      let new_gas_price = bump_gas_price(pending_tx.gas_price);
      let tx = send_tx(new_gas_price);
      update_tx_in_array(tx);
    }
  }

  ...
   
  // Search for new liquidation opportunities.
  if liquidation_opportunity {
    let tx = send_tx(initial_gas_price);
    add_tx_in_array(tx);
  }
}

The code works well most of the times, but there it an exception:

  1. Submit tx 0x123
  2. Wait 15 seconds, check if we got a receipt from Ethereum.
  3. If we didn't, re-calculate the gas price and send a replacement tx 0xabc.
  4. By the time the replacement tx 0xabc is broadcast to the Ethereum network, the 0x123 gets mined.
  5. The bot will attempt to replace the 0xabc tx forever, because the receipt is always null for 0xabc.

When I update the tx in the array, I also override the previous tx hash (this makes the implementation simple). In other words, the script ceases to be aware of past tx hashes once it broadcasts a replacement tx, which causes an infinite regress when a racing condition occurs.

The question is whether there is any way to avoid this situation? I've only run my bot on Rinkeby, against an Infura node. Maybe it's something that happens frequently on testnets, but rarely on mainnet?

I know that one solution is to modify the code to track multiple tx hashes per liquidation opportunity, but that would greatly increase the complexity of the implementation (I'm using Rust).