feat(funding): fund the account pool from a root key for real-chain runs - #40
Conversation
seiload generates random EVM accounts and never funds them, so it only works
against mock chains (mock_balances auto-tops EVM balances) or a fresh genesis
that pre-funds them. To run against a real, long-running chain (e.g. arctic-1)
the accounts need a balance.
Add an optional `funding` config block: given one funded root key, seiload
funds its generated pool via the Disperse contract at startup, before prewarm
and dispatch.
- config: `FundingConfig{rootKeyEnv|rootKey, fundAmountWei, batchSize,
disperseAddress}`; `BigInt` string-JSON wrapper (wei exceeds 2^53);
`ValidateFunding` rejects `newAccountRate>0` under funding (on-demand
accounts can't be funded) and a missing key.
- funder: parse root key → dial → enumerate pool → skip already-funded
(idempotent across restarts) → deploy/reuse Disperse → batch disperseEther.
The root's first EVM tx auto-associates its cosmos balance to the EVM side.
- types: `AccountPool.GetAccounts()` to enumerate the fixed pool.
- main: fund after sender init, before prewarm; no-op when `funding` is nil
(mock/genesis path unchanged) or in dry-run.
- profiles/arctic-1.json: example real-chain profile (chainId 713715).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
PR SummaryHigh Risk Overview Config introduces Runtime adds the Includes Reviewed by Cursor Bugbot for commit 414ec7c. Bugbot is set up for automated code reviews on this repo. Configure here. |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit b655eeb. Configure here.
Cross-review (systems / sei-network / idiom / security-dissent) findings: - [BLOCKS, systems] filterUnderfunded leaked ~17 goroutines / deadlocked on the early-error or ctx-cancel path (unbuffered channels, no cancellation). Rewritten with errgroup.WithContext + SetLimit: bounded, cancels in-flight on first error or ctx done, no leak. - [BLOCKS, security] dropped `disperseAddress` — it sent the root's value to an unvalidated, externally-configurable contract. Always deploy a fresh Disperse (cheap; also the root's auto-association tx). - [security] removed the inline `rootKey` config field (committed-secret footgun); key now comes from `rootKeyFile` (preferred — mounted file, not in the process env) or `rootKeyEnv` (fallback). - [security] assert receipt.Status == success after every WaitMined (deploy + each disperse) and CodeAt-non-empty after deploy — a lying RPC or reverted tx no longer passes silently. - [security] corrected the FundAccounts doc: seiload generates a fresh random pool each start, so a restart funds a new set (prior balances stranded — bounded, funny-money devnet); the skip guards within-run double-funding. - [sei-network] documented that the root must be funded at its EVM (cast) address or pre-associated. - [idiom] ValidateFunding doc now starts with the symbol name; BigInt error is type-scoped (reusable); added BigInt.ToBigInt(); unified `funding:` prefix. - locked the sequential-nonce invariant with a comment. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Final idiom/style nits from cross-review (both non-blocking): remove the `a := a` shadow (per-iteration loop vars since Go 1.22; module floor 1.25), and comment that TrimSpace handles the trailing newline a SOPS-mounted key file carries. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Both Cursor Bugbot inline findings were flagged against the first commit and are already resolved as of
Also addressed from the same review: dropped the unvalidated |
Move the why/flow/association/self-deploy/nonce/idempotency narrative out of scattered function and inline comments into a single funder/doc.go package document (renders as sectioned go doc). Function comments are now concise and point to the package doc; load-bearing code-site notes (nonce invariant, TrimSpace, shared-amount) stay as one-liners. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Cut/trim restating-the-symbol comments (ToBigInt, GasFeeCap "cap"), de-dup the mock/genesis-unchanged clause to a single home (doc.go), and trim the main.go call-site comment to the load-bearing prewarm/dispatch ordering. Comments only; no logic change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| // balances are stranded. That is acceptable on a funny-money devnet and bounded | ||
| // by the root balance. The already-funded skip guards against double-funding | ||
| // within a single run, not across restarts. | ||
| package funder |
There was a problem hiding this comment.
Does this file need to be a README instead? it seems it doesn't have any code 🤔

Problem
seiload generates random EVM accounts and never funds them, so today it only works against mock chains (
mock_balancesauto-tops EVM balances at execution) or a fresh genesis that pre-funds them. Pointed at a real, long-running chain (e.g. arctic-1), every generated account is at zero balance and every transfer fails.What
Add an optional
fundingconfig block. Given one funded root key, seiload funds its generated account pool via the existing Disperse contract at startup — before prewarm and dispatch — so it can drive load on a real chain. Absent the block, behavior is byte-for-byte unchanged (mock/genesis path).How
funding.go):FundingConfig; aBigIntstring-JSON wrapper (wei exceeds 2^53 so JSON numbers would lose precision);ValidateFunding()rejects a missing key andnewAccountRate>0under funding (on-demand accounts can't be funded → their first tx would fail for gas).funder/funder.go): parse root key → dial → enumerate the pool → skip already-funded accounts (idempotent across pod restarts) → deploy or reuse Disperse → batchdisperseEther. The root's first EVM tx auto-associates its cosmos balance to the EVM side (Sei ante handler), so no explicit association step.AccountPool.GetAccounts()to enumerate the fixed pool.fundingis nil or in dry-run.chainId: 713715— arctic-1's EVM chain id, not the713714local default).Test
config/funding_test.go: BigInt string round-trip (incl. >2^53 + rejecting bare JSON numbers), funding defaults, andValidateFundingcases.go build ./...,go vet,go test ./config/... ./types/... ./generator/...all green;gofmtclean.Why (context)
Restores the synthetic load that kept arctic-1 at ~19 TPS (it stopped when the migration disarmed the EC2 node that ran the old co-located loadtest), and lets us run a load generator per region to study us-east-2 commit participation. Funded from recovered genesis test accounts (funny money); the key ships as one SOPS secret per region on the platform side.
🤖 Generated with Claude Code