XSC-0001: Fungible Token
XSC-0001 is the core fungible-token interface used across the Xian stack.
Required Surface
The standard token surface is:
@export
def change_metadata(key: str, value: Any):
...
@export
def transfer(amount: float, to: str):
...
@export
def approve(amount: float, to: str):
...
@export
def transfer_from(amount: float, to: str, main_account: str):
...
@export
def balance_of(address: str) -> float:
...Expected Semantics
transfermoves balance fromctx.callertotoapprovesets an allowance fortotransfer_fromspends frommain_accountusing the caller's allowancebalance_ofreturns the current balance for an address
The current canonical implementation stores allowances in a dedicated approvals hash using a two-dimensional key:
approvals[owner, spender] = amountExpected Events
The canonical token contract emits:
TransferApprove
Typical indexed fields are sender and recipient/spender so that CometBFT and dashboard subscribers can query them efficiently.
Recommended State Layout
balances = Hash(default_value=0)
approvals = Hash(default_value=0)
metadata = Hash()Common metadata keys in the canonical token are:
token_nametoken_symboltoken_logo_urltoken_logo_svgtoken_websitetotal_supplyoperatorpermit_authorizer
Wallets should prefer token_logo_url when it is present and non-empty. If the URL field is empty, they should fall back to token_logo_svg and render the on-chain SVG directly.
The canonical currency contract treats metadata in two classes:
- display metadata, such as name, symbol, logo, and website, can be changed by
metadata["operator"]throughchange_metadata - sensitive metadata, such as
total_supply,operator, andpermit_authorizer, is not writable throughchange_metadata
total_supply is managed by token accounting. The canonical currency contract exposes governance-only helpers for sensitive authority changes:
@export
def set_operator(new_operator: str):
...
@export
def set_permit_authorizer(new_authorizer: str):
...Tokens that support XSC-0002 Permit Authorizer should keep the permit-authorizer contract name in token state and guard approve_from_authorizer(...) against all other callers.
Compatibility Notes
- validate
amount > 0for transfers - approvals may be
0and should overwrite prior allowance - emit events on successful transfer/approval paths
- keep function names and argument names stable so tooling can introspect them