Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
6df5694
loadtest: WIP first version of a gas limiter based on a oscillation c…
tclemos Oct 18, 2025
d1b2b61
add traces; add gas limiter readme; fix infinte loadtest loop;
tclemos Oct 21, 2025
cb8eec5
add gas pricer; add flags to gasmanager; rename to gasmanager package
tclemos Oct 21, 2025
df895e6
add tx-gas-chart command; adjusts to gas manager
tclemos Oct 24, 2025
6913ade
refactoring loadtest gas price manager waves and tx-gas-chart;
tclemos Oct 29, 2025
efd46ad
refactoring loadtest gas price manager waves and tx-gas-chart;
tclemos Oct 30, 2025
14bde21
gas manager and tx-gas-chart refactoring and docs;
tclemos Nov 4, 2025
a54d7a5
adds usage documentation for tx-gas-chart
tclemos Nov 4, 2025
a2e8784
improves documentation for gasmanager package
tclemos Nov 4, 2025
a6306b5
Merge branch 'main' into thiago/loadtest-gas-oscillation
tclemos Nov 4, 2025
281d293
refactoring
tclemos Nov 4, 2025
c84d18a
make gen-doc
tclemos Nov 5, 2025
045b61b
Merge branch 'main' into thiago/loadtest-gas-oscillation
tclemos Nov 5, 2025
8be800a
small doc formating
tclemos Nov 5, 2025
094b8c8
fix: nil pointer dereference in contract-call loadtest mode
praetoriansentry Nov 6, 2025
e1143f6
loadtest: remove infinite; gas provider wait first new block to provi…
tclemos Nov 10, 2025
bfa4469
make gen-doc
tclemos Nov 10, 2025
cd1dc88
Merge branch 'main' into thiago/loadtest-gas-oscillation
tclemos Nov 10, 2025
9ec1165
WIP
tclemos Nov 12, 2025
9a7c411
Merge branch 'main' into thiago/loadtest-gas-oscillation
tclemos Nov 12, 2025
cf4b2fd
tx-gas-chart: make it compatible with op-stack deposit txs
tclemos Dec 5, 2025
8487df0
chart header fix
tclemos Dec 9, 2025
d4e585b
Merge branch 'main' into thiago/loadtest-gas-oscillation
tclemos Dec 29, 2025
3bb36dd
fix merge conflicts
tclemos Dec 29, 2025
b31daed
tx-gas-chart: allow start block to be zero
tclemos Dec 29, 2025
a71900b
fix codeql issues
tclemos Dec 29, 2025
2682887
add unit tests to TestGetSenderFromTx
tclemos Dec 29, 2025
b8befb7
Merge branch 'main' into thiago/loadtest-gas-oscillation
tclemos Jan 2, 2026
88c8bfd
fix: correct variation formula in DynamicGasPriceStrategy
tclemos Jan 5, 2026
fd50259
fix: add context cancellation support to GasVault.SpendOrWaitAvailabl…
tclemos Jan 5, 2026
e10edd4
fix: use passed context instead of creating new root context in runLo…
tclemos Jan 5, 2026
d2ca6a2
fix: add validation for empty gas prices in DynamicGasPriceStrategy
tclemos Jan 5, 2026
3ab54a4
fix: use cmd.Context() instead of context.Background() in txgaschart
tclemos Jan 5, 2026
400665f
Merge branch 'main' into thiago/loadtest-gas-oscillation
tclemos Jan 5, 2026
372bbb6
loadtest: revert debug comments for random mode
tclemos Jan 5, 2026
ed2a81d
loadtest: add comment for better explanation
tclemos Jan 5, 2026
9c34dba
loadtest: make lint
tclemos Jan 5, 2026
0f34ce6
txgaschart: improve usage.md readability
tclemos Jan 5, 2026
77934a4
docs: update README to reflect context cancellation changes and fix m…
tclemos Jan 5, 2026
c7b611d
fix: prevent precision loss and overflow in average block gas calcula…
tclemos Jan 5, 2026
57b645b
test: add comprehensive unit tests for GasVault
tclemos Jan 5, 2026
19b76be
test: add comprehensive unit tests for gas pricing strategies
tclemos Jan 5, 2026
3efbc01
test: add comprehensive unit tests for OscillatingGasProvider
tclemos Jan 5, 2026
c9b0f84
docs: add lessons learned to CLAUDE.md for future sessions
tclemos Jan 5, 2026
0c6543f
fix: remove impossible int > MaxInt64 check flagged by staticcheck
tclemos Jan 5, 2026
837d138
make gen-doc
tclemos Jan 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 154 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,14 @@ The tool supports configuration via:
- Use zerolog for structured, performant logging throughout the project

