0%
SupportWallet Services
Best Practice Guide on How to implement User Operation Retries

Best Practice Guide on How to implement User Operation Retries

Written by
Liang
Published on August 23, 20244 min read

Proper wait and retry mechanisms are imperative for any production dApp to ensure all UOs land effectively. Due to network market fluctuations or unexpected congestion, the time required for UOs to mine can fluctuate, similar to normal transactions.

Why is my UO taking so long to land?

It’s very common for fee markets to fluctuate quickly, especially on testnets. When this happens, UOs in the mempool can become underpriced and take longer than expected to mine.

1 ) Wait for UOs to land before sending another from the same user

a. Simplify with React Hooks - combine sending and waiting.


Use our React hooks to streamline your code. Use the useSendUserOperation hook with await and retry (waitForTxn flag = true) to wait for UOs to land and attempt to dropAndReplace. Ensure to catch errors where the wait method times out - see #2.

b. Without React Hooks - combine sending and waiting for UOs

sendTransaction or sendTransactions methods combine sending and waiting for the user operation in one call. Ensure to catch errors where the wait method times out - see #2.

c. Send and Wait for User Operations Separately

If you prefer to send and then wait, use the waitForUserOperationTransaction method. This continuously polls for the user operation receipt to wait for the UO to be confirmed on-chain. Retry mechanisms are defined on the client config (see #2)

Ensure to catch errors if this method times out - you can drop and replace the previous user operation or continue waiting.

2) Increase wait time

  • On the smart account client opts params, you can customize the retry configurations to your needs - txMaxRetries, txRetryIntervalMs, txRetryMultiplier .

  • All client wait functionality will use these configurations to attempt to get the UO receipt and this method will throw an error if no receipt is found after the retry period.

  • It’s still possible for wait methods to error out and you should catch these errors. When this happens you should either continue to wait or retry.

  1. Retry sending using drop and replace

    • If a UO has not landed after defined wait times (i.e. wait methods error out), you can retry sending the UO with higher fees - this will require another user signature.

    • Use the dropAndReplaceUserOperation endpoint. This endpoint automatically applies the required 10% increase on maxPriorityFeePerGas and maxFeePerGas. Alternatively, you can submit a new UO with the same 10% fee increase.

  2. Handle Fee Market Fluctuations using multipliers

    • If your user operations are not being confirmed on-chain for an extended period, this could be due to fee market fluctuations.

    • We recommend adding overrides (multiplier) to your gas estimates to better handle these fluctuations.

1) Wait for UOs to land before sending another from the same user

  • Use the eth_getUserOperationReceipt endpoint to check for the UO landing on chain. Do not attempt to send another UO until the previous one has successfully been mined and the receipt is returned.

2) Retry sending using drop and replace

  • userOps can become "stuck" in the mempool if their gas fee limits are too low to be included in a bundle. You may want to force drop and replace UOs after some time.

    1. Re-estimate gas fees: Re-estimate the gas fees required for your operation. This can be done in various ways which are mentioned below:

      1. Use the eth_maxPriorityFeePerGas method to obtain maxPriorityFeePerGas.

    1. Choose the suitable increase values: Once you have the re-estimated gas fees, choose the maximum of a 10% increase or the re-estimated values. This ensures that your new gas fee is competitively priced to be included in a block.

    2. Account for Rundler's service tip: Rundler requires a small tip for its services via maxPriorityFeePerGas. Detailed information about this can be found on the Bundler API Fee Logic page, but in summary:

      • On Arbitrum, the recommended tip is a minimum 5% of the estimated network base fee when the bundle is mined.

      • On all other mainnets Alchemy supports, the recommended tip is at least 25% higher than the estimated priority fee on the network.

      • No tip is required on testnets.

    After calculating the new values, send your userOp again with the updated fees and it should go through successfully.

3) Handle fee market fluctuations using multipliers

  1. When calling alchemy_requestGasAndPaymasterAndData, use the overrides param to apply multipliers on top of gas and fee estimates. This will support getting UOs prioritized in the mempool and handle possible market fluctuations better.

Common scenario without proper mechanisms in place

Below is a common scenario customers run in to at scale due to not implementing recommended wait and retry mechanisms. It may lead to multiple downstream affects.

Scenario

  • 1 UO sent to the bundler and accepted, however, market fees fluctuated and the UO becomes underpriced

  • That UO is now pending for a long period of time in the mempool because it is underpriced and in a "stuck" state

  • Without error catching for this, you continue to request gas manager signatures and attempt to send UOs

  • Paymaster signatures will be granted successfully, BUT bundler will reject the UO because there is already an existing userOp in the mempool for the sender

  • This leads to 2 issues

    1. Since paymaster signatures are valid and contribute to the pending amount, you may hit your gas manager limits.

      • These signatures are valid for the expiry set on the policy itself and they cannot be invalidated until timeout

    2. You will experience replacement underpriced errors until the first UO is either landed or forcibly dropped

To avoid these issues we highly recommend you implement the practices above.

Was this article helpful?
Share:
Banner background image

Not finding what you need?