Skip to benchmarks
D20DAO

BENCHMARKS / ARC TESTNET

Load tests, with receipts.

4 scenarios run on 2026-09-17 against the public D20DAO keeper on Arc Testnet: 470 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 one keeper on a testnet, not an SLA.

Served within 60 s
470 / 470
all scenarios; 0 refunds, 0 expired
Burst of 200
6.90 fulfillments/s
over 29 s, 13 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 / 3 / 34 / 6 / 6 / 620 transactionsmean 1.0, max 10.34/slimited by arrivals296,868+ 296,981 fulfillment61 s66 s wall clock
Burst of 5050 requests at once1 × 50 in 1 block100%50 of 5010 / 12 / 12 / 1218 / 23 / 23 / 234 transactionsmean 12.5, max 167.14/s50 over 7 s195,809+ 262,141 fulfillment12 s16 s wall clock
Burst of 200200 requests at once4 × 50 in 2 blocks100%200 of 20021 / 33 / 35 / 3541 / 65 / 70 / 7013 transactionsmean 15.4, max 166.90/s200 over 29 s195,404+ 260,834 fulfillment35 s39 s wall clock
Sustained 5 requests/s5 requests/s for 40 s40 × 5 in 40 blocks100%200 of 2005 / 7 / 8 / 810 / 14 / 14 / 1424 transactionsmean 8.3, max 154.88/slimited by arrivals230,591+ 235,122 fulfillment45 s48 s wall clock

SEQUENTIAL-20 / 2026-09-17 02:13 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 planned
Request transactions
20 transactions of 1, in 20 blocks; at most 1 in one block
Request span
119 blocks, 59 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 3, max 3
Blocks
p50 4, p95 6, p99 6, max 6
Wall clock
broadcast to observed fulfillment: p50 2.9 s, p95 3.9 s, max 3.9 s
Epoch publication
0 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.34/s, limited by arrivals (20 over 59 s, 20 fulfillment transactions)
Duration
61 s from first request to last fulfillment; 66 s wall clock

Gas and cost

Request gas
296,868 per request (p50), at 25.0 gwei
Fulfillment gas
296,981 per served request, 324,300 max per transaction
User cost
0.0800 USDC fee + 0.0074 USDC gas per request
Keeper
0.0400 USDC fee share, 0.0083 USDC gas per served request

Arc block conditions

Blocks
126 (62,500,922 to 62,501,047), mean interval 0.50 s
Base fee
20 / 20 / 20 gwei (min / median / max)
Gas used ratio
mean 3.3%, max 10.6% of a 30,000,000 gas limit

BURST-50 / 2026-09-17 02:17 UTC

Burst of 50

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

Load and concurrency

Requests
50 opened of 50 planned
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 10, p95 12, p99 12, max 12
Blocks
p50 18, p95 23, p99 23, max 23
Wall clock
broadcast to observed fulfillment: p50 10.6 s, p95 12.7 s, max 12.7 s
Epoch publication
50 of 50 requests waited for their epoch packet to be published

Batching and throughput

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

Gas and cost

Request gas
195,809 per request (p50), at 25.0 gwei
Fulfillment gas
262,141 per served request, 4,166,740 max per transaction
User cost
0.0800 USDC fee + 0.0049 USDC gas per request
Keeper
0.0400 USDC fee share, 0.0078 USDC gas per served request

Arc block conditions

Blocks
27 (62,501,300 to 62,501,326), mean interval 0.50 s
Base fee
20 / 20 / 20 gwei (min / median / max)
Gas used ratio
mean 8.1%, max 34.3% of a 30,000,000 gas limit

BURST-200 / 2026-09-17 02:19 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 planned
Request transactions
4 transactions of 50, in 2 blocks; at most 100 in one block
Request span
2 blocks, 0 s

Outcome

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

Completion

Chain seconds
p50 21, p95 33, p99 35, max 35
Blocks
p50 41, p95 65, p99 70, max 70
Wall clock
broadcast to observed fulfillment: p50 22.3 s, p95 34.5 s, max 36.5 s
Epoch publication
200 of 200 requests waited for their epoch packet to be published

Batching and throughput

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

Gas and cost

Request gas
195,404 per request (p50), at 25.0 gwei
Fulfillment gas
260,834 per served request, 4,245,702 max per transaction
User cost
0.0800 USDC fee + 0.0049 USDC gas per request
Keeper
0.0400 USDC fee share, 0.0075 USDC gas per served request

Arc block conditions

Blocks
73 (62,501,574 to 62,501,646), mean interval 0.50 s
Base fee
20 / 20 / 20 gwei (min / median / max)
Gas used ratio
mean 8.0%, max 69.7% of a 30,000,000 gas limit

SUSTAINED-5RPS-40S / 2026-09-17 02:22 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 planned
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 7, p99 8, max 8
Blocks
p50 10, p95 14, p99 14, max 14
Wall clock
broadcast to observed fulfillment: p50 6.0 s, p95 7.8 s, max 8.4 s
Epoch publication
15 of 200 requests waited for their epoch packet to be published

Batching and throughput

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

Gas and cost

Request gas
230,591 per request (p50), at 25.0 gwei
Fulfillment gas
235,122 per served request, 3,446,919 max per transaction
User cost
0.0800 USDC fee + 0.0057 USDC gas per request
Keeper
0.0400 USDC fee share, 0.0063 USDC gas per served request

Arc block conditions

Blocks
92 (62,501,898 to 62,501,989), mean interval 0.52 s
Base fee
20 / 20 / 20 gwei (min / median / max)
Gas used ratio
mean 7.5%, max 25.7% 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, the keeper address in the public deployment manifest.

Host
Hetzner vServer, AMD EPYC-Rome, 4 vCPU, 7.6 GiB RAM, 75 GB disk
System
Ubuntu 24.04.4 LTS, Docker 29.8.1; keeper image from commit d15b4a0
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
one nonce lane: one fulfillment transaction at a time
RPC
https://rpc.testnet.arc.io, https://rpc.blockdaemon.testnet.arc.io, https://rpc.drpc.testnet.arc.io

Contracts and software

Coordinator proxy
0xd20DA0FF9087d053f0291524Eac12abA1ADBd945
Implementation
0xd20da0c375cefcda65703699a4090237057e9b68 (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
0x7cFf09B81D013f1cbBd216607879BF529760b1B3 (solc 0.8.28, cancun)
Benchmark software
@d20dao/vrf-sdk@0.3.3, ethers 6.17.0, Node 24.19.0
Script commit
4dd5f79693ad5b9faee9912b21c97ed5feeb7165, clean working tree
Results commit
8e727a822305388c03d7ffc55f9a0ce9e148ed21
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.3.3) 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

  • One keeper process with one transaction lane served every request, 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 200 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 40.1 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