Skip to benchmarks
D20DAO

BENCHMARKS / ARC TESTNET

Load tests, with receipts.

6 scenarios run on 2026-09-18 against the public D20DAO keepers on Arc Testnet, a primary and a backup: 1,270 paid requests opened through a benchmark consumer contract. Every figure below comes from committed result files and links to its transactions. These are observations of two keepers on a testnet, not an SLA.

Served within 60 s
1,270 / 1,270
all scenarios; 0 refunds, 0 expired
Burst of 200
16.67 fulfillments/s
over 12 s, 19 fulfillment transactions
Isolated request, p50
2 s
4 blocks; sequential-20
Largest batch
16 requests
in one fulfillment transaction

Scenarios compared

Completion is chain time from the request block to the fulfillment block, in seconds and in blocks (p50 / p95 / p99 / max). Throughput is a serving rate only for bursts; in the other scenarios it follows the arrival rate. Duration runs from the first request block to the last fulfillment block.

ScenarioRequestsSuccess in 60 sCompletion, sBlocksFulfillment batchesThroughputGas per requestDuration
Sequential baseline20 requests, one at a time20 × 1 in 20 blocks100%20 of 202 / 3 / 14 / 144 / 6 / 29 / 2920 transactionsmean 1.0, max 10.28/slimited by arrivals296,969+ 301,311 fulfillment74 s79 s wall clock
Burst of 5050 requests at once1 × 50 in 1 block100%50 of 508 / 10 / 10 / 1017 / 20 / 20 / 205 transactionsmean 10.0, max 167.14/s50 over 7 s195,898+ 261,391 fulfillment10 s14 s wall clock
Burst of 200200 requests at once4 × 50 in 2 blocks100%200 of 20010 / 14 / 15 / 1520 / 28 / 30 / 3019 transactionsmean 11.2, max 1616.67/s200 over 12 s195,493+ 264,097 fulfillment16 s19 s wall clock
Sustained 5 requests/s5 requests/s for 40 s40 × 5 in 40 blocks100%200 of 2005 / 8 / 9 / 99 / 16 / 19 / 1922 transactionsmean 9.1, max 165.13/slimited by arrivals230,680+ 232,667 fulfillment42 s47 s wall clock
Burst of 500500 requests at once10 × 50 in 5 blocks100%500 of 50018 / 25 / 27 / 2735 / 49 / 53 / 5335 transactionsmean 14.3, max 1622.73/s500 over 22 s195,493+ 249,086 fulfillment27 s30 s wall clock
Burst of 300, primary stopped300 requests at once6 × 50 in 4 blocks100%300 of 3008 / 17 / 17 / 1816 / 34 / 34 / 3622 transactionsmean 13.6, max 1621.43/s300 over 14 s195,898+ 246,498 fulfillment18 s22 s wall clock

SEQUENTIAL-20 / 2026-09-18 12:23 UTC

Sequential baseline

Baseline: 20 requests, one per transaction; the next request is sent only after the previous one is fulfilled or expired.

Load and concurrency

Requests
20 opened of 20 in the scenario
Request transactions
20 transactions of 1, in 20 blocks; at most 1 in one block
Request span
145 blocks, 72 s

Outcome

Success in 60 s
100% (20 of 20)
Callbacks delivered
20 (consumer counted 20)
Refunds / expired
0 / 0

Completion

Chain seconds
p50 2, p95 3, p99 14, max 14
Blocks
p50 4, p95 6, p99 29, max 29
Wall clock
broadcast to observed fulfillment: p50 2.8 s, p95 3.8 s, max 15.4 s
Epoch publication
2 of 20 requests waited for their epoch packet to be published

Batching and throughput

Fulfillment transactions
20; 0 other applications' requests shared them
Members per transaction
  1. 1
  2. 1
  3. 1
  4. 1
  5. 1
  6. 1
  7. 1
  8. 1
  9. 1
  10. 1
  11. 1
  12. 1
  13. 1
  14. 1
  15. 1
  16. 1
  17. 1
  18. 1
  19. 1
  20. 1
Throughput
0.28/s, limited by arrivals (20 over 71 s, 20 fulfillment transactions)
Duration
74 s from first request to last fulfillment; 79 s wall clock

Gas and cost

Request gas
296,969 per request (p50), at 25.0 gwei
Fulfillment gas
301,311 per served request, 368,018 max per transaction
User cost
0.0800 USDC fee + 0.0074 USDC gas per request
Keeper
0.0400 USDC fee share, 0.0097 USDC gas per served request

Arc block conditions

Blocks
151 (62,741,155 to 62,741,305), mean interval 0.50 s
Base fee
20 / 20 / 20 gwei (min / median / max)
Gas used ratio
mean 4.3%, max 14.7% of a 30,000,000 gas limit

BURST-50 / 2026-09-18 12:26 UTC

