Metadata-Version: 2.4
Name: sui-scallop-sdk
Version: 0.3.0a1
Summary: Python SDK for Scallop Lending Protocol on Sui
Project-URL: Homepage, https://github.com/scallop-io/sui-scallop-sdk-python
Project-URL: Documentation, https://github.com/scallop-io/sui-scallop-sdk-python#readme
Project-URL: Repository, https://github.com/scallop-io/sui-scallop-sdk-python
Project-URL: Bug Tracker, https://github.com/scallop-io/sui-scallop-sdk-python/issues
Project-URL: Changelog, https://github.com/scallop-io/sui-scallop-sdk-python/blob/master/CHANGELOG.md
Author-email: Scallop Team <team@scallop.io>
License: Apache-2.0
License-File: LICENSE
Keywords: blockchain,defi,lending,scallop,sui
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: base58>=2.1.0
Requires-Dist: httpx>=0.25.0
Requires-Dist: pydantic>=2.0.0
Requires-Dist: pynacl>=1.5.0
Requires-Dist: python-dotenv>=1.0.0
Provides-Extra: dev
Requires-Dist: mypy>=1.0.0; extra == 'dev'
Requires-Dist: pre-commit>=3.0.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
Requires-Dist: pytest>=7.0.0; extra == 'dev'
Requires-Dist: ruff>=0.8.0; extra == 'dev'
Description-Content-Type: text/markdown

# Scallop Python SDK