## Development Guidelines
- Use conventional commit messages

### Commit Messages
- Use simple, concise one-line commit messages
- Format: `type: brief description of what was changed`
- Keep it direct and to the point - avoid long explanations in commit message
- Example: `fix: correct variation formula in DynamicGasPriceStrategy`
- Example: `test: add comprehensive unit tests for GasVault`
- Example: `docs: update README to reflect context cancellation changes`

## Development Memories
- Use `make build` to build polycli
Expand Down Expand Up @@ -294,6 +301,152 @@ go func() {
}()
```

## Additional Patterns & Lessons Learned

### Random Variation Formula
When implementing random variation within a percentage range:

```go
// ✅ DO: Correct uniform distribution
variationMin := 1.0 - variation // e.g., 0.7 for ±30%
variationMax := 1.0 + variation // e.g., 1.3 for ±30%
factor := variationMin + rand.Float64() * (variationMax - variationMin)
// Result: uniformly distributed between 0.7 and 1.3

// ❌ DON'T: Incorrect - produces wrong range
factor := variationMin + rand.Float64() * variationMax
// Result: distributed between 0.7 and 2.0 (not ±30%!)
```

### Type Conversion Safety (big.Int and int64)
When converting between types with different ranges:

```go
// ✅ DO: Check bounds before conversion
numBlocks := len(blocks)
if numBlocks == 0 {
avgGasUsed = 0
} else if numBlocks > math.MaxInt64 {
log.Warn().Msg("value exceeds int64 max")
avgGasUsed = 0
} else {
result := new(big.Int).Div(total, big.NewInt(int64(numBlocks)))
if result.IsUint64() {
avgGasUsed = result.Uint64()
} else {
avgGasUsed = math.MaxUint64
log.Warn().Msg("result exceeds uint64 max")
}
}

// ❌ DON'T: Blindly convert without checking
avgGasUsed = new(big.Int).Div(total, big.NewInt(int64(len(blocks)))).Uint64()
// Can panic if len(blocks) > MaxInt64 or result > MaxUint64
```

### Blocking Operations Must Accept Context
Any operation that can block must accept `context.Context` for cancellation:

```go
// ✅ DO: Accept context, use ticker, handle cancellation
func (v *Vault) SpendOrWait(ctx context.Context, amount uint64) error {
ticker := time.NewTicker(100 * time.Millisecond)
defer ticker.Stop()

for {
if v.trySpend(amount) {
return nil
}
select {
case <-ticker.C:
continue
case <-ctx.Done():
return ctx.Err()
}
}
}

// ❌ DON'T: Use bare time.Sleep in infinite loop
func (v *Vault) SpendOrWait(amount uint64) {
for {
if v.trySpend(amount) {
break
}
time.Sleep(100 * time.Millisecond) // Cannot be cancelled!
}
}
```

### Constructor Validation
Validate inputs in constructors and return errors:

```go
// ✅ DO: Validate and return error
func NewDynamicPricer(config Config) (*DynamicPricer, error) {
if len(config.Prices) == 0 {
return nil, fmt.Errorf("config.Prices cannot be empty")
}
return &DynamicPricer{config: config}, nil
}

// ❌ DON'T: Panic on invalid input or allow invalid state
func NewDynamicPricer(config Config) *DynamicPricer {
// Will panic on first use if Prices is empty
return &DynamicPricer{config: config}
}
```

### Documentation Must Match Implementation
Keep documentation (especially README files) synchronized with code:

**Critical to document:**
- Function signatures including context parameters and return types
- Error return conditions (not just success path)
- Async/timing behavior (e.g., "vault has zero budget until first block header received")
- Cancellation behavior

**Example:**
```markdown
<!-- ✅ DO: Accurate signature and behavior -->
- **`SpendOrWaitAvailableBudget(context.Context, uint64) error`**:
Attempts to spend gas; blocks if insufficient budget is available or until context is cancelled.
Returns nil on success, ctx.Err() if cancelled.

<!-- ❌ DON'T: Outdated or incomplete -->
- **`SpendOrWaitAvailableBudget(uint64)`**:
Attempts to spend gas; blocks if insufficient budget is available.
```

### Test Coverage Requirements
When adding new components, always include comprehensive unit tests:

**Required test categories:**
- Basic functionality (creation, getters, setters)
- Edge cases (zero values, nil inputs, overflow, division by zero)
- Error conditions (invalid configs, failed operations)
- Concurrency (multiple goroutines, race conditions)
- Context cancellation (immediate cancel, timeout, graceful shutdown)
- Blocking/waiting behavior (wait then succeed, wait then timeout)

**Pattern for concurrent tests:**
```go
func TestConcurrentAccess(t *testing.T) {
component := NewComponent()
const numGoroutines = 100
var wg sync.WaitGroup

for i := 0; i < numGoroutines; i++ {
wg.Add(1)
go func() {
defer wg.Done()
// Perform concurrent operations
}()
}
wg.Wait()
// Verify final state
}
```

## Code Style

### Cobra Flags
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ Note: Do not modify this section! It is auto-generated by `cobra` using `make ge

