feat(bench-tps): add deposit & withdraw e2e load testing flows#89
Merged
feat(bench-tps): add deposit & withdraw e2e load testing flows#89
Conversation
Adds two new bench-tps subcommands for end-to-end load testing:
- `deposit`: sets up L1 escrow instance, mints tokens, runs deposit
load against the escrow program, and tracks l2_minted via
operator-solana metrics.
- `withdraw`: sets up L1 escrow + L2 withdrawer accounts (including
L1 ATAs for recipients), runs WithdrawFunds burn load on L2, and
tracks l1_released via operator-contra metrics.
Key fix: create L1 ATAs for withdrawer keypairs during setup.
validate_ata() in the escrow program returns InvalidInstructionData
when the recipient ATA is empty, causing all ReleaseFunds calls to
fail silently with "invalid instruction data".
Also fixes docker-compose: add COMMON_ESCROW_INSTANCE_ID passthrough,
METRICS_PORT=9103, and port mapping to operator-contra so the bench
can scrape l1_released from outside the Docker network.
- Add deposit flow: L1 escrow Deposit load test with e2e measurement via
operator-solana mints (contra_operator_mints_sent_total{escrow})
- Add withdraw flow: L2 WithdrawFunds burn + L1 ReleaseFunds e2e path;
setup creates L1 ATAs for withdrawers to fix InvalidInstructionData on
ReleaseFunds; memo nonce added to ensure unique tx signatures
- Add COMMON_SOURCE_RPC_URL to operator-contra in docker-compose.devnet.yml
and scripts/devnet/config/operator-contra.toml
- Add bench-tps/.env.sample.devnet with devnet-specific placeholders
- Restructure Grafana dashboard: separate Deposit and Withdraw flow sections
each showing pipeline stages in order (sent → indexer → operator → confirmed);
remove Mixed Load panels
- Improve run.sh cleanup: use `down` instead of `stop`, trap EXIT INT TERM ERR
with a deduplication guard to ensure teardown on all exit paths
- Update README and BOTTLENECK_ANALYSIS with per-flow run/measure/debug guides
- Fix clippy: remove redundant serde_json imports, fix over-indented doc comment
a5bfa6e to
9b64c5e
Compare
Huzaifa696
commented
Apr 2, 2026
dev-jodee
reviewed
Apr 2, 2026
Collaborator
dev-jodee
left a comment
There was a problem hiding this comment.
2 small nits + what we discussed on slack
dev-jodee
approved these changes
Apr 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Extends
contra-bench-tpswith two new subcommands —depositandwithdraw— that load-test the full cross-chain flows end-to-end.New flows
Deposit (Solana -> Contra)
Sends
Depositinstructions to the escrow program on the Solana validator. Setup funds each depositor account with SOL and Solana tokens, creates the escrow instance PDA, registers the mint, and seeds the instance ATA. During the load phase, each transaction transfers tokens from a depositor's Solana ATA into the escrow instance ATA. Measuressolana_landed(validator confirmations viagetTransactionCount) andcontra_minted(e2e Contra mints confirmed byoperator-solanaviacontra_operator_mints_sent_total{escrow}).The CLI final summary prints
sent,solana_landed,contra_minted,drop(solana_landed - contra_minted) and TPS. The Grafana dashboard shows four panels in order: Solana Sent TPS → Indexer Solana events indexed → Operator fetch rate + backlog depth → Contra Mint Rate.Deposit flow dashboard
<img width="1477" height="664" alt="Screenshot from 2026-04-02 16-46-29" src="https://github.com/user-attachments/assets/f5ce0ea7-77df-4924-986b-54451dca2468" />Withdraw (Contra → Solana)
Sends
WithdrawFunds(burn) transactions to the Contra write-node. Setup initialises the same escrow infrastructure on Solana, creates Contra ATAs funded with tokens, and creates Solana ATAs for each withdrawer. During the load phase, each transaction burns tokens from the withdrawer's Contra ATA. Measurescontra_burned(Contra confirmations viagetTransactionCount) andsolana_released(e2e Solana releases confirmed byoperator-contraviacontra_operator_mints_sent_total{withdraw}).The CLI final summary prints
sent,contra_burned,solana_released,drop(contra_burned - solana_released) and TPS. The Grafana dashboard shows four panels in order — Contra Sent/Landed TPS → Indexer Contra events indexed → Operator fetch rate + backlog depth → Solana Release Rate.Withdraw flow dashboard
<img width="1477" height="664" alt="Screenshot from 2026-04-02 16-48-38" src="https://github.com/user-attachments/assets/a25912d8-128d-45ea-92f5-d311c02cdf80" />Changes outside
bench-tpsdocker-compose.devnet.yml—operator-contrawas missingCOMMON_SOURCE_RPC_URL, which is the Contra gateway URL the operator uses to read withdrawal burn events. Without it,operator-contracannot process any withdrawals.METRICS_PORTalso added to all indexer/operator services for observability.bench-tps/.env.sample.devnet— New env sample for devnet deploymentsscripts/devnet/config/operator-contra.toml— Addedsource_rpc_urlfieldindexer/config/local/operator-contra.toml— Addedsource_rpc_url = "http://localhost:8898"so the local operator-contra reads from the Contra gateway.indexer/config/local/operator-solana.toml— Fixedrpc_url, should be Contra gateway 8898 for reading mint events and addedsource_rpc_url = "http://localhost:18899"for Solana.grafana/dashboards/contra-bench.json— Restructured dashboard into separate Deposit Flow (Solana → Contra) and Withdraw Flow (Contra → Solana) sections. This makes it straightforward to see which stage is the bottleneck during a run.bench-tps/scripts/run.sh— Cleanup now callsdocker compose downand made sure that the containers' teardown fires exactly once regardless of how the script exits.Docs
README.mdandBOTTLENECK_ANALYSIS.mdupdated with per-flow sections covering how to run, what each metric means, and a panel-by-panel bottleneck guide.Test plan
./bench-tps/scripts/run.sh deposit— verifysolana_landedandcontra_mintedappear in final summary./bench-tps/scripts/run.sh withdraw— verifycontra_burnedandsolana_releasedare non-zerodocker compose downand all containers stopCoverage Report
Extends
contra-bench-tpswith two new subcommands —depositandwithdraw— that load-test the full cross-chain flows end-to-end.New flows
Deposit (Solana -> Contra)
Sends
Depositinstructions to the escrow program on the Solana validator. Setup funds each depositor account with SOL and Solana tokens, creates the escrow instance PDA, registers the mint, and seeds the instance ATA. During the load phase, each transaction transfers tokens from a depositor's Solana ATA into the escrow instance ATA. Measuressolana_landed(validator confirmations viagetTransactionCount) andcontra_minted(e2e Contra mints confirmed byoperator-solanaviacontra_operator_mints_sent_total{escrow}).The CLI final summary prints
sent,solana_landed,contra_minted,drop(solana_landed - contra_minted) and TPS. The Grafana dashboard shows four panels in order: Solana Sent TPS → Indexer Solana events indexed → Operator fetch rate + backlog depth → Contra Mint Rate.Deposit flow dashboard
Withdraw (Contra → Solana)
Sends
WithdrawFunds(burn) transactions to the Contra write-node. Setup initialises the same escrow infrastructure on Solana, creates Contra ATAs funded with tokens, and creates Solana ATAs for each withdrawer. During the load phase, each transaction burns tokens from the withdrawer's Contra ATA. Measurescontra_burned(Contra confirmations viagetTransactionCount) andsolana_released(e2e Solana releases confirmed byoperator-contraviacontra_operator_mints_sent_total{withdraw}).The CLI final summary prints
sent,contra_burned,solana_released,drop(contra_burned - solana_released) and TPS. The Grafana dashboard shows four panels in order — Contra Sent/Landed TPS → Indexer Contra events indexed → Operator fetch rate + backlog depth → Solana Release Rate.Withdraw flow dashboard
Changes outside
bench-tpsdocker-compose.devnet.yml—operator-contrawas missingCOMMON_SOURCE_RPC_URL, which is the Contra gateway URL the operator uses to read withdrawal burn events. Without it,operator-contracannot process any withdrawals.METRICS_PORTalso added to all indexer/operator services for observability.bench-tps/.env.sample.devnet— New env sample for devnet deploymentsscripts/devnet/config/operator-contra.toml— Addedsource_rpc_urlfieldindexer/config/local/operator-contra.toml— Addedsource_rpc_url = "http://localhost:8898"so the local operator-contra reads from the Contra gateway.indexer/config/local/operator-solana.toml— Fixedrpc_url, should be Contra gateway 8898 for reading mint events and addedsource_rpc_url = "http://localhost:18899"for Solana.grafana/dashboards/contra-bench.json— Restructured dashboard into separate Deposit Flow (Solana → Contra) and Withdraw Flow (Contra → Solana) sections. This makes it straightforward to see which stage is the bottleneck during a run.bench-tps/scripts/run.sh— Cleanup now callsdocker compose downand made sure that the containers' teardown fires exactly once regardless of how the script exits.Docs
README.mdandBOTTLENECK_ANALYSIS.mdupdated with per-flow sections covering how to run, what each metric means, and a panel-by-panel bottleneck guide.Test plan
./bench-tps/scripts/run.sh deposit— verifysolana_landedandcontra_mintedappear in final summary./bench-tps/scripts/run.sh withdraw— verifycontra_burnedandsolana_releasedare non-zerodocker compose downand all containers stopCoverage Report