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.
| Scenario | Requests | Success in 60 s | Completion, s | Blocks | Fulfillment batches | Throughput | Gas per request | Duration |
|---|---|---|---|---|---|---|---|---|
| Sequential baseline | 20 requests, one at a time20 × 1 in 20 blocks | 100%20 of 20 | 2 / 3 / 14 / 14 | 4 / 6 / 29 / 29 | 20 transactionsmean 1.0, max 1 | 0.28/slimited by arrivals | 296,969+ 301,311 fulfillment | 74 s79 s wall clock |
| Burst of 50 | 50 requests at once1 × 50 in 1 block | 100%50 of 50 | 8 / 10 / 10 / 10 | 17 / 20 / 20 / 20 | 5 transactionsmean 10.0, max 16 | 7.14/s50 over 7 s | 195,898+ 261,391 fulfillment | 10 s14 s wall clock |
| Burst of 200 | 200 requests at once4 × 50 in 2 blocks | 100%200 of 200 | 10 / 14 / 15 / 15 | 20 / 28 / 30 / 30 | 19 transactionsmean 11.2, max 16 | 16.67/s200 over 12 s | 195,493+ 264,097 fulfillment | 16 s19 s wall clock |
| Sustained 5 requests/s | 5 requests/s for 40 s40 × 5 in 40 blocks | 100%200 of 200 | 5 / 8 / 9 / 9 | 9 / 16 / 19 / 19 | 22 transactionsmean 9.1, max 16 | 5.13/slimited by arrivals | 230,680+ 232,667 fulfillment | 42 s47 s wall clock |
| Burst of 500 | 500 requests at once10 × 50 in 5 blocks | 100%500 of 500 | 18 / 25 / 27 / 27 | 35 / 49 / 53 / 53 | 35 transactionsmean 14.3, max 16 | 22.73/s500 over 22 s | 195,493+ 249,086 fulfillment | 27 s30 s wall clock |
| Burst of 300, primary stopped | 300 requests at once6 × 50 in 4 blocks | 100%300 of 300 | 8 / 17 / 17 / 18 | 16 / 34 / 34 / 36 | 22 transactionsmean 13.6, max 16 | 21.43/s300 over 14 s | 195,898+ 246,498 fulfillment | 18 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
- 1
- 1
- 1
- 1
- 1
- 1
- 1
- 1
- 1
- 1
- 1
- 1
- 1
- 1
- 1
- 1
- 1
- 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
- 4
- 8
- 8
- 16
- 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
- 4
- 8
- 8
- 8
- 16
- 8
- 16
- 16
- 16
- 16
- 16
- 16
- 16
- 8
- 8
- 8
- 16
- 4
- 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
- 5
- 2
- 13
- 11
- 9
- 5
- 5
- 5
- 10
- 10
- 5
- 10
- 10
- 16
- 4
- 8
- 7
- 15
- 16
- 16
- 16
- 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
- 4
- 4
- 8
- 8
- 16
- 16
- 16
- 16
- 16
- 16
- 16
- 16
- 16
- 16
- 16
- 16
- 16
- 16
- 16
- 16
- 16
- 16
- 16
- 16
- 16
- 16
- 16
- 16
- 16
- 16
- 16
- 16
- 4
- 16
- 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
- 8
- 16
- 1
- 16
- 16
- 16
- 16
- 16
- 16
- 16
- 16
- 16
- 16
- 16
- 16
- 16
- 16
- 11
- 6
- 16
- 16
- 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
- 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.
- 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.
- 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.
- 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.
- 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