Protocol limit estimation
Certain actions in the protocol require looping over dynamic size arrays. To avoid hitting the block gas limit, special protocol limits were introduced which revert the transaction before the loop even starts. Values for these limits are then determined through estimation process, described here.
Identifying the limits
First we identify which limits we use at the moment and which functions (or chain of functions) use them. The goal is to identify the external functions which can be called during the estimation.
List of limits
maxExchangesPerBatch
completeExchangeBatch
maxOffersPerGroup
createGroupInternal -> createGroup
maxOffersPerGroup
createGroupInternal -> createOfferWithCondition
not a problem, always length 1
maxOffersPerGroup
preUpdateChecks -> addOffersToGroupInternal -> addOffersToGroup
maxOffersPerGroup
preUpdateChecks -> addOffersToGroupInternal -> createOfferAddToGroup
not a problem, always length 1
maxOffersPerGroup
preUpdateChecks -> removeOffersFromGroup
maxOffersPerBundle
createBundleInternal -> createBundle
maxOffersPerBundle
createBundleInternal -> createTwinAndBundleAfterOffer
not a problem, always length 1
maxTwinsPerBundle
createBundleInternal -> createBundle
maxTwinsPerBundle
createBundleInternal -> createTwinAndBundleAfterOffer
not a problem, always length 1
maxOffersPerBatch
createOfferBatch
maxOffersPerBatch
voidOfferBatch
maxOffersPerBatch
extendOfferBatch
maxTokensPerWithdrawal
withdrawFundsInternal -> withdrawFunds
maxTokensPerWithdrawal
withdrawFundsInternal -> withdrawProtocolFees
maxFeesPerDisputeResolver
createDisputeResolver
maxFeesPerDisputeResolver
addFeesToDisputeResolver
maxFeesPerDisputeResolver
removeFeesFromDisputeResolver
maxDisputesPerBatch
expireDisputeBatch
maxAllowedSellers
createDisputeResolver
maxAllowedSellers
addSellersToAllowList
maxAllowedSellers
removeSellersFromAllowList
Estimation config
Config file is placed in scripts/config/limit-estimation.js. It has the following fields:
blockGasLimit: block gas limit against which you want to make the estimatesafeGasLimitPercent: percent of total gas block limit that you consider safe for a transaction to actually be included in the block. For example ifblockGasLimitis30Mand you don't want your transaction to exceed15M, setsafeGasLimitPercentto50.maxArrayLength: maximum length of the array used during the estimation. This value is typically smaller than actual limits calculated at the end. Increasing this value makes estimation more precise, however it also takes more time. Improvement in the estimate is increasing slower than run time, so setting this to100should be more than enough. If you want to speed up the process, setting this to10will still give you very good results.limits: list of limits you want to estimate. Each limit is an object with fields:name: name of the limitmethods: object of pairs"methodName":"handlerName"wheremethodNameis the name of the external function that uses the limit andhandlerNameis the name of the handler where this function is implemented. Example for limitmaxOffersPerGroup:
Setting up the environment
For each of the limits you must prepare an evironment, before it can be tested. For example, before maxOffersPerGroup can be tested, protocol contracts must be deployed and enough offers must be created so the limit can actually be tested. A similar setup is needed for all other methods.
This is done in file scripts/util/estimate-limits.js. Each of the limits must have a setup function which accepts maxArrayLength, prepares the environment and returns the invocation details that can be then used when invoking the methods during the estimation.
Invocation details contain
account: account that calls the method (important if access is restricted)args: array of arguments that needs to be passed into methodarrayIndex: index that tells which parameter's length should be varied during the estimationstructField: if array is part of a struct, specify the field name
The returned object must be in form { methodName_1: invocationDetails_1, methodName_2: invocationDetails_2, ..., methodName_n: invocationDetails_2} with details for all methods specified in estimation config.
Running the script
Scrip is run by calling
npm run estimate-limits
During the estimation it outputs the information about the method it is estimating. At the end it stores the estimation details into two files:
logs/limit_estimates.jsonData in JSON formatlogs/limit_estimates.mdData in MD table
Results
The results for parameters
blockGasLimit:30,000,000(current ethereum mainnet block gas limit)safeGasLimitPercent:60
maxExchangesPerBatch
557
333
maxOffersPerGroup
388
232
maxOffersPerBundle
508
303
maxTwinsPerBundle
510
304
maxOffersPerBatch
51
31
maxTokensPerWithdrawal
491
293
maxFeesPerDisputeResolver
305
181
maxDisputesPerBatch
302
181
maxAllowedSellers
597
352
max value is determined based on blockGasLimit, while safe value also applies safeGasLimitPercent.
Gas spent for different sizes of arrays
completeExchangeBatch
createGroup
addOffersToGroup
removeOffersFromGroup
createBundle
createBundle
createOfferBatch
voidOfferBatch
extendOfferBatch
withdrawFunds
withdrawProtocolFees
createDisputeResolver
addFeesToDisputeResolver
removeFeesFromDisputeResolver
expireDisputeBatch
createDisputeResolver
addSellersToAllowList
removeSellersFromAllowList
1
184357
216179
166813
339950
266339
266339
645410
76369
63841
98246
124263
456727
168014
99325
223643
720355
120611
76720
2
237945
292824
243899
362917
324373
324237
1220883
111203
85650
144880
193424
552740
264455
145349
322061
769463
169117
99602
3
290692
369287
319811
385956
382685
382414
1796358
145208
107442
191521
262643
649449
360736
191089
420849
817790
217760
122522
4
344271
446010
396802
409687
440789
440369
2371848
179853
129223
238345
330958
745985
457657
236552
519034
866852
266659
145744
5
397506
522438
473499
432743
498656
498102
2947330
213982
150777
284285
399900
842313
553655
282583
617217
915916
315181
168453
6
450933
599145
549901
455724
556830
556142
3522863
248993
172476
330931
468759
939354
650340
327624
716072
964978
364020
191603
7
504557
675705
626011
478855
615481
614658
4098400
282740
194322
377398
538021
1038389
746851
373526
814350
1015980
412672
214102
8
557350
752116
703168
502121
673490
672532
4673951
317493
216313
423687
606770
1135642
843190
418787
912629
1065136
462015
237182
9
610704
829174
779581
525383
731386
730295
5289900
351745
237643
470697
675733
1232882
940232
464621
1013746
1114293
510385
260262
10
664157
905366
855847
548648
789928
788700
5869922
386681
259853
516720
744561
1325047
1039257
510454
1112302
1163448
559101
283342
20
1199674
1672031
1622868
780097
1371424
1368854
11585194
730484
476329
981216
1435505
2293894
2003996
966566
2097899
1648704
1050125
511799
30
1731595
2440464
2391564
1012387
1955241
1951326
17350904
1078743
693892
1446838
2126474
3256561
2967075
1424971
3083565
2138411
1535807
740419
40
2271836
3202792
3154246
1244680
2539116
2533856
23112733
1423827
911454
1913450
2817507
4248050
3934100
1882668
4069301
2628126
2025507
969085
50
2800719
3969835
3921550
1476974
3117065
3110472
28911628
1768963
1135500
2380130
3503226
5220856
4929416
2340381
5055106
3111881
2515218
1199241
60
3333793
4764194
4715895
1542105
3699938
3692002
2114152
1353506
2841377
3983943
6192222
5902160
2409313
6040982
3600680
2999184
1220202
70
3866927
5535734
5487697
1607235
4307529
4298195
2459395
1566348
3307259
4471569
7137316
6853941
2478233
7030675
4113038
3487977
1241164
80
4424168
6302543
6256137
1672369
4893873
4883188
2804690
1783781
3773180
4953126
8125585
7799011
2547183
8046950
4604672
3976781
1262132
90
4960338
7052123
7005985
1737566
5480275
5468239
3144491
2001293
4263556
5434728
9068993
8785898
2616114
9014128
5096317
4491310
1283105
100
5496569
7801814
7755932
1802826
6066735
6053349
3489284
2218882
4732249
5916387
10051836
9729258
2685059
9977691
5587972
4982950
1304081
max
557
388
389
1733
508
510
51
868
1375
641
491
305
309
932
302
597
610
1906
safe
333
232
232
1031
303
304
31
520
824
384
293
181
185
557
181
352
365
1141
Methodology
As seen from the results above, some limits are relatively high and to actually hit the limit, already the setup would take a lot of time (e.g. for making >1700 offers to hit limit in removeOffersFromGroup). To get the estimates we therefore use the following approach:
get the actual estimates for relatively small number of different array lengths
given how gas is determined, there exist approximate linear relation, which can be written as
gasSpent = intrinsicGas + arrayLength*costPerLoop. Intrinsic costs here contains all costs that are fixed regardless of the array size.use linear regression to estimate
intrinsicGasandcostPerLoopuse these estimates to calculate the biggest
arrayLengthwheregasSpent <= blockGasLimitwhich gives the maximum value. To get the safe value we find the biggestarrayLengthwheregasSpent <= safeGasLimitPercent*blockGasLimit
Last updated