Skip to content
Security
Skill

/sep41-token-safety

Trigger Pattern SEP-41 token patterns detected (approve/transfer/transfer_from/allowance/balance) - Inject Into Breadth agents, depth-token-flow, depth-edge-case

From plugin
plamen
276160 skills12 agents4 commands
Install
$ npx -y skills add PlamenTSV/plamen --skill sep41-token-safety --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/sep41-token-safety

Context preview

The summary Claude sees to decide when to auto-load this skill.

Trigger Pattern SEP-41 token patterns detected (approve/transfer/transfer_from/allowance/balance) - Inject Into Breadth agents, depth-token-flow, depth-edge-case

SKILL.md

sep41-token-safety.SKILL.md
name: "sep41-token-safety"
description: "Trigger Pattern SEP-41 token patterns detected (approve/transfer/transfer_from/allowance/balance) - Inject Into Breadth agents, depth-token-flow, depth-edge-case"

SEP41_TOKEN_SAFETY Skill (Soroban)

> **Trigger Pattern**: SEP-41 token patterns detected (`approve`/`transfer`/`transfer_from`/`allowance`/`balance`) > **Inject Into**: Breadth agents, depth-token-flow, depth-edge-case > **Finding prefix**: `[ST-N]` > **Rules referenced**: R4, R5, R10, R11, R13, R15

SEP-41 is Soroban's token interface standard, analogous to ERC-20. It introduces Soroban-specific behaviors that differ from ERC-20: allowances use Temporary storage with `expiration_ledger`, the `approve` function can be front-run similarly to ERC-20, and the Stellar Asset Contract (SAC) bridges Stellar classic assets into Soroban with additional accounting complexity.

1. Approve Race Condition

The `approve(from, spender, amount, expiration_ledger)` function overwrites the current allowance without checking the existing value. This is the ERC-20 approve race condition, present identically in SEP-41:

| Token Contract | `approve` Guarded? | Guard Type | Effective? | |---------------|-------------------|-----------|-----------| | `{contract}` | YES/NO | `require!(current == 0)` / none | YES/NO |

**Attack sequence**: 1. Owner approves spender for 100 tokens 2. Owner submits new approval for 50 tokens 3. Spender front-runs the new approval and spends the 100-token allowance 4. New 50-token approval is set 5. Spender spends 50 more tokens — net: 150 tokens spent, 50 intended

**Soroban-specific note**: Soroban does not have a traditional mempool — transaction ordering is determined by validators, not fee-based priority. However, multi-operation transactions and DEX routing can create ordering dependencies. The race condition is lower likelihood than on EVM but the vulnerability exists.

**Check for**:

  • Does `approve` allow setting non-zero over non-zero without an intermediate zero-set step?
  • Does the protocol documentation warn users about the race condition?
  • Are there any off-chain tools (like `increaseAllowance` / `decreaseAllowance` equivalents) to safely adjust allowances?

2. Allowance Expiry

SEP-41 allowances include an `expiration_ledger` parameter stored alongside the allowance amount. When the ledger passes `expiration_ledger`, the allowance is treated as zero. Verify expiry is handled correctly:

| Contract | Allowance Expiry Checked Before Use? | Expired Allowance Returns 0 or Panics? | Protocol Communicates Expiry to Users? | |---------|--------------------------------------|---------------------------------------|---------------------------------------| | `{contract}` | YES/NO | `{behavior}` | YES/NO |

**Allowance storage**: SEP-41 standard stores allowances in Temporary storage with TTL linked to `expiration_ledger`. When `expiration_ledger` passes, the Temporary entry may be pruned, and the `allowance()` call returns zero automatically.

**Checks**:

  • Does the calling contract handle the case where a previously valid allowance has expired and `transfer_from` now fails?
  • Is `expiration_ledger` validated to be in the future when `approve` is called? Setting `expiration_ledger` in the past silently creates an already-expired allowance
  • For protocol-controlled allowances (e.g., a contract that approves tokens on behalf of users): does the contract re-approve if the allowance has expired?

**Edge case**: `expiration_ledger = 0` behavior — verify whether the token contract treats 0 as "no expiry" or "expired at ledger 0" (already expired). Inconsistency here could cause silent allowance failures.

3. Transfer Auth Propagation

SEP-41 `transfer(from, to, amount)` requires `from.require_auth()`. `transfer_from(spender, from, to, amount)` requires `spender.require_auth()` and checks the allowance. Trace auth through the contract's transfer chains:

| Calling Function | Transfer Type | Auth Address | `require_auth` Called? | Correct Subject? | |-----------------|--------------|-------------|----------------------|-----------------| | `{fn}` | `transfer` / `transfer_from` | `{from or spender}` | YES/NO | YES/NO |

**Patterns to check**:

  • A contract calling `token.transfer(user, contract, amount)` on behalf of a user — does it call `user.require_auth()` first, or does it rely on the token contract to enforce it?
  • Sub-invocation auth propagation: if a function calls `token.transfer` inside a `invoke_contract` chain, is the `AuthorizedInvocation` tree constructed to include the token transfer?
  • `transfer_from` where the spender is the calling contract itself (valid for vault patterns) — is the allowance actually set, or does the contract assume it already is?

4. SAC Interaction

The Stellar Asset Contract (SAC) wraps Stellar classic XLM and issued assets as SEP-41 tokens. Contracts that interact with SAC face unique considerations:

| Concern | Addressed? | Evidence | |---------|-----------|----------| | SAC balance includes trust line state (frozen/unauthorized) | YES/NO | `{fn:line or NONE}` | | Classic Stellar operations affecting SAC balance not reflected in Soroban state | YES/NO | `{fn:line or NONE}` | | Clawback feature of issued assets handled | YES/NO | `{fn:line or NONE}` | | Issuer authorization revocation handled | YES/NO | `{fn:line or NONE}` | | Token address permanence (classic re-issuance / migration) | YES/NO | `{fn:line or NONE}` |

**SAC-specific risks**:

  • **Trust line authorization**: A classic Stellar account can have its trust line for an asset revoked by the issuer. After revocation, `transfer` to that account fails silently or panics. Contracts that assume all transfers succeed need to handle this.
  • **Clawback**: Some Stellar assets have clawback enabled. An issuer can call `clawback` to reduce a Soroban contract's balance without any Soroban transaction. This means a contract's `balance()` can decrease between two Soroban transa
Read more
Ships withplamen

Autonomous Web3 security auditor for Claude Code and OpenAI Codex CLI. Orchestrates 18-100 AI agents across 40+ phases to produce audit reports with verified PoC exploits — for smart contracts and L1 node-client infrastructure.

Get the whole plugin