- [polycli signer](doc/polycli_signer.md) - Utilities for security signing transactions.

- [polycli tx-gas-chart](doc/polycli_tx-gas-chart.md) - plot a chart of transaction gas prices and limits

- [polycli ulxly](doc/polycli_ulxly.md) - Utilities for interacting with the uLxLy bridge.

- [polycli version](doc/polycli_version.md) - Get the current version of this application.
Expand Down
4 changes: 2 additions & 2 deletions cmd/loadtest/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -723,7 +723,7 @@ func (ap *AccountPool) returnFunds(ctx context.Context, lock bool) error {
if err != nil {
return err
}
gasPrice, _ := getSuggestedGasPrices(ctx, ap.client)
gasPrice, _ := getSuggestedGasPrices(ctx, ap.client, nil)
txFee := new(big.Int).Mul(ethTransferGas, gasPrice)
// triple the txFee to account for gas price fluctuations and
// different ways to charge transactions, like op networks
Expand Down Expand Up @@ -1117,7 +1117,7 @@ func (ap *AccountPool) createEOATransferTx(ctx context.Context, sender *ecdsa.Pr
return nil, err
}
tops.GasLimit = uint64(21000)
tops = configureTransactOpts(ctx, ap.client, tops)
tops = configureTransactOpts(ctx, ap.client, tops, nil)

var nonce uint64

Expand Down
23 changes: 23 additions & 0 deletions cmd/loadtest/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,17 @@ type (
OutputRawTxOnly bool
CheckBalanceBeforeFunding bool

// gas manager
GasManagerOscillationWave string
GasManagerTarget uint64
GasManagerPeriod uint64
GasManagerAmplitude uint64

GasManagerPriceStrategy string
GasManagerFixedGasPriceWei uint64
GasManagerDynamicGasPricesWei string
GasManagerDynamicGasPricesVariation float64

// Computed
CurrentGasPrice *big.Int
CurrentGasTipCap *big.Int
Expand Down Expand Up @@ -309,6 +320,18 @@ v3, uniswapv3 - perform UniswapV3 swaps`)
f.UintVar(&ltp.ReceiptRetryInitialDelayMs, "receipt-retry-initial-delay-ms", 100, "initial delay in milliseconds for receipt polling (uses exponential backoff with jitter)")
f.BoolVar(&ltp.CheckBalanceBeforeFunding, "check-balance-before-funding", false, "check account balance before funding sending accounts (saves gas when accounts are already funded)")

// gas manager flags - gas limit
f.Uint64Var(&ltp.GasManagerTarget, "gas-manager-target", 30_000_000, "target gas limit for the gas manager oscillation wave")
f.Uint64Var(&ltp.GasManagerPeriod, "gas-manager-period", 1, "period in blocks for the gas manager oscillation wave")
f.Uint64Var(&ltp.GasManagerAmplitude, "gas-manager-amplitude", 0, "amplitude for the gas manager oscillation wave")
f.StringVar(&ltp.GasManagerOscillationWave, "gas-manager-oscillation-wave", "flat", "type of oscillation wave for the gas manager (flat | sine | square | triangle | sawtooth)")

// gas manager flags - gas price
f.StringVar(&ltp.GasManagerPriceStrategy, "gas-manager-price-strategy", "estimated", "gas price strategy for the gas manager (estimated | fixed | dynamic)")
f.Uint64Var(&ltp.GasManagerFixedGasPriceWei, "gas-manager-fixed-gas-price-wei", 300000000, "fixed gas price in wei for the gas manager fixed strategy")
f.StringVar(&ltp.GasManagerDynamicGasPricesWei, "gas-manager-dynamic-gas-prices-wei", "0,1000000,0,10000000,0,100000000", "comma-separated list of gas prices in wei for the gas manager dynamic strategy, 0 means the tx will use the suggested gas price from the network.")
f.Float64Var(&ltp.GasManagerDynamicGasPricesVariation, "gas-manager-dynamic-gas-prices-variation", 0.3, "variation percentage (e.g., 0.3 for ±30%) to apply to each gas price in the dynamic strategy")

// TODO Compression
}

Expand Down
Loading