Skip to content

Common Pitfalls

Authorization Context

ctx.caller is the immediate caller. ctx.signer is the original transaction signer and remains unchanged across contract calls.

Use ctx.caller for direct authorization and allowance checks. Use ctx.caller == ctx.signer when a function must reject contract-mediated calls.

python
@export
def admin_action():
    assert ctx.caller == owner.get(), "Only owner"

Using ctx.signer here could let an intermediate contract act while preserving the owner's signer identity.

Amount and Balance Checks

Negative or unchecked amounts can reverse a transfer or create negative balances.

python
@export
def transfer(amount: float, to: str):
    assert to != "", "Recipient is required"
    assert amount > 0, "Amount must be positive"
    assert balances[ctx.caller] >= amount, "Insufficient balance"
    balances[ctx.caller] -= amount
    balances[to] += amount

Validate bounds, identifiers, deadlines, and collection sizes before changing state.

Mutable Stored Values

Values read from Hash or Variable are defensive copies. Write a modified list or dictionary back to storage:

python
items = data["items"]
items.append(value)
data["items"] = items

Persistent State

Ordinary Python globals are recreated for each execution. Persistent data must use Variable or Hash.

python
counter = Variable(default_value=0)

@export
def bump():
    counter.set(counter.get() + 1)

External Calls

Update and validate your own state before calling another contract. Xian rolls back the full transaction when a nested call fails, but successful callbacks can still observe incorrectly ordered state.

Apply checks-effects-interactions and guard any workflow that must not be re-entered.

Numeric Semantics

Contract float values are deterministic decimal-backed numbers, not binary Python floats. They support 61 whole digits and 30 fractional digits; extra fractional digits truncate toward zero and out-of-range values fail.

Use int for integral quantities and test rounding/boundary behavior for amounts and rates.

Randomness

Contract random is deterministic and derived from public execution context. It is suitable for reproducible sampling, not adversarially unpredictable lotteries. Use a reviewed commit-reveal or oracle design when manipulation or prediction matters.

Secrets

Contract source, state, events, transaction inputs, and public outputs are observable. Never store passwords, private keys, seed phrases, or unencrypted secrets on-chain.

Deployment Checklist

  • every privileged export authorizes the correct caller
  • direct-call requirements distinguish caller from signer
  • amounts, balances, identifiers, deadlines, and collection sizes are bounded
  • state is updated before external calls
  • mutable values are written back
  • no durable data relies on Python globals
  • numeric precision and truncation are tested
  • emitted events contain no secrets
  • failure tests assert complete rollback
  • deployment uses reviewed source, constructor arguments, owner, and developer metadata

See Audit Checklist for the short review form.