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.
@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.
@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] += amountValidate 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:
items = data["items"]
items.append(value)
data["items"] = itemsPersistent State
Ordinary Python globals are recreated for each execution. Persistent data must use Variable or Hash.
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.