Skip to content

Commit 3f4c197

Browse files
mootz12fnando
andauthored
feat: add fees command/config and rename fee to inclusion fee (#2321)
Rename the `--fee` argument to `--inclusion-fee`, and deprecate the fee argument. Introduces a new command `stellar fees` to replace `stellar feestats`, and add the ability to set inclusion fees in the CLI config. Adds `--resource-fee` argument to allow editing of resource fees. --------- Co-authored-by: Nando Vieira <me@fnando.com>
1 parent fc6745f commit 3f4c197

File tree

32 files changed

+1139
-595
lines changed

32 files changed

+1139
-595
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,5 @@ test_snapshots
1010
local.sh
1111
.stellar
1212
.zed
13+
node_modules/
14+
.DS_Store

FULL_HELP_DOCS.md

Lines changed: 312 additions & 459 deletions
Large diffs are not rendered by default.

cmd/crates/soroban-test/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,8 @@ impl TestEnv {
281281
sign_with_lab: false,
282282
sign_with_ledger: false,
283283
},
284+
fee: None,
285+
inclusion_fee: None,
284286
}
285287
}
286288

cmd/crates/soroban-test/tests/it/config.rs

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,47 @@ fn set_default_identity() {
251251
.success();
252252
}
253253

254+
#[test]
255+
fn warns_if_default_identity_will_be_ignored() {
256+
let sandbox = TestEnv::default();
257+
258+
sandbox
259+
.new_assert_cmd("keys")
260+
.env(
261+
"SOROBAN_SECRET_KEY",
262+
"SC4ZPYELVR7S7EE7KZDZN3ETFTNQHHLTUL34NUAAWZG5OK2RGJ4V2U3Z",
263+
)
264+
.arg("add")
265+
.arg("alice")
266+
.assert()
267+
.success();
268+
269+
sandbox
270+
.new_assert_cmd("keys")
271+
.env(
272+
"SOROBAN_SECRET_KEY",
273+
"SDIY6AQQ75WMD4W46EYB7O6UYMHOCGQHLAQGQTKHDX4J2DYQCHVCQYFD",
274+
)
275+
.arg("add")
276+
.arg("bob")
277+
.assert()
278+
.success();
279+
280+
sandbox
281+
.new_assert_cmd("keys")
282+
.env("STELLAR_ACCOUNT", "bob")
283+
.arg("use")
284+
.arg("alice")
285+
.assert()
286+
.stderr(predicate::str::contains(
287+
"Environment variable STELLAR_ACCOUNT is set, which will override this default source account.",
288+
))
289+
.success();
290+
291+
let config_contents = fs::read_to_string(sandbox.config_dir().join("config.toml")).unwrap();
292+
assert!(config_contents.contains("identity = \"alice\""));
293+
}
294+
254295
#[test]
255296
fn set_default_network() {
256297
let sandbox = TestEnv::default();
@@ -272,6 +313,78 @@ fn set_default_network() {
272313
.success();
273314
}
274315

316+
#[test]
317+
fn warns_if_default_network_will_be_ignored() {
318+
let sandbox = TestEnv::default();
319+
320+
sandbox
321+
.new_assert_cmd("network")
322+
.env("STELLAR_NETWORK", "custom_network")
323+
.arg("use")
324+
.arg("testnet")
325+
.assert()
326+
.stderr(predicate::str::contains(
327+
"Environment variable STELLAR_NETWORK is set, which will override this default network.",
328+
))
329+
.success();
330+
331+
let config_contents = fs::read_to_string(sandbox.config_dir().join("config.toml")).unwrap();
332+
assert!(config_contents.contains("network = \"testnet\""));
333+
}
334+
335+
#[test]
336+
fn set_default_inclusion_fee() {
337+
let sandbox = TestEnv::default();
338+
339+
sandbox
340+
.new_assert_cmd("fees")
341+
.arg("use")
342+
.args(["--amount", "150"])
343+
.assert()
344+
.stderr(predicate::str::contains(
345+
"The default inclusion fee is set to `150`",
346+
))
347+
.success();
348+
349+
let config_contents = fs::read_to_string(sandbox.config_dir().join("config.toml")).unwrap();
350+
assert!(config_contents.contains("inclusion_fee = 150"));
351+
}
352+
353+
#[test]
354+
fn warns_if_default_inclusion_fee_will_be_ignored() {
355+
let sandbox = TestEnv::default();
356+
357+
sandbox
358+
.new_assert_cmd("fees")
359+
.env("STELLAR_INCLUSION_FEE", "200")
360+
.arg("use")
361+
.args(["--amount", "150"])
362+
.assert()
363+
.stderr(predicate::str::contains(
364+
"Environment variable STELLAR_INCLUSION_FEE is set, which will override this default inclusion fee.",
365+
))
366+
.success();
367+
368+
let config_contents = fs::read_to_string(sandbox.config_dir().join("config.toml")).unwrap();
369+
assert!(config_contents.contains("inclusion_fee = 150"));
370+
}
371+
372+
#[test]
373+
fn cannot_set_default_inclusion_fee_below_100() {
374+
let sandbox = TestEnv::default();
375+
376+
sandbox
377+
.new_assert_cmd("fees")
378+
.arg("use")
379+
.args(["--amount", "99"])
380+
.assert()
381+
.stderr(predicate::str::contains(
382+
"Fee amount must be at least 100 stroops, but got 99",
383+
))
384+
.failure();
385+
assert!(fs::read_to_string(sandbox.config_dir().join("config.toml")).is_err());
386+
}
387+
275388
#[test]
276389
fn cannot_create_contract_with_test_name() {
277390
let sandbox = TestEnv::default();

cmd/crates/soroban-test/tests/it/integration.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ mod contract;
44
mod cookbook;
55
mod custom_types;
66
mod dotenv;
7+
mod fee_args;
78
mod fee_stats;
89
mod hello_world;
910
mod init;
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
use predicates::prelude::predicate;
2+
use soroban_cli::xdr::{self, Limits, ReadXdr};
3+
use soroban_test::{AssertExt, TestEnv};
4+
5+
use super::util::deploy_hello;
6+
7+
fn get_inclusion_fee_from_xdr(tx_xdr: &str) -> u32 {
8+
let tx = xdr::TransactionEnvelope::from_xdr_base64(tx_xdr, Limits::none()).unwrap();
9+
match tx {
10+
xdr::TransactionEnvelope::TxV0(te) => te.tx.fee,
11+
xdr::TransactionEnvelope::Tx(te) => te.tx.fee,
12+
xdr::TransactionEnvelope::TxFeeBump(te) => te.tx.fee.try_into().unwrap(),
13+
}
14+
}
15+
16+
#[tokio::test]
17+
async fn inclusion_fee_arg() {
18+
let sandbox = &TestEnv::new();
19+
let id = deploy_hello(sandbox).await;
20+
21+
// Defaults to 100
22+
let tx_xdr = sandbox
23+
.new_assert_cmd("contract")
24+
.arg("invoke")
25+
.args(["--id", &id.to_string()])
26+
.arg("--build-only")
27+
.arg("--")
28+
.arg("inc")
29+
.assert()
30+
.success()
31+
.stdout_as_str();
32+
assert_eq!(get_inclusion_fee_from_xdr(&tx_xdr), 100u32);
33+
34+
// Update manually to 200
35+
sandbox
36+
.new_assert_cmd("fees")
37+
.arg("use")
38+
.args(["--amount", "200"])
39+
.assert()
40+
.stderr(predicate::str::contains(
41+
"The default inclusion fee is set to `200`",
42+
))
43+
.success();
44+
45+
let tx_xdr = sandbox
46+
.new_assert_cmd("contract")
47+
.arg("invoke")
48+
.args(["--id", &id.to_string()])
49+
.arg("--build-only")
50+
.arg("--")
51+
.arg("inc")
52+
.assert()
53+
.success()
54+
.stdout_as_str();
55+
assert_eq!(get_inclusion_fee_from_xdr(&tx_xdr), 200u32);
56+
57+
// Arg overrides config
58+
let tx_xdr = sandbox
59+
.new_assert_cmd("contract")
60+
.arg("invoke")
61+
.args(["--id", &id.to_string()])
62+
.args(["--inclusion-fee", "300"])
63+
.arg("--build-only")
64+
.arg("--")
65+
.arg("inc")
66+
.assert()
67+
.success()
68+
.stdout_as_str();
69+
assert_eq!(get_inclusion_fee_from_xdr(&tx_xdr), 300u32);
70+
71+
// Update from fee stats (going to be 100 since sandbox)
72+
sandbox
73+
.new_assert_cmd("fees")
74+
.arg("use")
75+
.args(["--fee-metric", "p50"])
76+
.assert()
77+
.stderr(predicate::str::contains(
78+
"The default inclusion fee is set to `100`",
79+
))
80+
.success();
81+
82+
// Deprecated fee arg ignored if inclusion-fee config exists
83+
let tx_xdr = sandbox
84+
.new_assert_cmd("contract")
85+
.arg("invoke")
86+
.args(["--id", &id.to_string()])
87+
.args(["--fee", "300"])
88+
.arg("--build-only")
89+
.arg("--")
90+
.arg("inc")
91+
.assert()
92+
.success()
93+
.stdout_as_str();
94+
assert_eq!(get_inclusion_fee_from_xdr(&tx_xdr), 100u32);
95+
96+
// Update manually to 200
97+
sandbox
98+
.new_assert_cmd("fees")
99+
.arg("use")
100+
.args(["--amount", "200"])
101+
.assert()
102+
.stderr(predicate::str::contains(
103+
"The default inclusion fee is set to `200`",
104+
))
105+
.success();
106+
107+
let tx_xdr = sandbox
108+
.new_assert_cmd("contract")
109+
.arg("invoke")
110+
.args(["--id", &id.to_string()])
111+
.arg("--build-only")
112+
.arg("--")
113+
.arg("inc")
114+
.assert()
115+
.success()
116+
.stdout_as_str();
117+
assert_eq!(get_inclusion_fee_from_xdr(&tx_xdr), 200u32);
118+
119+
// Verify unset clears the config
120+
sandbox
121+
.new_assert_cmd("fees")
122+
.arg("unset")
123+
.assert()
124+
.stderr(predicate::str::contains(
125+
"The default inclusion fee has been cleared",
126+
))
127+
.success();
128+
129+
let tx_xdr = sandbox
130+
.new_assert_cmd("contract")
131+
.arg("invoke")
132+
.args(["--id", &id.to_string()])
133+
.arg("--build-only")
134+
.arg("--")
135+
.arg("inc")
136+
.assert()
137+
.success()
138+
.stdout_as_str();
139+
assert_eq!(get_inclusion_fee_from_xdr(&tx_xdr), 100u32);
140+
}

cmd/crates/soroban-test/tests/it/integration/fee_stats.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ use soroban_test::{AssertExt, TestEnv};
55
async fn fee_stats_text_output() {
66
let sandbox = &TestEnv::new();
77
sandbox
8-
.new_assert_cmd("fee-stats")
8+
.new_assert_cmd("fees")
9+
.arg("stats")
910
.arg("--output")
1011
.arg("text")
1112
.assert()
@@ -19,7 +20,8 @@ async fn fee_stats_text_output() {
1920
async fn fee_stats_json_output() {
2021
let sandbox = &TestEnv::new();
2122
let output = sandbox
22-
.new_assert_cmd("fee-stats")
23+
.new_assert_cmd("fees")
24+
.arg("stats")
2325
.arg("--output")
2426
.arg("json")
2527
.assert()

cmd/crates/soroban-test/tests/it/integration/tx/general.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ async fn simulate() {
3030
.assert()
3131
.success()
3232
.stdout_as_str();
33-
let assembled = simulate_and_assemble_transaction(&sandbox.client(), &tx, None)
33+
let assembled = simulate_and_assemble_transaction(&sandbox.client(), &tx, None, None)
3434
.await
3535
.unwrap();
3636
let txn_env: TransactionEnvelope = assembled.transaction().clone().into();

0 commit comments

Comments
 (0)