Sui RPC Client
Direct Sui JSON-RPC access for low-level blockchain queries and transaction execution.
Overview
The SDK includes SuiRpcClient (sync) and AsyncSuiRpcClient (async) for direct Sui blockchain interaction. These are used internally by ScallopQuery and ScallopBuilder, but you can also use them directly for custom queries.
Quick Start
from sui_scallop_sdk import SuiRpcClient
# Standalone
rpc = SuiRpcClient("https://fullnode.mainnet.sui.io:443", timeout=30.0)
# Or from an existing ScallopClient
client = ScallopClient(network="mainnet")
rpc = client.rpc_client
SuiRpcClient Methods
Object Queries
# Get single object
obj = rpc.get_object(
"0xOBJECT_ID",
options={"showContent": True, "showType": True, "showOwner": True},
)
print(obj["data"]["content"])
# Get multiple objects in one call
objects = rpc.get_objects(
["0xOBJ_1", "0xOBJ_2", "0xOBJ_3"],
options={"showContent": True},
)
# Get objects owned by address
owned = rpc.get_owned_objects(
owner="0xADDRESS",
object_type="0x2::coin::Coin<0x2::sui::SUI>", # Optional filter
cursor=None, # For pagination
limit=50,
)
for item in owned["data"]:
print(item["data"]["objectId"])
Coin Queries
# Get coins of specific type
coins = rpc.get_coins(
owner="0xADDRESS",
coin_type="0x2::sui::SUI", # Optional
limit=50,
)
for coin in coins["data"]:
print(f"ID: {coin['coinObjectId']}, Balance: {coin['balance']}")
# Get ALL coins (any type)
all_coins = rpc.get_all_coins(owner="0xADDRESS", limit=50)
# Get aggregated balance
balance = rpc.get_balance(
owner="0xADDRESS",
coin_type="0x2::sui::SUI",
)
print(f"Total: {balance['totalBalance']}")
# Get all balances across all coin types
balances = rpc.get_all_balances(owner="0xADDRESS")
for b in balances:
print(f"{b['coinType']}: {b['totalBalance']}")
Dynamic Fields
Dynamic fields are on-chain key-value pairs attached to objects. Scallop uses them extensively for obligation data storage.
# List dynamic fields of a parent object
fields = rpc.get_dynamic_fields(
parent_id="0xPARENT_OBJECT",
cursor=None,
limit=50,
)
for field in fields["data"]:
print(f"Name: {field['name']}, Type: {field['objectType']}")
# Get specific dynamic field by name
field_obj = rpc.get_dynamic_field_object(
parent_id="0xPARENT_OBJECT",
name={"type": "address", "value": "0x123..."},
)
Transaction Execution
import base64
# Dry run (simulate)
dry_result = rpc.dry_run_transaction(
tx_bytes=base64.b64encode(tx_bytes).decode(),
)
print(f"Gas: {dry_result['effects']['gasUsed']}")
# Execute signed transaction
exec_result = rpc.execute_transaction(
tx_bytes=base64.b64encode(tx_bytes).decode(),
signatures=["base64_signature_string"],
options={
"showEffects": True,
"showEvents": True,
"showObjectChanges": True,
},
)
print(f"Digest: {exec_result['digest']}")
Utility Methods
# Get current gas price
gas_price = rpc.get_reference_gas_price()
print(f"Gas price: {gas_price} MIST")
# Get chain identifier
chain_id = rpc.get_chain_identifier()
print(f"Chain: {chain_id}")
Context Manager
with SuiRpcClient("https://fullnode.mainnet.sui.io:443") as rpc:
obj = rpc.get_object("0x...")
# Connection automatically closed
Or manually:
rpc = SuiRpcClient("https://fullnode.mainnet.sui.io:443")
try:
# ... use rpc
finally:
rpc.close()
AsyncSuiRpcClient
Same API as SuiRpcClient, but all methods are async:
from sui_scallop_sdk.sui_rpc import AsyncSuiRpcClient # Not in __all__, import directly
import asyncio
async def main():
async with AsyncSuiRpcClient("https://fullnode.mainnet.sui.io:443") as rpc:
# All methods are awaitable
obj = await rpc.get_object("0xOBJECT_ID")
balance = await rpc.get_balance("0xADDRESS")
coins = await rpc.get_coins("0xADDRESS")
# Parallel queries
results = await asyncio.gather(
rpc.get_balance("0xADDR1"),
rpc.get_balance("0xADDR2"),
rpc.get_balance("0xADDR3"),
)
asyncio.run(main())
Available Async Methods
| Method | Signature |
|---|---|
get_object |
async (object_id, options?) -> dict |
get_coins |
async (owner, coin_type?, cursor?, limit?) -> dict |
get_balance |
async (owner, coin_type?) -> dict |
close |
async () -> None |
The async client currently supports a subset of methods. For full coverage, use the sync client.
Error Handling
from sui_scallop_sdk import SuiRpcError
try:
obj = rpc.get_object("0xinvalid")
except SuiRpcError as e:
print(f"RPC error {e.code}: {e.message}")
SuiRpcError has:
code: int— JSON-RPC error codemessage: str— Error description
Pagination Pattern
Many RPC methods return paginated results:
all_coins = []
cursor = None
while True:
result = rpc.get_coins(
owner=address,
coin_type=coin_type,
cursor=cursor,
limit=50,
)
all_coins.extend(result["data"])
if not result.get("hasNextPage"):
break
cursor = result.get("nextCursor")
print(f"Found {len(all_coins)} coins")
Complete Example: Inspect Obligation On-Chain
from sui_scallop_sdk import ScallopClient, SuiRpcClient
client = ScallopClient(network="mainnet")
rpc = client.rpc_client
obligation_id = "0x..."
# Get raw obligation object
obj = rpc.get_object(obligation_id, options={"showContent": True, "showType": True})
content = obj["data"]["content"]
print(f"Type: {obj['data']['type']}")
print(f"Fields: {list(content['fields'].keys())}")
# Explore dynamic fields (debts, collaterals stored here)
fields = rpc.get_dynamic_fields(obligation_id)
for f in fields["data"]:
print(f" Field: {f['name']['value']} -> {f['objectType']}")
# Read individual field
detail = rpc.get_dynamic_field_object(obligation_id, f["name"])
print(f" Content: {detail['data']['content']['fields']}")
sui-kit (TypeScript) — Higher-Level Building Blocks
The Python SuiRpcClient above is the low-level surface. On the TypeScript side, sui-kit (which the TS SDK builds on) ships three companion classes. All of them are re-exported from the package root — @scallop-io/sui-kit only exports ., so always import from the root, not from internal subpaths.
import {
SuiKit,
SuiTxBlock,
SuiAccountManager,
MultiSigClient,
} from '@scallop-io/sui-kit';
MultiSigClient — Sui Multisig Helpers
Wraps publickey collection, threshold configuration and signature combination for multisig wallets. See sui-kit/src/libs/multiSig/client.ts for the implementation.
import { MultiSigClient } from '@scallop-io/sui-kit';
// Option 1: from already-decoded PublicKey instances
const multiSig = new MultiSigClient(
[
{ publicKey: pkA, weight: 1 },
{ publicKey: pkB, weight: 1 },
{ publicKey: pkC, weight: 1 },
],
2, // threshold
);
// Option 2: from raw base64-encoded ed25519 pubkeys
const multiSig2 = MultiSigClient.fromRawEd25519PublicKeys(
[pkA_base64, pkB_base64, pkC_base64],
[1, 1, 1],
2,
);
const address = multiSig.multiSigAddress(); // derived multisig address
const combined = multiSig.combinePartialSigs([sigA, sigB]); // ready to submit
Use this when:
- Treasury or operations wallets must co-sign Scallop transactions.
- A keeper bot's signing key needs an admin override path.
SuiAccountManager — Keypair Lifecycle
sui-kit/src/libs/suiAccountManager/ is a thin layer on top of mnemonic/keypair generation, derivation paths and signing. It's also the source the TS SDK uses internally — drop down to it when you need account features not exposed by the SDK (e.g. multiple derivation paths from one seed).
See crypto-keys for the Python equivalent.
SuiTxBlock — Transaction Composition
SuiTxBlock wraps @mysten/sui Transaction with ergonomic helpers (splitSUIFromGas, transferCoinToMany, mergeCoins, moveCall with named args). ScallopBuilder extends it — drop down to plain SuiTxBlock when you're composing a PTB that mixes Scallop calls with arbitrary moveCalls and want the same fluent API.
import { SuiKit, SuiTxBlock } from '@scallop-io/sui-kit';
const suiKit = new SuiKit({ mnemonics: '...' });
const tx = new SuiTxBlock();
tx.splitSUIFromGas([1_000_000_000]);
tx.moveCall(`${scallopPkg}::user::supply`, [/* args */], [coinType]);
// suiKit.currentAddress is a getter (property), not a function.
tx.transferObjects([/* ... */], suiKit.currentAddress);
await suiKit.signAndSendTxn(tx);
For Scallop-specific composition patterns see advanced-transactions.
References
- Sui Basics - Sui blockchain fundamentals
- Addresses - Protocol contract addresses
- Crypto Keys Skill - Python keypair lifecycle
- Advanced Transactions - PTB composition patterns