Burst of 50

50 requests opened by one transaction, so all share one block.

Load and concurrency

Requests
50 opened of 50 in the scenario
Request transactions
1 transaction of 50, in 1 block; at most 50 in one block
Request span
1 block, 0 s

Outcome

Success in 60 s
100% (50 of 50)
Callbacks delivered
50 (consumer counted 50)
Refunds / expired
0 / 0

Completion

Chain seconds
p50 8, p95 10, p99 10, max 10
Blocks
p50 17, p95 20, p99 20, max 20
Wall clock
broadcast to observed fulfillment: p50 10.1 s, p95 10.6 s, max 10.6 s
Epoch publication
50 of 50 requests waited for their epoch packet to be published

Batching and throughput

Fulfillment transactions
5; 0 other applications' requests shared them
Members per transaction
  1. 4
  2. 8
  3. 8
  4. 16
  5. 14
Throughput
7.14/s (50 over 7 s, 5 fulfillment transactions)
Duration
10 s from first request to last fulfillment; 14 s wall clock

Gas and cost

Request gas
195,898 per request (p50), at 25.0 gwei
Fulfillment gas
261,391 per served request, 4,178,583 max per transaction
User cost
0.0800 USDC fee + 0.0049 USDC gas per request
Keeper
0.0400 USDC fee share, 0.0085 USDC gas per served request

Arc block conditions

Blocks
24 (62,741,560 to 62,741,583), mean interval 0.52 s
Base fee
20 / 20 / 20 gwei (min / median / max)
Gas used ratio
mean 7.2%, max 34.6% of a 30,000,000 gas limit

BURST-200 / 2026-09-18 12:28 UTC

Burst of 200

200 requests opened by four 50-request transactions broadcast together; about 11M gas each, so they need at least two 30M-gas blocks.

Load and concurrency

Requests
200 opened of 200 in the scenario
Request transactions
4 transactions of 50, in 2 blocks; at most 100 in one block
Request span
2 blocks, 1 s

Outcome

Success in 60 s
100% (200 of 200)
Callbacks delivered
200 (consumer counted 200)
Refunds / expired
0 / 0

Completion

Chain seconds
p50 10, p95 14, p99 15, max 15
Blocks
p50 20, p95 28, p99 30, max 30
Wall clock
broadcast to observed fulfillment: p50 11.7 s, p95 15.7 s, max 16.7 s
Epoch publication
200 of 200 requests waited for their epoch packet to be published

Batching and throughput

Fulfillment transactions
19; 0 other applications' requests shared them
Members per transaction
  1. 4
  2. 8
  3. 8
  4. 8
  5. 16
  6. 8
  7. 16
  8. 16
  9. 16
  10. 16
  11. 16
  12. 16
  13. 16
  14. 8
  15. 8
  16. 8
  17. 16
  18. 4
  19. 4
Throughput
16.67/s (200 over 12 s, 19 fulfillment transactions)
Duration
16 s from first request to last fulfillment; 19 s wall clock

Gas and cost

Request gas
195,493 per request (p50), at 25.0 gwei
Fulfillment gas
264,097 per served request, 4,255,516 max per transaction
User cost
0.0800 USDC fee + 0.0049 USDC gas per request
Keeper
0.0400 USDC fee share, 0.0080 USDC gas per served request

Arc block conditions

Blocks
35 (62,741,833 to 62,741,867), mean interval 0.50 s
Base fee
20 / 20 / 20 gwei (min / median / max)
Gas used ratio
mean 13.1%, max 72.4% of a 30,000,000 gas limit

SUSTAINED-5RPS-40S / 2026-09-18 12:31 UTC

Sustained 5 requests/s

Sustained arrivals: one 5-request transaction every second for 40 seconds (200 requests). 40 s instead of 60 s keeps the whole suite under 500 paid requests.

Load and concurrency

Requests
200 opened of 200 in the scenario
Request transactions
40 transactions of 5, in 40 blocks; at most 5 in one block
Request span
79 blocks, 39 s

Outcome

Success in 60 s
100% (200 of 200)
Callbacks delivered
200 (consumer counted 200)
Refunds / expired
0 / 0

Completion

Chain seconds
p50 5, p95 8, p99 9, max 9
Blocks
p50 9, p95 16, p99 19, max 19
Wall clock
broadcast to observed fulfillment: p50 5.2 s, p95 9.4 s, max 10.5 s
Epoch publication
10 of 200 requests waited for their epoch packet to be published

Batching and throughput

Fulfillment transactions
22; 0 other applications' requests shared them
Members per transaction
  1. 5
  2. 2
  3. 13
  4. 11
  5. 9
  6. 5
  7. 5
  8. 5
  9. 10
  10. 10
  11. 5
  12. 10
  13. 10
  14. 16
  15. 4
  16. 8
  17. 7
  18. 15
  19. 16
  20. 16
  21. 16
  22. 2
