This release is a pre-release and may not be stable for production use.
Scallop Python SDK
A Python SDK for interacting with the Scallop Lending Protocol on Sui blockchain.
English | 繁體中文
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
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
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
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
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
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.
Supply (Earn Interest)
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)
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
export NETWORK=mainnet
export PRIVATE_KEY="suiprivkey1..."
# Optional when you want to override the derived address
export WALLET_ADDRESS="0x..."
from sui_scallop_sdk import ScallopClient
client = ScallopClient.from_env()
PRIVATE_KEY is supported for config-driven initialization. MNEMONIC is not yet supported.
Borrow
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
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
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
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
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
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
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_idis optional forlock_sca_quick,extend_lock_period_quick,extend_lock_amount_quick,renew_expired_vesca_quick, andredeem_sca_quick- when
vesca_key_idis 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
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
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
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
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:
PRIVATE_KEY=suiprivkey1... # or base64/hex format
NETWORK=mainnet
RPC_URL=https://fullnode.mainnet.sui.io:443
Then use from_env():
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 controlled by a wallet |
list_user_obligation_keys(address) |
List obligations with the owned key each one needs |
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; amount is in market coin / sCoin units, not underlying |
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
# 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... |
✅ |
| 2 | supply_quick (0.1 SUI) | F21Efd3... |
✅ |
| 3 | sCoin roundtrip (supply→mint→burn→withdraw) | BkLb4LB... |
✅ |
| 4 | Full lending cycle (Obligation + Collateral + Oracle + Borrow USDC) | BVWwpce... |
✅ |
| 5 | repay_quick (repay USDC) | C27xDCh... |
✅ |
| 6 | withdraw_collateral (with Oracle update) | 8MMpZyb... |
✅ |
| 7 | withdraw_quick (redeem Market Coin) | CXsck8H... |
✅ |
| 8 | supply + withdraw (non-quick) | AwxC6dM... |
✅ |
| 9 | transfer_objects (split + transfer) | 9cqVRxN... |
✅ |
| 10 | lock_sca (veSCA lock 10 SCA) | EfNSJTH... |
✅ |
Total gas: ~0.027 SUI
Notes
- Mainnet-only: Scallop is deployed on mainnet;
network="testnet"/"devnet"raiseScallopConfigErrorrather than building transactions against placeholder addresses - Live addresses: Protocol package IDs change on every upgrade, and the protocol rejects calls into a superseded package, so the SDK refreshes addresses from the Scallop address API on first use. Set
SCALLOP_LIVE_ADDRESSES=0or passresolve_live_addresses=Falseto pin the compiled-in values - Private Key Formats: Supports
suiprivkey1...(Bech32), Base64, and Hex. Use environment variables instead of hardcoding - Gas Fees: Transactions consume SUI as gas; ensure sufficient balance. Supplying or repaying SUI is taken from the gas coin, so keep more than
amount + gas_budget - Oracle Updates: Call
update_asset_prices_quick()before borrow/liquidate operations; it raisesOraclePriceErrorif a price cannot be posted - Retrying failures: check
TransactionResult.error_kind— onlyvalidationandbuildare safe to retry blindly;submissionmeans the transaction may already be on chain
License
Apache License 2.0 - see LICENSE for details.
Metadata
Release files for sui-scallop-sdk 0.3.0a2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| sui_scallop_sdk-0.3.0a2.tar.gz | 275.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| sui_scallop_sdk-0.3.0a2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 584.1 kB
Release files / sui_scallop_sdk-0.3.0a2.tar.gz
| Download URL | sui_scallop_sdk-0.3.0a2.tar.gz |
|---|---|
| Size | 275.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
01ea118c374b781b29f0110848cf0db1e942c5bd337b10718dd590322a619fe0
|
|
BLAKE2b-256 checksum How to use checksums |
2993c9661ed475a3a6e844936e175075e7c45386ffc673e6cb0a8e0fe2f42bed
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 31, 2026.
Transparency logRelease files / sui_scallop_sdk-0.3.0a2-py3-none-any.whl
| Download URL | sui_scallop_sdk-0.3.0a2-py3-none-any.whl |
|---|---|
| Size | 308.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6ec78b5f1441eb21990834a24f6c2e2478d7ee92d99acaddbd030987ed9b19ea
|
|
BLAKE2b-256 checksum How to use checksums |
326ba9a4dac6ae9cbd98b6d9094f05a386c6d429ec9022875f49288ee7f1d390
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 31, 2026.
Transparency log