[![PyPI version](https://img.shields.io/pypi/v/sui-scallop-sdk)](https://pypi.org/project/sui-scallop-sdk/)
[![CI](https://github.com/scallop-io/sui-scallop-sdk-python/actions/workflows/ci.yml/badge.svg)](https://github.com/scallop-io/sui-scallop-sdk-python/actions/workflows/ci.yml)
[![Python](https://img.shields.io/pypi/pyversions/sui-scallop-sdk)](https://pypi.org/project/sui-scallop-sdk/)
[![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE)

A Python SDK for interacting with the [Scallop Lending Protocol](https://scallop.io) on Sui blockchain.

English | [繁體中文](README_zh-TW.md)

## Features

### Core Lending Operations
- **Supply/Withdraw**: supply, withdraw (supply assets to earn interest)
- **Borrow/Repay**: borrow, repay
- **Collateral Management**: deposit_collateral, withdraw_collateral
- **Liquidation**: liquidate
- **Flash Loans**: borrow_flash_loan, repay_flash_loan

### Quick Methods
- **Auto Coin Selection**: supply_quick, withdraw_quick, repay_quick
- **Auto Price Update**: borrow_quick, liquidate_quick
- **Oracle Update**: update_asset_prices_quick
- **veSCA Lifecycle Helpers**: lock_sca_quick, extend_lock_period_quick, extend_lock_amount_quick, renew_expired_vesca_quick, redeem_sca_quick, split_vesca_quick, merge_vesca_quick

### Advanced Features
- **Staking Pool (Spool)**: Stake market coins for rewards
- **sCoin**: Mint/burn sCoin
- **veSCA**: Lock SCA for veSCA
- **Borrow Incentive**: Stake obligations for rewards
- **Referral**: Referral system
- **Loyalty Program**: Loyalty rewards

### Infrastructure
- **Cryptography**: Ed25519 signing, address derivation, Bech32 key import (`suiprivkey1...`)
- **BCS Serialization**: Full transaction serialization support (mainnet verified)
- **Oracle Integration**: Pyth + X Oracle

## Installation

```bash
pip install sui-scallop-sdk

# Or install from source
git clone https://github.com/scallop-io/sui-scallop-sdk-python
cd sui-scallop-sdk-python
pip install -e .
```

## Quick Start

### Query Obligation

```python
from sui_scallop_sdk import ScallopClient

# Initialize client
client = ScallopClient(network="mainnet")

# Query obligation
query = client.create_query()
obligation = query.get_obligation("0x...")

print(f"Risk Level: {obligation.risk_level}")
print(f"Is Liquidatable: {obligation.is_liquidatable}")
print(f"Total Debt USD: ${obligation.total_debt_usd}")
print(f"Total Collateral USD: ${obligation.total_collateral_usd}")
```

### Query Market And TVL

```python
from sui_scallop_sdk import ScallopClient

client = ScallopClient(network="mainnet")
query = client.create_query()

pool = query.get_market_pool("sui")
collateral = query.get_market_collateral("sui")
pool_address = query.get_pool_address("sui")
tvl = query.get_tvl()
prices = query.get_coin_prices(["sui", "sca", "usdc"])

print(pool.supply_amount, pool.borrow_amount, pool.supply_apy)
print(collateral.deposit_amount, collateral.collateral_factor)
print(pool_address.lending_pool_address, pool_address.flash_loan_fee_object)
print(tvl.total_value)
print(prices)
```

### Query Portfolio And Lendings

```python
from sui_scallop_sdk import ScallopClient

client = ScallopClient.from_env()
query = client.create_query()

portfolio = query.get_user_portfolio(client.wallet_address)
lendings = query.get_lendings(client.wallet_address)

print(portfolio.total_supply_value, portfolio.total_borrow_value, portfolio.net_apy)
print(portfolio.obligations)
print(lendings)
```

### Query Spool, sCoin, veSCA, And Loyalty

```python
from sui_scallop_sdk import ScallopClient

client = ScallopClient.from_env()
query = client.create_query()

spool = query.get_spool("ssui")
scoin_supply = query.get_scoin_total_supply("sui")
scoin_amounts = query.get_scoin_amounts(["ssui", "susdc"], client.wallet_address)
vescas = query.get_vescas(client.wallet_address, exclude_empty=True)

print(spool.reward_apr if spool else None)
print(scoin_supply, scoin_amounts)

if vescas:
    loyalty = query.get_loyalty_program_info(vescas[0].key_id)
    vesca_loyalty = query.get_vesca_loyalty_program_info(vescas[0].key_id)
    print(vescas[0].locked_sca_amount, vescas[0].current_vesca_balance)
    print(loyalty.pending_reward, vesca_loyalty.pending_vesca_reward)
```

For a runnable walkthrough, see [examples/query_example.py](examples/query_example.py).

### Supply (Earn Interest)

```python
from sui_scallop_sdk import ScallopClient

# Initialize with wallet credentials
# Supports: suiprivkey1... (bech32), base64, hex, or raw bytes
client = ScallopClient(
    secret_key="suiprivkey1...",
    network="mainnet",
)

# Create transaction
builder = client.create_builder()
tx = builder.create_tx_block()

# Supply 1 SUI to lending pool
market_coin_idx = tx.supply(coin_input_idx=0, coin_name="sui")
tx.transfer_objects([market_coin_idx], client.wallet_address)

# Execute
result = builder.sign_and_send_tx_block(tx)

if result.success:
    print(f"Success! TX: {result.digest}")
else:
    print(f"Failed: {result.error}")
```

### Quick Supply (Auto Coin Selection)

```python
tx = builder.create_tx_block()

# Use supply_quick for automatic coin selection
market_coin_idx = tx.supply_quick(
    amount=1_000_000_000,  # 1 SUI (9 decimals)
    coin_name="sui",
    sender=client.wallet_address
)
tx.transfer_objects([market_coin_idx], client.wallet_address)

result = builder.sign_and_send_tx_block(tx)
```

### Initialize From Environment

```bash
export NETWORK=mainnet
export PRIVATE_KEY="suiprivkey1..."
# Optional when you want to override the derived address
export WALLET_ADDRESS="0x..."
```

```python
from sui_scallop_sdk import ScallopClient

client = ScallopClient.from_env()
```

`PRIVATE_KEY` is supported for config-driven initialization. `MNEMONIC` is not yet supported.

### Borrow

```python
tx = builder.create_tx_block()

# Borrow with automatic oracle price update
borrowed_coin_idx = tx.borrow_quick(
    amount=100_000_000,  # 100 USDC (6 decimals)
    coin_name="usdc",
    obligation_id="0x...",
    obligation_key="0x...",
)
tx.transfer_objects([borrowed_coin_idx], client.wallet_address)

result = builder.sign_and_send_tx_block(tx)
```

### Repay

```python
tx = builder.create_tx_block()

# Auto coin selection for repay
tx.repay_quick(
    amount=50_000_000,  # Repay 50 USDC
    coin_name="usdc",
    obligation_id="0x...",
    sender=client.wallet_address,
)

result = builder.sign_and_send_tx_block(tx)
```

### Flash Loan

```python
tx = builder.create_tx_block()

# Borrow flash loan (returns TxResult objects for PTB tuple outputs)
coin_result, receipt_result = tx.borrow_flash_loan(
    amount=1_000_000_000,  # 1 SUI
    coin_name="sui"
)

# ... perform arbitrage or other operations ...

# Repay (must be in same transaction, includes fee)
tx.repay_flash_loan(coin_result, receipt_result, "sui")

result = builder.sign_and_send_tx_block(tx)
```

### Liquidation

```python
tx = builder.create_tx_block()

# Auto coin selection liquidation (auto updates oracle prices)
remaining_debt_idx, collateral_idx = tx.liquidate_quick(
    obligation_id="0x...",
    repay_amount=100_000_000,  # 100 USDC
    debt_coin_name="usdc",
    collateral_coin_name="sui",
    sender=client.wallet_address,
)

# Transfer received collateral
tx.transfer_objects(
    [{"kind": "NestedResult", "index": collateral_idx, "resultIndex": 1}],
    client.wallet_address,
)

result = builder.sign_and_send_tx_block(tx)
```

## Advanced Features

### Spool Staking

```python
tx = builder.create_tx_block()

# Create stake account
stake_account_idx = tx.create_stake_account(stake_pool_id, "sui")

# Stake market coin
tx.stake_spool(
    stake_account_id="0x...",
    stake_pool_id="0x...",
    coin_input_idx=market_coin_idx,
    coin_name="sui"
)

# Claim rewards
reward_idx = tx.claim_spool_rewards(
    stake_account_id="0x...",
    stake_pool_id="0x...",
    reward_pool_id="0x...",
    coin_name="sca"
)
tx.transfer_objects([reward_idx], client.wallet_address)

result = builder.sign_and_send_tx_block(tx)
```

### veSCA Lock

```python
tx = builder.create_tx_block()

# Lock SCA to get veSCA key (365 days)
vesca_key_idx = tx.lock_sca(
    sca_coin_idx=sca_coin_input,
    lock_period_days=365
)
tx.transfer_objects([vesca_key_idx], client.wallet_address)

# Extend lock period
tx.extend_lock_period(
    vesca_key_id="0x...",
    new_unlock_at=new_unlock_timestamp  # Unix epoch in seconds
)

# Add more SCA to existing lock
tx.extend_lock_amount(
    vesca_key_id="0x...",
    sca_coin_idx=additional_sca_input
)

# Redeem SCA (after lock expires)
sca_idx = tx.redeem_sca(vesca_key_id="0x...")
tx.transfer_objects([sca_idx], client.wallet_address)

result = builder.sign_and_send_tx_block(tx)
```

### veSCA Quick Methods

```python
tx = builder.create_tx_block()

# Initial lock with automatic SCA coin selection
new_vesca_idx = tx.lock_sca_quick(
    amount=10_000_000_000,      # 10 SCA in raw units
    lock_period_days=365,
    sender=client.wallet_address,
)
tx.transfer_objects([new_vesca_idx], client.wallet_address)

# Extend only the lock duration of the highest-balance veSCA
tx.extend_lock_period_quick(
    lock_period_days=30,
    sender=client.wallet_address,
)

# Top up an existing veSCA with automatic coin selection
tx.extend_lock_amount_quick(
    sca_amount=1_000_000_000,   # 1 SCA in raw units
    sender=client.wallet_address,
    vesca_key_id="0x...",       # optional, defaults to the sender's highest-balance veSCA
)

# Renew an expired veSCA
tx.renew_expired_vesca_quick(
    sca_amount=10_000_000_000,
    lock_period_days=180,
    sender=client.wallet_address,
    vesca_key_id="0x...",
)

# Redeem unlocked SCA and auto-transfer it back to the sender
tx.redeem_sca_quick(
    sender=client.wallet_address,
    vesca_key_id="0x...",
)

# Split a veSCA key and auto-transfer the new key NFT
tx.split_vesca_quick(
    split_amount=5_000_000_000,
    vesca_key_id="0x...",
    sender=client.wallet_address,
)

# Merge two veSCA keys, aligning unlock times automatically when needed
tx.merge_vesca_quick(
    target_vesca_key_id="0x...",
    source_vesca_key_id="0x...",
)

result = builder.sign_and_send_tx_block(tx)
```

Notes:
- veSCA quick methods stay builder-first in Python, so methods that need coin selection or auto-transfer still require an explicit `sender`
- `vesca_key_id` is optional for `lock_sca_quick`, `extend_lock_period_quick`, `extend_lock_amount_quick`, `renew_expired_vesca_quick`, and `redeem_sca_quick`
- when `vesca_key_id` is omitted, the SDK uses the sender's highest-balance veSCA position
- amounts are raw on-chain units, so `1 SCA = 1_000_000_000`

### sCoin Operations

```python
tx = builder.create_tx_block()

# Mint sCoin (from Market Coin)
scoin_idx = tx.mint_scoin(market_coin_idx=market_coin, coin_name="sui")
tx.transfer_objects([scoin_idx], client.wallet_address)

# Burn sCoin (get Market Coin back)
market_coin_idx = tx.burn_scoin(scoin_idx=scoin_input, coin_name="sui")
tx.transfer_objects([market_coin_idx], client.wallet_address)

result = builder.sign_and_send_tx_block(tx)
```

### Borrow Incentive

```python
tx = builder.create_tx_block()

# Stake obligation
tx.stake_obligation(
    obligation_id="0x...",
    obligation_key="0x..."
)

# Claim rewards
reward_idx = tx.claim_borrow_incentive(
    obligation_id="0x...",
    obligation_key="0x...",
    coin_name="sca"
)
tx.transfer_objects([reward_idx], client.wallet_address)

result = builder.sign_and_send_tx_block(tx)
```

### Referral Program

```python
tx = builder.create_tx_block()

# Bind to a referrer (using referrer's veSCA key ID)
tx.bind_referral(referrer_vesca_key_id="0x...")

# Claim referral rewards
reward_idx = tx.claim_referral_revenue(
    vesca_key_id="0x...",  # Your veSCA key ID
    coin_name="sui"
)
tx.transfer_objects([reward_idx], client.wallet_address)

result = builder.sign_and_send_tx_block(tx)
```

### Loyalty Program

```python
tx = builder.create_tx_block()

# Claim loyalty rewards
reward_idx = tx.claim_loyalty_revenue(vesca_key_id="0x...")
tx.transfer_objects([reward_idx], client.wallet_address)

result = builder.sign_and_send_tx_block(tx)
```

## Configuration

### Environment Variables

Create a `.env` file:

```env
PRIVATE_KEY=suiprivkey1...  # or base64/hex format
NETWORK=mainnet
RPC_URL=https://fullnode.mainnet.sui.io:443
```

Then use `from_env()`:

```python
client = ScallopClient.from_env()
```

### Supported Coins

| Coin | SDK Name | Decimals |
|------|----------|----------|
| SUI | `sui` | 9 |
| USDC (Native) | `usdc` | 6 |
| wUSDC (Wormhole) | `wusdc` | 6 |
| wUSDT (Wormhole) | `wusdt` | 6 |
| wETH (Wormhole) | `weth` | 8 |
| wBTC (Wormhole) | `wbtc` | 8 |
| wSOL (Wormhole) | `wsol` | 8 |
| wAPT (Wormhole) | `wapt` | 8 |
| CETUS | `cetus` | 9 |
| SCA | `sca` | 9 |
| DEEP | `deep` | 6 |
| FUD | `fud` | 5 |
| BLUB | `blub` | 2 |
| NS | `ns` | 6 |
| afSUI | `afsui` | 9 |
| haSUI | `hasui` | 9 |
| vSUI | `vsui` | 9 |
| HAEDAL | `haedal` | 9 |
| WAL | `wal` | 9 |
| haWAL | `hawal` | 9 |
| wWAL | `wwal` | 9 |
| USDY | `usdy` | 6 |
| FDUSD | `fdusd` | 6 |
| mUSD | `musd` | 9 |
| sbETH (Sui Bridge) | `sbeth` | 8 |
| sbUSDT (Sui Bridge) | `sbusdt` | 6 |
| sbwBTC (Sui Bridge) | `sbwbtc` | 8 |
| xBTC | `xbtc` | 8 |
| LOFI | `lofi` | 9 |
| suiUSDe | `suiusde` | 6 |
| zBTC | `zwbtc` | 8 |

> **Note:** The supported coins list is fetched from Scallop API. Some coins may only support specific operations (lending, collateral, or borrowing). Check Scallop documentation for the latest availability.

## API Reference

### ScallopClient

Main entry point for SDK.

| Method | Description |
|--------|-------------|
| `create_query()` | Create query interface |
| `create_builder()` | Create transaction builder |

### ScallopQuery

| Method | Description |
|--------|-------------|
| `get_obligation(obligation_id)` | Query one obligation |
| `list_user_obligations(address)` | List obligation IDs owned by a wallet |
| `get_coin_price(coin_name)` | Query one oracle-backed coin price |
| `get_coin_prices(coin_names)` | Query multiple oracle-backed coin prices |
| `get_market_pool(coin_name)` | Query one lending pool |
| `get_market_pools(coin_names=None)` | Query lending pools |
| `get_market_collateral(coin_name)` | Query one collateral pool |
| `get_market_collaterals(coin_names=None)` | Query collateral pools |
| `get_pool_address(coin_name)` | Query derived on-chain object addresses for one pool |
| `get_tvl(coin_names=None)` | Query aggregate TVL metrics |
| `get_lendings(address, coin_names=None)` | Query active lending positions |
| `get_user_portfolio(address)` | Query aggregated wallet portfolio |
| `get_spool(market_coin_name)` | Query one spool staking pool |
| `get_spools(market_coin_names=None)` | Query spool staking pools |
| `get_stake_accounts(market_coin_name, address)` | Query owned stake accounts for one spool |
| `get_all_stake_accounts(address)` | Query all owned stake accounts |
| `get_scoin_total_supply(coin_name)` | Query total sCoin supply |
| `get_scoin_amounts(coin_names=None, address=None)` | Query owned sCoin balances |
| `get_scoin_swap_rate(from_scoin, to_scoin)` | Query swap rate between sCoins |
| `get_vesca(vesca_key)` | Query one veSCA position |
| `get_vescas(address, exclude_empty=False)` | Query wallet veSCA positions |
| `get_vesca_treasury_info()` | Query aggregate veSCA treasury stats |
| `get_loyalty_program_info(vesca_key=None)` | Query loyalty reward pool and pending reward |
| `get_vesca_loyalty_program_info(vesca_key=None)` | Query veSCA loyalty pool and pending reward |
| `get_asset_oracles(coin_names=None)` | Query primary and secondary oracle routing |
| `get_price_update_policies()` | Query xOracle update policy rule objects |

### ScallopTxBlock - Core Methods

| Method | Description |
|--------|-------------|
| `supply(coin_input_idx, coin_name)` | Supply asset to lending pool |
| `withdraw(market_coin_idx, coin_name)` | Withdraw from lending pool |
| `borrow(obligation_id, obligation_key, amount, coin_name)` | Borrow from pool |
| `repay(obligation_id, coin_input_idx, coin_name)` | Repay borrowed amount |
| `deposit_collateral(obligation_id, coin_input_idx, coin_name)` | Deposit collateral |
| `withdraw_collateral(obligation_id, obligation_key, amount, coin_name)` | Withdraw collateral |
| `liquidate(obligation_id, repay_coin_idx, debt_coin_name, collateral_coin_name)` | Liquidate position → `(TxResult, TxResult)` |
| `borrow_flash_loan(amount, coin_name)` | Borrow flash loan → `(TxResult, TxResult)` |
| `repay_flash_loan(coin_idx, receipt_idx, coin_name)` | Repay flash loan (accepts `int` or `TxResult`) |

### ScallopTxBlock - Quick Methods

| Method | Description |
|--------|-------------|
| `supply_quick(amount, coin_name, sender)` | Auto coin selection supply |
| `withdraw_quick(amount, coin_name, sender)` | Auto coin selection withdraw |
| `borrow_quick(amount, coin_name, obligation_id, obligation_key)` | Auto price update borrow |
| `repay_quick(amount, coin_name, obligation_id, sender)` | Auto coin selection repay |
| `liquidate_quick(...)` | Auto coin selection liquidation |
| `lock_sca_quick(amount=None, lock_period_days=None, sender=None, vesca_key_id=None)` | Smart veSCA lifecycle helper for initial lock, extend, top-up, or expired renewal |
| `extend_lock_period_quick(lock_period_days, vesca_key_id=None, sender=None)` | Extend an active veSCA lock using sender lookup when needed |
| `extend_lock_amount_quick(sca_amount, sender, vesca_key_id=None)` | Auto-select SCA and add it to an active veSCA |
| `renew_expired_vesca_quick(sca_amount, lock_period_days, sender, vesca_key_id=None)` | Renew an expired veSCA with automatic SCA coin selection |
| `redeem_sca_quick(vesca_key_id=None, sender=None, transfer_sca=True)` | Redeem unlocked SCA and optionally auto-transfer it |
| `split_vesca_quick(split_amount, vesca_key_id, sender=None, transfer_vesca_key=True)` | Split a veSCA key with optional auto-transfer of the new key |
| `merge_vesca_quick(target_vesca_key_id, source_vesca_key_id)` | Merge veSCA keys with subscription safety and unlock alignment |

### ObligationInfo

| Property | Type | Description |
|----------|------|-------------|
| `obligation_id` | `str` | Object ID |
| `debts` | `List[DebtInfo]` | Debt positions |
| `collaterals` | `List[CollateralInfo]` | Collateral positions |
| `risk_level` | `Decimal` | Risk level (>= 1.0 = liquidatable) |
| `is_liquidatable` | `bool` | Liquidation eligibility |
| `is_bad_debt` | `bool` | Has debt but no collateral |
| `total_debt_usd` | `Decimal` | Total debt in USD |
| `total_collateral_usd` | `Decimal` | Total collateral in USD |

## Development

```bash
# Install dev dependencies
pip install -e ".[dev]"

# Run tests
make test

# Lint and format
make lint
make format

# Type checking
make typecheck

# Run all checks
make all
```

## Mainnet Verification

All core operations have been verified on Sui mainnet (10/10 tests passed):

| # | Operation | Transaction | Status |
|---|-----------|-------------|--------|
| 1 | Flash Loan (borrow + repay SUI) | [`F32f8cr...`](https://suiscan.xyz/mainnet/tx/F32f8crxphudTVixwwybCDFZT7dyRhawLHvG8jmFieBE) | ✅ |
| 2 | supply_quick (0.1 SUI) | [`F21Efd3...`](https://suiscan.xyz/mainnet/tx/F21Efd3cfyyMFmmHZRPqnDzq68mb3uSMWQu6Ecqx7ygj) | ✅ |
| 3 | sCoin roundtrip (supply→mint→burn→withdraw) | [`BkLb4LB...`](https://suiscan.xyz/mainnet/tx/BkLb4LB2jhCDgoWhXpXW2RAL5nw1LG94Stf4vR2mh1k) | ✅ |
| 4 | Full lending cycle (Obligation + Collateral + Oracle + Borrow USDC) | [`BVWwpce...`](https://suiscan.xyz/mainnet/tx/BVWwpce8FUopWS8eBvKmJAa2zeuuSkg5g6TzmWr4cSwT) | ✅ |
| 5 | repay_quick (repay USDC) | [`C27xDCh...`](https://suiscan.xyz/mainnet/tx/C27xDChCtdmTkK6mVBbDy24GnHbJS7otomW2DNzZuJXS) | ✅ |
| 6 | withdraw_collateral (with Oracle update) | [`8MMpZyb...`](https://suiscan.xyz/mainnet/tx/8MMpZybeWuwMS4uykybpnVb9brcbNcnc3XJs988ypfQc) | ✅ |
| 7 | withdraw_quick (redeem Market Coin) | [`CXsck8H...`](https://suiscan.xyz/mainnet/tx/CXsck8HZvQUYky5RLBUmwSE6A9G7fsb24VLYn6y9kiay) | ✅ |
| 8 | supply + withdraw (non-quick) | [`AwxC6dM...`](https://suiscan.xyz/mainnet/tx/AwxC6dMFL9pFC7QJMKM58HhKg9GLGrz9xnzBqrvYL1cG) | ✅ |
| 9 | transfer_objects (split + transfer) | [`9cqVRxN...`](https://suiscan.xyz/mainnet/tx/9cqVRxN6GsH5EWiFAXDFA44NbKQPAPy5sC4gAiYLanYW) | ✅ |
| 10 | lock_sca (veSCA lock 10 SCA) | [`EfNSJTH...`](https://suivision.xyz/txblock/EfNSJTHJ3f4S7ic1iP1XPYyF72Ne4uj7wFL8ekXWUJae) | ✅ |

Total gas: ~0.027 SUI

## Notes

1. **Mainnet-first**: Currently configured with mainnet addresses; testnet/devnet are placeholders
2. **Private Key Formats**: Supports `suiprivkey1...` (Bech32), Base64, and Hex. Use environment variables instead of hardcoding
3. **Gas Fees**: Transactions consume SUI as gas; ensure sufficient balance
4. **Oracle Updates**: Call `update_asset_prices_quick()` before borrow/liquidate operations

## License

Apache License 2.0 - see [LICENSE](LICENSE) for details.
