Skip to content

Commit 4b3362c

Browse files
committed
Reject native asset as a clawback target.
1 parent 387ad55 commit 4b3362c

3 files changed

Lines changed: 32 additions & 3 deletions

File tree

‎FULL_HELP_DOCS.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2209,7 +2209,7 @@ Claw back tokens from an account or contract (SAC admin)
22092209

22102210
###### **Options:**
22112211

2212-
- `--id <ID>` — The token to claw back: a contract id or alias, `native`, or a classic asset as `CODE:ISSUER`
2212+
- `--id <ID>` — The token to claw back: a contract id or alias, or a classic asset as `CODE:ISSUER`. The native asset has no administrator and cannot be clawed back
22132213
- `--admin <ADMIN>` — The token's administrator. Signs and authorizes the clawback, so it must be an identity or secret key you control (the asset issuer for a Stellar Asset Contract)
22142214
- `--from <FROM>` — Account or contract to claw the tokens back from. Accepts a `G…`/`M…` account, a `C…` contract address, or an alias
22152215
- `--amount <AMOUNT>` — Amount to claw back, in the token's smallest unit (stroops for a Stellar Asset Contract)

‎cmd/crates/soroban-test/tests/it/integration/token/clawback.rs‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,30 @@ async fn clawback_removes_balance_and_returns_receipt() {
6262
);
6363
}
6464

65+
#[tokio::test]
66+
async fn clawback_rejects_native_target() {
67+
let sandbox = &TestEnv::new();
68+
let test = test_address(sandbox);
69+
70+
// The native asset's SAC is deployed without an administrator, so `clawback`
71+
// can never apply to it — reject it up front with a typed error instead of
72+
// reaching an opaque failing invocation.
73+
let stdout = sandbox
74+
.new_assert_cmd("token")
75+
.args([
76+
"clawback", "--id", "native", "--admin", "test", "--from", &test, "--amount", "1",
77+
"--output", "json",
78+
])
79+
.assert()
80+
.failure()
81+
.stdout_as_str();
82+
let value: Value = serde_json::from_str(&stdout).unwrap();
83+
assert_eq!(
84+
value["error"]["type"], "native_not_administered",
85+
"expected a typed error, got: {stdout}"
86+
);
87+
}
88+
6589
#[tokio::test]
6690
async fn clawback_fails_when_sac_not_deployed() {
6791
let sandbox = &TestEnv::new();

‎cmd/soroban-cli/src/commands/token/clawback.rs‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ use crate::{
1616
#[derive(Debug, Parser, Clone)]
1717
#[group(skip)]
1818
pub struct Cmd {
19-
/// The token to claw back: a contract id or alias, `native`, or a classic
20-
/// asset as `CODE:ISSUER`.
19+
/// The token to claw back: a contract id or alias, or a classic asset as
20+
/// `CODE:ISSUER`. The native asset has no administrator and cannot be clawed
21+
/// back.
2122
#[arg(long = "id")]
2223
pub id: UnresolvedToken,
2324

@@ -143,6 +144,10 @@ impl Cmd {
143144
let token = self
144145
.id
145146
.resolve(&config.locator, &network.network_passphrase)?;
147+
// The native asset's SAC has no administrator, so a clawback against it
148+
// can never succeed; reject it up front instead of failing deep in the
149+
// call.
150+
args::reject_native_admin(&token, "clawback")?;
146151

147152
// The admin only authorizes the clawback; it is not itself a `clawback`
148153
// argument. The invoke pipeline can't source a transaction from a muxed

0 commit comments

Comments
 (0)