Throughput
5.13/s, limited by arrivals (200 over 39 s, 22 fulfillment transactions)
Duration
42 s from first request to last fulfillment; 47 s wall clock

Gas and cost

Request gas
230,680 per request (p50), at 25.0 gwei
Fulfillment gas
232,667 per served request, 3,693,437 max per transaction
User cost
0.0800 USDC fee + 0.0058 USDC gas per request
Keeper
0.0400 USDC fee share, 0.0064 USDC gas per served request

Arc block conditions

Blocks
89 (62,742,120 to 62,742,208), mean interval 0.50 s
Base fee
20 / 20 / 20 gwei (min / median / max)
Gas used ratio
mean 7.5%, max 24.0% of a 30,000,000 gas limit

BURST-500 / 2026-09-18 12:17 UTC

Burst of 500

500 requests opened by ten 50-request transactions broadcast together, served by a primary keeper and a backup keeper that joins when the queue is deep.

Load and concurrency

Requests
500 opened of 500 in the scenario
Request transactions
10 transactions of 50, in 5 blocks; at most 100 in one block
Request span
5 blocks, 2 s

Outcome

Success in 60 s
100% (500 of 500)
Callbacks delivered
500 (consumer counted 500)
Refunds / expired
0 / 0

Completion

Chain seconds
p50 18, p95 25, p99 27, max 27
Blocks
p50 35, p95 49, p99 53, max 53
Wall clock
broadcast to observed fulfillment: p50 19.2 s, p95 26.4 s, max 27.3 s
Epoch publication
400 of 500 requests waited for their epoch packet to be published

Batching and throughput

Fulfillment transactions
35; 0 other applications' requests shared them
Members per transaction
  1. 4
  2. 4
  3. 8
  4. 8
  5. 16
  6. 16
  7. 16
  8. 16
  9. 16
  10. 16
  11. 16
  12. 16
  13. 16
  14. 16
  15. 16
  16. 16
  17. 16
  18. 16
  19. 16
  20. 16
  21. 16
  22. 16
  23. 16
  24. 16
  25. 16
  26. 16
  27. 16
  28. 16
  29. 16
  30. 16
  31. 16
  32. 16
  33. 4
  34. 16
  35. 8
Throughput
22.73/s (500 over 22 s, 35 fulfillment transactions)
Duration
27 s from first request to last fulfillment; 30 s wall clock

Gas and cost

Request gas
195,493 per request (p50), at 25.2 gwei
Fulfillment gas
249,086 per served request, 4,229,234 max per transaction
User cost
0.0800 USDC fee + 0.0051 USDC gas per request
Keeper
0.0400 USDC fee share, 0.0081 USDC gas per served request

Arc block conditions

Blocks
56 (62,740,516 to 62,740,571), mean interval 0.51 s
Base fee
20 / 20 / 20 gwei (min / median / max)
Gas used ratio
mean 16.9%, max 75.9% of a 30,000,000 gas limit

BURST-300-PRIMARY-STOPPED / 2026-09-18 12:19 UTC

Burst of 300, primary stopped

300 requests opened together; the operator stops the primary keeper about 10 seconds in, so the backup keeper serves the rest. The stop is an operator action outside this script and is recorded in the run notes.

Load and concurrency

Requests
300 opened of 300 in the scenario
Request transactions
6 transactions of 50, in 4 blocks; at most 100 in one block
Request span
4 blocks, 2 s

Outcome

Success in 60 s
100% (300 of 300)
Callbacks delivered
300 (consumer counted 300)
Refunds / expired
0 / 0

Completion

Chain seconds
p50 8, p95 17, p99 17, max 18
Blocks
p50 16, p95 34, p99 34, max 36
Wall clock
broadcast to observed fulfillment: p50 9.6 s, p95 17.7 s, max 18.7 s
Epoch publication
250 of 300 requests waited for their epoch packet to be published

Batching and throughput

Fulfillment transactions
22; 0 other applications' requests shared them
Members per transaction
  1. 8
  2. 16
  3. 1
  4. 16
  5. 16
  6. 16
  7. 16
  8. 16
  9. 16
  10. 16
  11. 16
  12. 16
  13. 16
  14. 16
  15. 16
  16. 16
  17. 16
  18. 11
  19. 6
  20. 16
  21. 16
  22. 2
Throughput
21.43/s (300 over 14 s, 22 fulfillment transactions)
Duration
18 s from first request to last fulfillment; 22 s wall clock

Gas and cost

Request gas
195,898 per request (p50), at 25.0 gwei
Fulfillment gas
246,498 per served request, 4,221,846 max per transaction
User cost
0.0800 USDC fee + 0.0051 USDC gas per request
Keeper
0.0400 USDC fee share, 0.0069 USDC gas per served request

