Skip to content

Commit a4d6355

Browse files
feat(fortuna): Add max_fee config option and add AGENTS.md (#3291)
* Add max_fee configuration option and update documentation - Introduced an optional `max_fee` parameter in the Ethereum configuration to clamp dynamically calculated fees. - Updated the fee adjustment logic to respect the `max_fee` cap. - Enhanced README with contributing guidelines for repository workflows and review expectations. * bump ver
1 parent ccab9c7 commit a4d6355

File tree

9 files changed

+59
-6
lines changed

9 files changed

+59
-6
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

apps/fortuna/AGENTS.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Repository Guidelines
2+
3+
## Project Structure & Module Organization
4+
Runtime code sits in `src/`, with `api/`, `chain/`, `keeper/`, and `command/` owning HTTP routes, blockchain adapters, keeper loops, and CLI verbs exposed by `main.rs`. Shared types live in `lib.rs`, configuration templates in `config.sample.yaml`, migrations in `migrations/`, and build helpers (Dockerfile, flake.nix, check-sqlx.sh) at the root.
5+
6+
## Build, Test, and Development Commands
7+
- `cargo build --workspace`: compile the web service and helper binaries.
8+
- `cargo test [-- --nocapture]`: run unit/integration suites, optionally showing logs.
9+
- `RUST_LOG=INFO cargo run -- run`: start the local server using `config.yaml`.
10+
- `cargo run -- setup-provider`: register the randomness provider specified in the config.
11+
- `cargo sqlx migrate run` or `database reset`: apply or reset schema using the `.env` `DATABASE_URL`.
12+
- `./check-sqlx.sh`: ensure SQLx offline metadata is current before DB-affecting commits.
13+
- `cli start | cli test | cli fix`: Nix-shell shortcuts for watch, verify, and autofix loops.
14+
15+
## Coding Style & Naming Conventions
16+
Always run `cargo fmt --all` and `cargo clippy --all-targets --all-features -D warnings` so submissions match CI expectations (4-space indent, trailing commas, no lint debt). Modules and files use snake_case, public structs/enums use PascalCase, and constants remain SCREAMING_SNAKE_CASE. Align YAML/TOML samples with `config.sample.yaml`, and reference secrets by env var rather than literals.
17+
18+
## Testing Guidelines
19+
Keep module-level tests inside the relevant file (`#[cfg(test)]` blocks) and add `tests/` integration suites for CLI flows or keeper orchestration. Run `cargo sqlx migrate run` after editing migrations so `cargo test` interacts with the right schema. Prioritize coverage on chain adapters, keeper scheduling, replica assignment, and API pagination, mocking RPC traits to avoid network flakiness.
20+
21+
## Commit & Pull Request Guidelines
22+
Git history uses `type(scope): summary` (`fix(lazer)`, `chore(contract-manager): ...`), so keep subjects imperative and ≤72 characters, and isolate unrelated changes. Include regenerated SQLx data or config snapshots in the same commit that needs them. PRs should describe motivation, list the verification commands executed (`cargo test`, `./check-sqlx.sh`, etc.), and attach logs or screenshots whenever behavior changes.
23+
**Important**: When changing code, bump the package version in `Cargo.toml` based on semantic versioning. Run `cargo check` to ensure the changes are reflected in the Cargo.lock lockfile.
24+
25+
## Security & Configuration Tips
26+
Do not commit `config.yaml`, `.env`, or private keys—derive them from `config.sample.yaml` and inject secrets through env vars or secret stores. Each replica must run with unique `keeper.private_key` values, and only the fee-managing instance should hold `keeper.fee_manager_private_key`. Remove sensitive details from logs before sharing and rotate credentials immediately if a leak is suspected.

apps/fortuna/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "fortuna"
3-
version = "9.2.2"
3+
version = "9.3.0"
44
edition = "2021"
55

66
[lib]

apps/fortuna/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,3 +144,7 @@ automatically. If you use this configuration you will have a `cli` script in
144144
your dev shell which provides easy access to some common tasks, such as `cli
145145
start` to start the server in watch mode, `cli test` to run unit, code format,
146146
and lint checks, and `cli fix` to run auto-fixes for formatting and lint issues.
147+
148+
## Contributing
149+
150+
See [AGENTS.md](./AGENTS.md) for repository guidelines covering project layout, workflows, and review expectations before opening a pull request.

apps/fortuna/config.sample.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ chains:
3232
# How much to charge in fees
3333
fee: 1500000000000000
3434

35+
# Optional hard cap on the fee (in wei). If the dynamically calculated fee exceeds this, it will be clamped.
36+
# max_fee: 10000000000000000
37+
3538
# Set this temporarily to false if you have changed the fees and want to apply a new baseline fee.
3639
sync_fee_only_on_register: true
3740

apps/fortuna/src/api.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,7 @@ mod test {
609609
max_profit_pct: 100,
610610
min_keeper_balance: 100000000000000000,
611611
fee: 1500000000000000,
612+
max_fee: None,
612613
sync_fee_only_on_register: true,
613614
commitments: None,
614615
max_num_hashes: None,
@@ -632,6 +633,7 @@ mod test {
632633
max_profit_pct: 100,
633634
min_keeper_balance: 100000000000000000,
634635
fee: 2000000000000000,
636+
max_fee: None,
635637
sync_fee_only_on_register: true,
636638
commitments: None,
637639
max_num_hashes: None,

apps/fortuna/src/config.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,11 @@ pub struct EthereumConfig {
190190
#[serde(default)]
191191
pub fee: u128,
192192

193+
/// Optional hard cap on the fee (in wei). If the dynamically calculated fee exceeds this,
194+
/// it will be clamped to this value. When None, no cap is applied.
195+
#[serde(default)]
196+
pub max_fee: Option<u128>,
197+
193198
/// Only set the provider's fee when the provider is registered for the first time. Default is true.
194199
/// This is useful to avoid resetting the fees on service restarts.
195200
#[serde(default = "default_sync_fee_only_on_register")]

apps/fortuna/src/keeper.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ pub async fn run_keeper_threads(
169169
u64::try_from(100 + chain_eth_config.max_profit_pct)
170170
.expect("max_profit_pct must be >= -100"),
171171
chain_eth_config.fee,
172+
chain_eth_config.max_fee,
172173
metrics.clone(),
173174
)
174175
.in_current_span(),

apps/fortuna/src/keeper/fee.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ pub async fn adjust_fee_wrapper(
230230
target_profit_pct: u64,
231231
max_profit_pct: u64,
232232
min_fee_wei: u128,
233+
max_fee_wei: Option<u128>,
233234
metrics: Arc<KeeperMetrics>,
234235
) {
235236
// The maximum balance of accrued fees + provider wallet balance. None if we haven't observed a value yet.
@@ -246,6 +247,7 @@ pub async fn adjust_fee_wrapper(
246247
target_profit_pct,
247248
max_profit_pct,
248249
min_fee_wei,
250+
max_fee_wei,
249251
&mut high_water_pnl,
250252
&mut sequence_number_of_last_fee_update,
251253
metrics.clone(),
@@ -261,7 +263,8 @@ pub async fn adjust_fee_wrapper(
261263

262264
/// Adjust the fee charged by the provider to ensure that it is profitable at the prevailing gas price.
263265
/// This method targets a fee as a function of the maximum cost of the callback,
264-
/// c = (gas_limit) * (current gas price), with min_fee_wei as a lower bound on the fee.
266+
/// c = (gas_limit) * (current gas price), with min_fee_wei as a lower bound and max_fee_wei as an
267+
/// optional upper bound on the fee.
265268
///
266269
/// The method then updates the on-chain fee if all of the following are satisfied:
267270
/// - the on-chain fee does not fall into an interval [c*min_profit, c*max_profit]. The tolerance
@@ -282,6 +285,7 @@ pub async fn adjust_fee_if_necessary(
282285
target_profit_pct: u64,
283286
max_profit_pct: u64,
284287
min_fee_wei: u128,
288+
max_fee_wei: Option<u128>,
285289
high_water_pnl: &mut Option<U256>,
286290
sequence_number_of_last_fee_update: &mut Option<u64>,
287291
metrics: Arc<KeeperMetrics>,
@@ -313,11 +317,11 @@ pub async fn adjust_fee_if_necessary(
313317
.get_or_create(&account_label)
314318
.set((max_callback_cost / gas_limit) as f64 / 1e9);
315319

316-
let target_fee_min = std::cmp::max(
320+
let mut target_fee_min = std::cmp::max(
317321
(max_callback_cost * u128::from(min_profit_pct)) / 100,
318322
min_fee_wei,
319323
);
320-
let target_fee = std::cmp::max(
324+
let mut target_fee = std::cmp::max(
321325
(max_callback_cost * u128::from(target_profit_pct)) / 100,
322326
min_fee_wei,
323327
);
@@ -326,11 +330,19 @@ pub async fn adjust_fee_if_necessary(
326330
.get_or_create(&account_label)
327331
.set(((max_callback_cost * u128::from(target_profit_pct)) / 100) as f64 / 1e18);
328332

329-
let target_fee_max = std::cmp::max(
333+
let mut target_fee_max = std::cmp::max(
330334
(max_callback_cost * u128::from(max_profit_pct)) / 100,
331335
min_fee_wei,
332336
);
333337

338+
// Apply max fee cap if configured, but ensure it doesn't go below min_fee_wei
339+
if let Some(max_fee) = max_fee_wei {
340+
let effective_max = std::cmp::max(max_fee, min_fee_wei);
341+
target_fee_min = std::cmp::min(target_fee_min, effective_max);
342+
target_fee = std::cmp::min(target_fee, effective_max);
343+
target_fee_max = std::cmp::min(target_fee_max, effective_max);
344+
}
345+
334346
// Calculate current P&L to determine if we can reduce fees.
335347
let current_keeper_balance = contract
336348
.provider()

0 commit comments

Comments
 (0)