Metadata-Version: 2.4
Name: sui-sandbox
Version: 0.19.0
Classifier: Development Status :: 4 - Beta
Classifier: Programming Language :: Rust
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries
Summary: Python bindings for Sui Move package analysis and transaction replay
Keywords: sui,move,blockchain,walrus,replay
License: Apache-2.0
Requires-Python: >=3.9
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
Project-URL: Repository, https://github.com/Evan-Kim2028/sui-sandbox

# sui-sandbox

Python bindings for Sui Move package analysis, transaction replay, view function execution, and Move function fuzzing.

Built on [sui-sandbox](../../README.md) — runs the real Sui Move VM locally via PyO3. **All functions are standalone** — `pip install sui-sandbox` is all you need.

## Installation

### From PyPI

```bash
pip install sui-sandbox
```

### From source (requires Rust toolchain)

```bash
cd crates/sui-python
pip install maturin
maturin develop --release
```

## Quick Start

```python
import sui_sandbox

# Extract the full interface of the Sui framework
interface = sui_sandbox.extract_interface(package_id="0x2")
for mod_name, mod_data in interface["modules"].items():
    print(f"{mod_name}: {len(mod_data.get('functions', {}))} functions")

# Replay a historical transaction via Walrus (no API key needed)
result = sui_sandbox.replay(
    "At8M8D7QoW3HHXUBHHvrsdhko8hEDdLAeqkZBjNSKFk2",
    checkpoint=239615926,
)
print(f"Success: {result['local_success']}")

# Fuzz a Move function
report = sui_sandbox.fuzz_function("0x1", "u64", "max", iterations=50)
print(f"Successes: {report['outcomes']['successes']}")
```

## API Reference

#### `extract_interface(*, package_id=None, bytecode_dir=None, rpc_url="https://fullnode.mainnet.sui.io:443")`

Extract the complete interface JSON for a Move package — all modules, structs, functions, type parameters, abilities, and fields.

Provide either `package_id` (fetched via GraphQL) or `bytecode_dir` (local directory containing `bytecode_modules/*.mv` files), but not both.

**Returns:** `dict` with full interface tree.

```python
interface = sui_sandbox.extract_interface(package_id="0x1")
for mod_name, mod_data in interface["modules"].items():
    print(f"{mod_name}: {len(mod_data.get('functions', {}))} functions")
```

#### `get_latest_checkpoint()`

Get the latest archived checkpoint number from Walrus.

**Returns:** `int`

```python
cp = sui_sandbox.get_latest_checkpoint()
print(f"Latest checkpoint: {cp}")
```

#### `get_checkpoint(checkpoint)`

Fetch a checkpoint from Walrus and return a summary.

**Returns:** `dict` with `checkpoint`, `epoch`, `timestamp_ms`, `transaction_count`, `transactions` (list), and `object_versions_count`.

```python
data = sui_sandbox.get_checkpoint(239615926)
for tx in data["transactions"]:
    print(f"  {tx['digest']}: {tx['commands']} commands, {tx['input_objects']} inputs")
```

#### `fetch_package_bytecodes(package_id, *, resolve_deps=True)`

Fetch package bytecodes via GraphQL, optionally resolving transitive dependencies.

**Returns:** `dict` with `packages` (map of package ID to list of base64-encoded module bytecodes) and `count`.

```python
pkgs = sui_sandbox.fetch_package_bytecodes("0x2", resolve_deps=True)
print(f"Fetched {pkgs['count']} packages")
```

#### `json_to_bcs(type_str, object_json, package_bytecodes)`

Convert a Sui object JSON representation to BCS bytes using Move type layout.

**Returns:** `bytes`

#### `transaction_json_to_bcs(transaction_json)`

Convert Snowflake-style `TRANSACTION_JSON` (or canonical Sui `TransactionData` JSON)
into raw transaction BCS bytes.

This is useful when your pipeline has transaction JSON but not transaction BCS.

**Returns:** `bytes`

#### `call_view_function(package_id, module, function, *, type_args=None, object_inputs=None, pure_inputs=None, child_objects=None, package_bytecodes=None, fetch_deps=True)`

Execute a Move function in the local VM with full control over object and pure inputs.

**Returns:** `dict` with `success`, `error`, `return_values`, `return_type_tags`, `gas_used`.

`object_inputs` entries must use:

```python
{
    "object_id": "0x...",
    "type_tag": "0x2::...",
    "bcs_bytes": [1, 2, 3],   # or bytes in Python
    "is_shared": False,       # optional
    "mutable": False,         # optional
}
```

Legacy compatibility: `owner` is also accepted as an alias for `is_shared`:
`"immutable"` / `"address_owned"` => non-shared, `"shared"` => shared.

#### `fuzz_function(package_id, module, function, *, iterations=100, seed=None, sender="0x0", gas_budget=50_000_000_000, type_args=[], fail_fast=False, max_vector_len=32, dry_run=False, fetch_deps=True)`

Fuzz a Move function with randomly generated inputs.

Use `dry_run=True` to check parameter classification without executing.

**Returns:** `dict` with `target`, `classification`, `outcomes` (successes/errors), `gas_profile`.

```python
# Dry run — check if function is fuzzable
info = sui_sandbox.fuzz_function("0x1", "u64", "max", dry_run=True)
print(f"Fuzzable: {info['classification']['is_fully_fuzzable']}")

# Full fuzz run
report = sui_sandbox.fuzz_function("0x1", "u64", "max", iterations=50, seed=42)
print(f"Successes: {report['outcomes']['successes']}")
```

#### `import_state(*, state=None, transactions=None, objects=None, packages=None, cache_dir=None)`

Import replay data from JSON/JSONL/CSV into a local replay cache.

```python
sui_sandbox.import_state(
    transactions="exports/transactions.csv",
    objects="exports/objects.jsonl",
    packages="exports/packages.csv",
    cache_dir=".sui-cache",
)
```

#### `deserialize_transaction(raw_bcs)` / `deserialize_package(bcs)`

Decode raw BCS blobs into structured JSON for debugging or preprocessing.

#### `replay(digest=None, *, rpc_url=..., source="hybrid", checkpoint=None, state_file=None, cache_dir=None, allow_fallback=True, prefetch_depth=3, prefetch_limit=200, auto_system_objects=True, no_prefetch=False, compare=False, analyze_only=False, verbose=False)`

Replay a historical Sui transaction locally with the Move VM.

Replay source modes:
- `checkpoint=...` uses Walrus (no API key needed)
- `state_file=...` replays from a local exported state file
- `source="local"` (or `cache_dir=...`) replays from imported local cache
- otherwise uses gRPC/hybrid (requires `SUI_GRPC_API_KEY`)

Use `analyze_only=True` to inspect state hydration without executing the transaction.

Use `compare=True` to compare local execution results with on-chain effects.

**Returns:** `dict` — replay results (with `local_success`, `effects`, `execution_path`, optionally `comparison`) or analysis summary (with `commands`, `inputs`, `objects`, `packages`, `input_summary`).

```python
# Analyze state hydration only (no VM execution)
analysis = sui_sandbox.replay(
    "At8M8D7QoW3HHXUBHHvrsdhko8hEDdLAeqkZBjNSKFk2",
    checkpoint=239615926,
    analyze_only=True,
)
print(f"Commands: {analysis['commands']}, Objects: {analysis['objects']}")

# Full replay via Walrus (no API key needed)
result = sui_sandbox.replay(
    "At8M8D7QoW3HHXUBHHvrsdhko8hEDdLAeqkZBjNSKFk2",
    checkpoint=239615926,
)
print(f"Success: {result['local_success']}")

# Full replay via local state file
result = sui_sandbox.replay(state_file="exports/replay_state.json")

# Full replay via local cache import
sui_sandbox.import_state(state="exports/replay_state.json", cache_dir=".sui-cache")
result = sui_sandbox.replay(digest="DigestHere...", source="local", cache_dir=".sui-cache")

# Full replay via gRPC with comparison
import os
os.environ["SUI_GRPC_API_KEY"] = "your-key"
result = sui_sandbox.replay("DigestHere...", compare=True)
if result.get("comparison"):
    print(f"Status match: {result['comparison']['status_match']}")
```

## Platform Support

Pre-built wheels are available for:
- Linux x86_64 (glibc 2.17+)
- Linux aarch64 (glibc 2.17+)
- macOS x86_64 (10.12+)
- macOS aarch64 (11.0+)

Building from source requires Rust 1.80+ and Python 3.9+.

## License

Apache 2.0