Arc block conditions

Blocks
39 (62,740,712 to 62,740,750), mean interval 0.50 s
Base fee
20 / 20 / 20 gwei (min / median / max)
Gas used ratio
mean 15.1%, max 74.9% of a 30,000,000 gas limit

Keeper under test

Hardware and settings as declared by the operator for these runs; they cannot be checked from chain data. Every fulfillment transaction came from 0x61659d9A9A85dA07C36e7d1B35CF0d96CF199Cac, 0xbb2fdE97a5F4855bEf872C71fbb80Be3170127Ee.

Host
x86-64 virtual server (KVM), AMD EPYC-Genoa, 4 vCPU, 7.8 GiB RAM, 256 GB disk
System
Debian 13, Docker 29.8.1; keeper image from commit 5af598e
Settings
POLL_MS 250, TICK_TIMEOUT_SECONDS 20, FULFILL_BATCH_MAX 16, MAX_GAS 6,000,000, MAX_FEE_PER_GAS 100 gwei, FEE_COVERAGE_BPS 10000
Transactions
two lanes: a primary keeper and a backup keeper, each with its own wallet and one nonce lane
RPC
https://rpc.blockdaemon.testnet.arc.io, an Alchemy Arc Testnet endpoint, https://rpc.testnet.arc.network, https://rpc.drpc.testnet.arc.io

Contracts and software

Coordinator proxy
0xd20DA0FF9087d053f0291524Eac12abA1ADBd945
Implementation
0xd20da0dada4352a1a9722be43a2d85923443458c (ERC-1967 slot, matched the manifest before and after every run)
Pricing
minimum fee 0.08 USDC, multiplier 5, overhead 300,000 gas, keeper share 50%
Load consumer
0xd23C36e40444e8E90f1213F23F391C5b1135215f (solc 0.8.28, cancun)
Benchmark software
@d20dao/vrf-sdk@0.4.0, ethers 6.17.0, Node 24.19.0
Script commit
5df5f5b695f8528af1d0fffe674446d54e7f5725, clean working tree
Results commit
8f7cd64036488eace76d6a59c74e0bc5e362c63f
Observation RPC
https://rpc.blockdaemon.testnet.arc.io

Methodology

  1. A benchmark wallet owns LoadConsumer, a small MIT contract that extends the SDK's D20VRFConsumer. One open() call pays quoteFee(100,000) inside the transaction for each request, opens up to 100 raw requests against the public coordinator proxy and records every authenticated callback. Arc Testnet keeps only about 16 pending transactions per sender, so concurrent load has to come from many requests per transaction.
  2. The Node script (run with Node 24.19.0, ethers 6.17.0 and @d20dao/vrf-sdk@0.4.0) reads the coordinator proxy from the public deployment manifest, checks the implementation in the ERC-1967 slot against it, quotes with quoteRequestFee and broadcasts the request transactions for each scenario.
  3. Completion latency is the fulfillment block timestamp minus the request block timestamp, and the same in block numbers. Success means RandomnessFulfilled was accepted at or before the request's deadline (request time plus 60 s); callback delivery, refunds and expiry come from getRequest after settlement.
  4. Batch sizes are the members of each fulfillRandomnessBatch transaction that served a request of the run, including other applications' requests when the keeper combined them. Throughput is a run's fulfilled requests divided by the seconds between its first and last fulfillment block; only a burst measures serving rate, because every request is open from the start. The first transaction's requests count although its block opens the window, so short windows overstate the rate slightly.
  5. Block conditions cover every block from the chain head at the start of a run to the last block holding one of its requests or fulfillments. Percentiles use the nearest-rank method. Wall-clock times are measured by the benchmark machine polling a public RPC every 500 ms.

Limitations

  • A primary and a backup keeper, each with one transaction lane, served the requests on Arc Testnet. The numbers do not describe Arc Mainnet, other hardware or other keeper settings.
  • The testnet keeper also serves other applications, and both the keeper and the benchmark use public RPC endpoints with unknown load and rate limits.
  • Arc block timestamps have whole-second resolution, so latencies in seconds carry up to a second of rounding; block counts are finer at about two blocks per second.
  • Requests are raw words with 100,000 callback gas and a small storage-writing callback. Samples of 20 to 500 requests from single runs make tail percentiles indicative only.
  • These are observations, not an SLA. The protocol's only timing rule is that a request not fulfilled within 60 seconds becomes refundable.

Reproduce

The repository README explains each field, funding and the safety limits. Use a dedicated Arc Testnet wallet; these four runs spent 108.2 test USDC in fees and gas.

git clone https://github.com/d20dao/benchmarks
cd benchmarks && npm ci && npm run compile -- --check
export BENCH_PRIVATE_KEY_FILE=/secure/path/arc-testnet.key
npm run deploy -- --force
npm run suite
npm run summary