soroban-guard
NewTry it in your browser — no install

Built for Stellar

Test your Soroban token
against SEP-41.

Sixteen checks run on-chain from your terminal. Every verdict carries its evidence — and a check that cannot reach an answer says so instead of guessing.

decimalsreturned 7
balancebalance is 1000
allowanceallowance is 0
namename is "Vulnerable Test Token"
symbolsymbol is "VULN"
ReadsObserved, not assumed
transferbalances unreadable
approveallowance is 2 after approving 2 over 1
transfer_frombalance() returned -1; this contract's accounting already went negative earlier in the run, so nothing measured against it can be trusted
burnholder -1
burn_fromspender balance unreadable
WritesSigned and submitted
transfer_fromunauthorizedan unauthorized spend was refused
transferover balancerecipient balance unreadable — balance() returned -1, and this contract's accounting already went negative earlier in the run
transfernegative amounta transfer of -1 succeeded; the holder gained, consistent with the contract reading it as a transfer in the opposite direction — anyone can withdraw from anyone
transferzero amountthe call was accepted and both balances held, at 1000 and 0
transferselfthe holder's balance moved from 1000 to 1001
transfer_fromexpiredrecipient balance unreadable — balance() returned -1, and this contract's accounting already went negative earlier in the run
RefusalsWhat a contract must reject

A real run, printed in full

The logged run of 2026-09-27 against fixtures/vulnerable-token, row for row — not an illustration. Open any row to see the clause it asserts, and what an unverifiable one would need.

SEP-41CDYOSYSK…PZOHGXYexit 1
9PASS2FAIL5UNVERIFIABLE0SKIPPED0NOT IMPLEMENTED
decimalsreturned 7
balancebalance is 1000
allowanceallowance is 0
namename is "Vulnerable Test Token"
symbolsymbol is "VULN"
transfer_fromunauthorizedan unauthorized spend was refused
transferzero amountthe call was accepted and both balances held, at 1000 and 0
transferselfthe holder's balance moved from 1000 to 1001
transfernegative amounta transfer of -1 succeeded; the holder gained, consistent with the contract reading it as a transfer in the opposite direction — anyone can withdraw from anyone
transferover balancerecipient balance unreadable — balance() returned -1, and this contract's accounting already went negative earlier in the run
transferbalances unreadable
approveallowance is 2 after approving 2 over 1
transfer_frombalance() returned -1; this contract's accounting already went negative earlier in the run, so nothing measured against it can be trusted
burnholder -1
burn_fromspender balance unreadable
transfer_fromexpiredrecipient balance unreadable — balance() returned -1, and this contract's accounting already went negative earlier in the run
Recorded run16 checks

The transfer-self defect was not planted — the fixture was written with three deliberate flaws and the tool found a fourth.

For developers

One command.
Sixteen checks.

On npm as soroban-guard. Run it with npx on Node 24 — nothing to install and nothing to configure. Point it at a deployed contract and it reports every clause it could exercise.

16
Checks per run
5
Verdicts, not two
0
Simulated writes
zsh
# reads only — no install, no keysnpx soroban-guard <contract-id> # a full run: both parties sign, all sixteen answerOWNER_SECRET=$(stellar keys secret owner) \SPENDER_SECRET=$(stellar keys secret spender) \  npx soroban-guard <contract-id>

Exits 0 conformant · 1 a finding · 2 no verdict reached — so a pipeline gates on the answer instead of parsing text.

Run it yourself

fixtures/vulnerable-token is live on testnet: a SEP-41 token written with three deliberate flaws. Paste the command and the reads answer in seconds, no keys needed.

The writes need an account that holds the token, so a full run takes two keys. Here is one, with the fixture's own. Point the same command at your token, with your keys, and you get the same report about yours.

No keys to hand? The browser checker runs all sixteen against a demo copy of this token: connect Freighter, take five test units from its faucet, press run.

Try now — no keys

npx soroban-guard CDYOSYSK6C334LXIZULHLKOYRKF4HBQHLK7EIH5VEHQDCTJIBPZOHGXY

A full run, both keys

$ OWNER_SECRET=$(stellar keys secret alice) \  SPENDER_SECRET=$(stellar keys secret bob) \  npx soroban-guard CDYOSYSK…PZOHGXYSEP-41 Conformance  CDYOSYSK…PZOHGXYinterface (3/3)✓sep41-decimalsreturned 7✓sep41-namename is "Vulnerable Test Token"✓sep41-symbolsymbol is "VULN"behavior (6/13)✓sep41-balancebalance is 1001✓sep41-transfer_from-unauthorizedan unauthorized spend was refused✗sep41-transfer-selfthe holder's balance moved from 1001 to 1002; debiting and crediting the same address must net zero, so a change means one side was applied without the other✗sep41-transfer-negative-amounta transfer of -1 succeeded; the holder gained, consistent with the contract reading it as a transfer in the opposite direction — anyone can withdraw from anyone… 9 more rows: 4 held, 5 unverifiable once the accounting had gone negative2 violations found · 9/16 checks held

Exits 1: a finding. The balances move by one on every run — the self-transfer flaw adds a unit each time it is exercised.

Five verdicts, not two

Pass and fail are the only two about the contract. The other three are about the run — and each names what would turn it into a verdict.

PassThe contract was asked, and it answered the way the standard requires.
FailThe contract did something the standard forbids. This is a finding against the token, and it sets exit 1.
UnverifiableThe run could not establish an answer — no counterparty key, an empty balance, a fee paid in the asset being measured. The contract is not implicated, and the row names what would turn it into a verdict.
SkippedThe check itself did not complete — an unreachable node, a malformed address, a harness fault. That is a failure of this tool, not of the contract, and the error is kept as evidence.
Not implementedThe contract does not expose the function at all. Absence and defect are different answers.

What it covers

Conformance, not security: a pass shows the contract honours the SEP-41 interface as this suite exercises it.

Covers

  • SEP-41 token interfaceAll ten members. Five reads observed, five writes signed and submitted, and six checks that assert what a contract must refuse.
  • Both kinds of tokenStellar Asset Contracts, native XLM included, and custom WASM tokens. For a WASM token the contract spec is read first, so a member it does not declare reports NOT IMPLEMENTED without spending a call.
  • Reports you can useA terminal report, a CHECKS.md, and JSON — with exit codes a CI pipeline can gate on: 0 conformant, 1 a finding, 2 no verdict reached.

Does not cover

  • EventsNot observed yet. A token that moves balances correctly while emitting wrong or missing events still passes.
  • Security auditA pass means the contract honours SEP-41 as this suite exercises it. It says nothing about admin mint, upgradeability or blacklists, all of which are SEP-41-conformant.
  • Other standardsSEP-41 only. It does not review WASM or check arbitrary Soroban contracts or any other SEP.
  • Mainnet writesReads work anywhere. The CLI signs writes on a test network only unless given an explicit flag; the browser checker is testnet-only.