sei-load
A load testing tool for Sei Chain that generates transactions and measures performance.
Quick Start
1. Build
make build2. Create Config
cp profiles/local.json my-config.jsonEdit my-config.json:
{
"endpoints": ["http://localhost:8545"],
"chainId": 713714,
"scenarios": [
{"name": "EVMTransfer", "weight": 100}
],
"accounts": {
"count": 100,
"newAccountRate": 0.1
},
"settings": {
"tps": 100,
"statsInterval": "10s",
"bufferSize": 1000,
"trackUserLatency": true
}
}3. Run
./seiload --config my-config.jsonCommand Line Options
| Flag | Default | Description |
|---|---|---|
--config, -c | Config file path (required) | |
--tps, -t | 0 | Transactions per second (0 = unlimited) |
--stats-interval, -s | 10s | Stats logging interval |
--buffer-size, -b | 1000 | Sender queue size |
--dry-run | false | Simulate without sending |
--debug | false | Log each transaction |
--track-receipts | false | Enable the block-indexed tx→inclusion tracker (stamps InclusionTime; reports included/expired/inflight-at-shutdown) |
--inclusion-reap-after | 30s | How long an un-included tx waits before reaping as expired (tune to expected inclusion time on congested chains) |
--track-blocks | false | Track block statistics |
--track-user-latency | false | Track user latency metrics |
--prewarm | false | Prewarm accounts before test |
--chain-file | Contract registry file naming the target chain and its deployed contracts. Repeatable; each layers over the registry compiled into the binary, and a later file wins. A path that does not exist fails startup. | |
--chain-record-path | Where to write a chain file describing what this run deployed, for an operator to review and commit. In a pod use /dev/stdout. Requires genesisHash in the profile. |
Examples
Basic Load Test
./seiload --config my-config.json --tps 100High Throughput Test
./seiload --config my-config.json --buffer-size 2000 --max-in-flight 20000Debug Mode
./seiload --config my-config.json --debug --dry-runWith Receipt Tracking
./seiload --config my-config.json --track-receipts --track-blocksUser Latency Monitoring
./seiload --config my-config.json --track-user-latency --stats-interval 5sConfiguration
Basic Structure
{
"endpoints": ["http://localhost:8545"],
"chainId": 713714,
"scenarios": [...],
"accounts": {...},
"settings": {...}
}Scenarios
"scenarios": [
{"name": "EVMTransfer", "weight": 50},
{"name": "ERC20", "weight": 30},
{"name": "ERC721", "weight": 20}
]Account Management
"accounts": {
"count": 100,
"newAccountRate": 0.1
}Settings
"settings": {
"tps": 100,
"statsInterval": "10s",
"bufferSize": 1000,
"trackUserLatency": true
}Settings Precedence: CLI flags > Config file settings > Default values
Available settings:
tps: Transactions per second (0 = unlimited)statsInterval: Stats logging interval (e.g., "10s", "5m")bufferSize: Sender queue sizedryRun: Simulate without sending transactionsdebug: Enable debug loggingtrackReceipts: Track transaction receiptstrackBlocks: Track block statisticstrackUserLatency: Track user latency metricsprewarm: Prewarm accounts before test
Available Scenarios
- EVMTransfer: Simple ETH transfers
- ERC20: ERC20 token operations
- ERC20Noop: ERC20 no-op transactions
- ERC20Conflict: ERC20 conflicting transactions
- ERC721: NFT operations
Output
Standard Metrics
throughput tps=133.00, txs=1330, latency(avg=8ms p50=5ms p99=27ms max=494ms)User Latency (with --track-user-latency)
user latency height=5191 txs=32 min=1s p50=2s max=5sBlock Stats (with --track-blocks)
blocks height=5191 time(p50=2s p99=5s max=8s) gas(p50=21000 p99=50000 max=100000)Development
Before you push
Run the full local CI gate in one command:
make verify # check-lint-pin + lint + test + build + CLI --help + check-bindingsmake verify mirrors the gating CI jobs (build-and-test, bindings-check):
the golangci-lint version-pin check, lint, test, compile the CLI, and a --help smoke. The only gating step it does
not run is CI's dry-run smoke (a backgrounded seiload --dry-run killed after
5s) — that asserts no exit code and stays CI-only, so a green verify is a strong
signal but not a literal guarantee of that one step. Install the pinned toolchain
once first so your local results match CI:
make install-tools # full toolchain: Node (via nvm), solc, abigen, golangci-lint (pinned to v2.12.2)
# or, for the linter only:
make install-lint # golangci-lint pinned to v2.12.2golangci-lint is pinned to a specific version (Makefile GOLANGCI_VERSION,
the workflow's golangci-lint-action version:, and .golangci.yml); make lint
warns if the binary on your PATH differs. A drifting/unpinned linter is the usual
"passes locally, fails CI" trap — make install-lint gives you the exact CI version.
Build
make buildTest
make test # runs with -raceLint
make lintClean
make cleanTroubleshooting
Connection Issues
- Check endpoint URLs in config
- Verify network connectivity
- Try
--dry-runto test config
Low Performance
- Increase
--buffer-size - Increase
--max-in-flightfor open-loop runs
Memory Issues
- Reduce
--buffer-size - Reduce
--max-in-flightfor open-loop runs - Disable receipt tracking