xian smart contracts cursor rules prompt file
via PatrickJS/awesome-cursorrules
Cursor rules for native Python smart contract development on the Xian blockchain.
What is xian smart contracts cursor rules prompt file?
Provides comprehensive guidance for writing, testing, and securing smart contracts in native Python on Xian. Use this rule when developing contracts that interact with the Xian blockchain, including state management, context variables, event handling, and security best practices.
- Define contract structure with proper naming conventions and decorator usage (@export, @construct)
- Manage contract state using Variable and Hash data structures with read-only foreign access
- Access context variables (ctx.caller, ctx.signer, ctx.this, ctx.owner) for permissions and contract identity
- Interact with other contracts via dynamic imports and interface enforcement
- Emit and test events using LogEvent with indexed parameters for querying
- Write secure tests using unittest framework with environment setup, signer specification, and vulnerability testing
Applies to
File patterns this rule matches.
Rule definition (reference)
Source of truth, from the repository.
Xian Smart Contract Development - Cursor Rules
XIAN is the currency of the Xian blockchain. Never mention TAU or Lamden.
Contract Structure
Basic Structure
- Smart contracts are written in native Python without transpilation
- Contract names must follow the pattern:
^con_[a-z][a-z0-9_]*$ - Contract names must start with 'con_' prefix (except system contracts like 'currency')
- Contract names must be lowercase, only contain letters, numbers and underscores after prefix
- Contract names must be max 64 characters
Naming Conventions
- You cannot use '_' as a prefix for variables or functions (e.g.,
_private_varis not allowed) - Follow standard Python naming conventions otherwise
- Use descriptive names for clarity
- A contract can not be deployed by another contract
Function Types
@exportdecorator defines public functions callable by any user or contract@constructdecorator defines initialization function executed once at contract submission (optional)- Functions without decorators are private and can only be called by the contract itself
- Functions with
@exportcan call private functions internally
Constructor Arguments
- Optional arguments can be provided to the
@constructfunction - Initial state can be setup using these arguments
State Management
Variable
Variableis a way to define a singular state variable in the contract- Use
variable.set(value)to modify - Use
variable.get()to retrieve
my_var = Variable()
@construct
def seed():
my_var.set(0) # Initialize variable
@export
def increment():
my_var.set(my_var.get() + 1)
Hash
Hashis a key-value store for the contract- Default value can be specified with
Hash(default_value=0) - Access through dictionary-like syntax:
hash[key] = valueandhash[key] - Supports nested keys with tuple:
hash[key1, key2] = value
my_hash = Hash(default_value=0)
@export
def set_value(key: str, value: int):
my_hash[key] = value
@export
def get_value(key: str):
return my_hash[key]
Illegal Delimiters
":" and "." cannot be used in Variable or Hash keys.
Foreign State Access
ForeignHashprovides read-only access to a Hash from another contractForeignVariableprovides read-only access to a Variable from another contract
token_balances = ForeignHash(foreign_contract='con_my_token', foreign_name='balances')
foundation_owner = ForeignVariable(foreign_contract='foundation', foreign_name='owner')
Context Variables
ctx.caller
- The identity of the person or contract calling the function
- Changes when a contract calls another contract's function
- Used for permission checks in token contracts
ctx.signer
- The top-level user who signed the transaction
- Remains constant throughout transaction execution
- Only used for security guards/blacklisting, not for account authorization
ctx.this
- The identity/name of the current contract
- Never changes
- Useful when the contract needs to refer to itself
ctx.owner
- Owner of the contract, optional field set at time of submission
- Only the owner can call exported functions if set
- Can be changed with
ctx.owner = new_owner
ctx.entry
- Returns tuple of (contract_name, function_name) of the original entry point
- Helps identify what contract and function initiated the call chain
Built-in Variables
Time and Blockchain Information
now- Returns the current datetimeblock_num- Returns the current block number, useful for block-dependent logicblock_hash- Returns the current block hash, can be used as a source of randomness
Example usage:
@construct
def seed():
submission_time = Variable()
submission_block_num = Variable()
submission_block_hash = Variable()
# Store blockchain state at contract creation
submission_time.set(now)
submission_block_num.set(block_num)
submission_block_hash.set(block_hash)
Imports and Contract Interaction
Importing Contracts
- Use
importlib.import_module(contract_name)for dynamic contract imports - Static contract imports can be done with
import <contract_name> - Only use 'import' syntax for contracts, not for libraries or Python modules
- Trying to import standard libraries will not work within a contract (they're automatically available)
- Dynamic imports are preferred when the contract name is determined at runtime
- Can enforce interface with
importlib.enforce_interface() - NEVER import anything other than a contract.
- ALL contracting libraries are available globally
- NEVER IMPORT importlib. It is already available globally.
@export
def interact_with_token(token_contract: str, recipient: str, amount: float):
token = importlib.import_module(token_contract)
# Define expected interface
interface = [
importlib.Func('transfer', args=('amount', 'to')),
importlib.Var('balances', Hash)
]
# Enforce interface
assert importlib.enforce_interface(token, interface)
# Call function on other contract
token.transfer(amount=amount, to=recipient)
Error Handling
Assertions
- Use
assertstatements for validation and error checking - Include error messages:
assert condition, "Error message"
No Try/Except
- Exception handling with try/except is not allowed
- Use conditional logic with if/else statements instead
# DO NOT USE:
try:
result = 100 / value
except:
result = 0
# CORRECT APPROACH:
assert value != 0, "Cannot divide by zero"
result = 100 / value
# OR
if value == 0:
result = 0
else:
result = 100 / value
Prohibited Built-ins
getattris an illegal built-in function and must not be used- Other Python built-ins may also be restricted for security reasons
Modules
Random
- Seed RNG with
random.seed() - Generate random integers with
random.randint(min, max)
Datetime
- Available by default without importing
- Compare timestamps with standard comparison operators
- Use the built-in
nowvariable for current time
Crypto
- Provides cryptographic functionality using the PyNaCl library under the hood
- Employs the Ed25519 signature scheme for digital signatures
- Main function is
verifyfor signature validation
# Verify a signature
is_valid = crypto.verify(vk, msg, signature)
# Returns True if the signature is valid for the given message and verification key
Example usage in a contract:
@export
def verify_signature(vk: str, msg: str, signature: str):
# Use the verify function to check if the signature is valid
is_valid = crypto.verify(vk, msg, signature)
# Return the result of the verification
return is_valid
Hashlib
- Xian provides a simplified version of hashlib with a different API than Python's standard library
- Does not require setting up an object and updating it with bytes
- Functions directly accept and return hexadecimal strings
# Hash a hex string with SHA3 (256 bit)
hash_result = hashlib.sha3("68656c6c6f20776f726c64") # hex for "hello world"
# If not a valid hex string, it will encode the string to bytes first
text_hash = hashlib.sha3("hello world")
# SHA256 works the same way (SHA2 256-bit, used in Bitcoin)
sha256_result = hashlib.sha256("68656c6c6f20776f726c64")
Testing
Setting Up Tests
- Use Python's unittest framework
- Client available via
from contracting.client import ContractingClient - Flush client before and after each test
Setting Test Environment
- Pass environment variables like
now(datetime) in a dictionary
from contracting.stdlib.bridge.time import Datetime
env = {"now": Datetime(year=2021, month=1, day=1, hour=0)}
result = self.some_contract.some_fn(some_arg=some_value, environment=env)
Specifying Signer
- Specify the signer when calling contract functions in tests
result = self.some_contract.some_fn(some_arg=some_value, signer="some_signer")
Events
Defining Events
- Use
LogEventto define events at the top level of a contract - Each event has a name and a schema of parameters with their types
- Set
idx: Truefor parameters that should be indexed for querying
TransferEvent = LogEvent(
event="Transfer",
params={
"from": {'type': str, 'idx': True},
"to": {'type': str, 'idx': True},
"amount": {'type': (int, float, decimal)}
}
)
ApprovalEvent = LogEvent(
event="Approval",
params={
"owner": {'type': str, 'idx': True},
"spender": {'type': str, 'idx': True},
"amount": {'type': (int, float, decimal)}
}
)
Emitting Events
- Call the event variable as a function and pass a dictionary of parameter values
- All parameters defined in the event schema must be provided
- Event parameters must match the specified types
@export
def transfer(amount: float, to: str):
sender = ctx.caller
# ... perform transfer logic ...
# Emit the transfer event
TransferEvent({
"from": sender,
"to": to,
"amount": amount
})
Testing Events
- Use
return_full_output=Truewhen calling contract functions in tests to capture events - Access events in the result dictionary's 'events' key
- Assert on event types and parameters in tests
# In your test function
result = self.contract.transfer(
amount=100,
to="recipient",
signer="sender",
return_full_output=True
)
# Verify events
events = result['events']
assert len(events) == 1
assert events[0]['event'] == 'Transfer'
assert events[0]['from'] == 'sender'
assert events[0]['to'] == 'recipient'
assert events[0]['amount'] == 100
Common Event Types
- Transfer: When value moves between accounts
- Approval: When spending permissions are granted
- Mint/Burn: When tokens are created or destroyed
- StateChange: When significant contract state changes
- ActionPerformed: When important contract actions execute
Smart Contract Testing Best Practices
Test Structure
- Use Python's unittest framework for structured testing
- Create a proper test class that inherits from
unittest.TestCase - Implement
setUpandtearDownmethods to isolate tests - Define the environment and chain ID in setUp for consistent testing
class TestMyContract(unittest.TestCase):
def setUp(self):
# Bootstrap the environment
self.chain_id = "test-chain"
self.environment = {"chain_id": self.chain_id}
self.deployer_vk = "test-deployer"
# Initialize the client
self.client = ContractingClient(environment=self.environment)
self.client.flush()
# Load and submit the contract
with open('path/to/my_contract.py') as f:
code = f.read()
self.client.submit(code, name="my_contract", constructor_args={"owner": self.deployer_vk})
# Get contract instance
self.contract = self.client.get_contract("my_contract")
def tearDown(self):
# Clean up after each test
self.client.flush()
Test Organization
- Group tests by functionality using descriptive method names
- Follow the Given-When-Then pattern for clear test cases
- Test both positive paths and error cases
- Define all variables within the test, not in setUp
- Define all variables and parameters used by a test WITHIN THE TEST, not within setUp
- This ensures test isolation and prevents unexpected side effects between tests
def test_transfer_success(self):
# GIVEN a sender with balance
sender = "alice"
self.contract.balances[sender] = 1000
# WHEN a transfer is executed
result = self.contract.transfer(amount=100, to="bob", signer=sender)
# THEN the balances should be updated correctly
self.assertEqual(self.contract.balances["bob"], 100)
self.assertEqual(self.contract.balances[sender], 900)
Testing for Security Vulnerabilities
1. Authorization and Access Control
- Test that only authorized users can perform restricted actions
- Verify that contract functions check
ctx.callerorctx.signerappropriately
def test_change_metadata_unauthorized(self):
# GIVEN a non-operator trying to change metadata
with self.assertRaises(Exception):
self.contract.change_metadata(key="name", value="NEW", signer="attacker")
2. Replay Attack Protection
- Test that transaction signatures cannot be reused
- Verify nonce mechanisms or one-time-use permits
def test_permit_double_spending(self):
# GIVEN a permit already used once
self.contract.permit(owner="alice", spender="bob", value=100, deadline=deadline,
signature=signature)
# WHEN the permit is used again
# THEN it should fail
with self.assertRaises(Exception):
self.contract.permit(owner="alice", spender="bob", value=100,
deadline=deadline, signature=signature)
3. Time-Based Vulnerabilities
- Test behavior around time boundaries (begin/end dates)
- Test with different timestamps using the environment parameter
def test_time_sensitive_function(self):
# Test with time before deadline
env = {"now": Datetime(year=2023, month=1, day=1)}
result = self.contract.some_function(signer="alice", environment=env)
self.assertTrue(result)
# Test with time after deadline
env = {"now": Datetime(year=2024, month=1, day=1)}
with self.assertRaises(Exception):
self.contract.some_function(signer="alice", environment=env)
4. Balance and State Checks
- Verify state changes after operations
- Test for correct balance updates after transfers
- Ensure state consistency through complex operations
def test_transfer_balances(self):
# Set initial balances
self.contract.balances["alice"] = 1000
self.contract.balances["bob"] = 500
# Perform transfer
self.contract.transfer(amount=300, to="bob", signer="alice")
# Verify final balances
self.assertEqual(self.contract.balances["alice"], 700)
self.assertEqual(self.contract.balances["bob"], 800)
5. Signature Validation
- Test with valid and invalid signatures
- Test with modified parameters to ensure signatures aren't transferable
def test_signature_validation(self):
# GIVEN a properly signed message
signature = wallet.sign_msg(msg)
# WHEN using the correct parameters
result = self.contract.verify_signature(msg=msg, signature=signature,
public_key=wallet.public_key)
# THEN verification should succeed
self.assertTrue(result)
# BUT when using modified parameters
with self.assertRaises(Exception):
self.contract.verify_signature(msg=msg+"tampered", signature=signature,
public_key=wallet.public_key)
6. Edge Cases and Boundary Conditions
- Test with zero values, max values, empty strings
- Test operations at time boundaries (exactly at deadline)
- Test with invalid inputs and malformed data
def test_edge_cases(self):
# Test with zero amount
with self.assertRaises(Exception):
self.contract.transfer(amount=0, to="receiver", signer="sender")
# Test with negative amount
with self.assertRaises(Exception):
self.contract.transfer(amount=-100, to="receiver", signer="sender")
7. Capturing and Verifying Events
- Use
return_full_output=Trueto capture events - Verify event emissions and their parameters
def test_event_emission(self):
# GIVEN a setup for transfer
sender = "alice"
receiver = "bob"
amount = 100
self.contract.balances[sender] = amount
# WHEN executing with return_full_output
result = self.contract.transfer(
amount=amount,
to=receiver,
signer=sender,
return_full_output=True
)
# THEN verify the event was emitted with correct parameters
self.assertIn('events', result)
events = result['events']
self.assertEqual(len(events), 1)
event = events[0]
self.assertEqual(event['event'], 'Transfer')
self.assertEqual(event['data_indexed']['from'], sender)
self.assertEqual(event['data_indexed']['to'], receiver)
self.assertEqual(event['data']['amount'], amount)
Common Exploits to Test For
Reentrancy
- Test that state is updated before external calls
- Verify operations complete atomically
def test_no_reentrancy_vulnerability(self):
# Set up the attack scenario (if possible with Xian)
# Verify state is properly updated before any external calls
# For example, check that balances are decreased before tokens are sent
# Verify proper operation ordering in the contract
Integer Overflow/Underflow
- Test with extremely large numbers
- Test arithmetic operations at boundaries
def test_integer_boundaries(self):
# Set a large balance
self.contract.balances["user"] = 10**20
# Test with large transfers
result = self.contract.transfer(amount=10**19, to="receiver", signer="user")
# Verify results are as expected
self.assertEqual(self.contract.balances["user"], 9*10**19)
self.assertEqual(self.contract.balances["receiver"], 10**19)
Front-Running Protection
- Test mechanisms that prevent frontrunning (e.g., commit-reveal)
- Test deadline-based protections
def test_front_running_protection(self):
# Test with deadlines to ensure transactions expire
deadline = Datetime(year=2023, month=1, day=1)
current_time = Datetime(year=2023, month=1, day=2) # After deadline
with self.assertRaises(Exception):
self.contract.time_sensitive_operation(
param1="value",
deadline=str(deadline),
environment={"now": current_time}
)
Authorization Bypass
- Test authorization for all privileged operations
- Try to access functions with different signers
def test_authorization_checks(self):
# Test admin functions with non-admin signers
with self.assertRaises(Exception):
self.contract.admin_function(param="value", signer="regular_user")
# Test with proper authorization
result = self.contract.admin_function(param="value", signer="admin")
self.assertTrue(result)
Best Practices Summary
- Test both positive and negative paths
- Test permissions and authorization thoroughly
- Use environment variables to test time-dependent behavior
- Verify event emissions using
return_full_output=True - Test against potential replay attacks and signature validation
- Check edge cases and boundary conditions
- Verify state consistency after operations
- Test for common security vulnerabilities
Related rules
Senior full-stack TypeScript, React, Node.js guidance with clean architecture, testing, and WHY-oriented reasoning.
Quantitative factor research skills for designing, evaluating, and mining alpha factors in equities markets.
Android development with Jetpack Compose, clean architecture, and Material Design 3.
Angular development with Novo Elements UI library using standalone components.
Expert Angular 18 + TypeScript development with Jest, emphasizing clean code and performance.
Manage Kubernetes clusters, add-ons, stacks, and credentials via the Ankra CLI platform.