Skip to main content

Determining gas requirements

Determining gas requirements (gasreq) for your offer logic in a maker contract is important to avoid failures, save on provision, and make offers as attractable as possible.

To determine the gasreq, you can measure the worst case gas usage when makerExecute and makerPosthook are called. There could be exception cases which are very gas costly where you simply want the offer to fail instead, and you could skip those.

Measuring gas usage can be done with Foundry - in the smart offer tutorial you can see how to make and take orders with your contract, and using cast run [transactionHash] you can find the actual gas usage. Bear in mind that all relevant code paths should be hit, the tutorial only covers the happy path of makerExecute followed by makerPosthook with a successful trade.

Note, that the tutorial did not implement either makerExecute or makerPosthook as they are implemented by the strat lib and in turn invoke actual strategy through a template method pattern.

For instance, if you have followed the tutorial and have a transactionHash for taking the offer, you can run the following:

cast run --label $DAI:DAI --label $WETH:WETH --label $MANGROVE:Mangrove --label $OFFER_MAKER:OfferMakerTutorial <transactionHash>

In the trace you can find OfferMakerTutorial::makerExecute and OfferMakerTutorial::makerPosthook and note down the number in brackets which is the total gas cost of the function (more on Foundry's tracing).

When using the strat lib, then the gas usage from the router is automatically added by the library and should be subtracted from the measured total. Your own router would have to measure its worst case cost and provide it as part of its parameters.

In our case the gas usage was 45,658 and 1,659, and since there is no router used, we don't have special code in posthookFallback for this strat, we have to account for a small overhead in Mangrove for gas accounting, and Foundry's traces leave some gas unaccounted for then setting the gasreq to 70,000 for this contract is a good choice. However, note that the default posthookSuccess can repost an offer for the residual if not fully taken and that would require some gas. See how-to on residual for more details.

For more complete gas consumption analysis for a larger contract it can be helpful to use Foundry's gas tracking features.

Note that gas usage can change with ethereum upgrades or compiler changes, and various tricks can be used to reduce gas.

Here are some common operations and their approximate gas consumption:

OperationApproximate Gas
Simple Direct contract30,000
ERC20 transfer25,000 (worst case is when transferring to account with 0 balance)
AAVE redeem/borrow300,000
updateOffer without changing the price20,000
retractOffer without deprovision10,000
retractOffer with deprovision50,000
Ethereum opcodesSee ethereum documentation
Set cold storage value x!=0 to x!=03,000
Set cold storage value x==0 to x!=020,000

The attentive reader may see that the tutorial uses more than a Simple Direct contract. This is because the maker does not transfer tokens to the contract prior to the offer being taken - so the contract pulls them from the maker just-in-time - costing the roughly 25,000 gas of an ERC20 transfer.

To verify this, you can run the tutorial but transfer tokens to the contract before taking the offer with:

cast send --rpc-url $LOCAL_URL "$WETH" "transfer(address, uint)" "$OFFER_MAKER" 1000000000000000000  --private-key "$PRIVATE_KEY"

The gasreq should be taken into account when provisioning.