Post
Share your knowledge.
How will this solve my problem of reading all the keys of a nested table?
Anyway I can't get the table reference back because
Programmable transaction blocks (PTBs) do not currently allow the use of object references returned from one of its transaction command
``
How will this solve my problem of reading all the keys of a nested table?
- Sui
Answers
111. Reading All Keys of a Nested Table
In Sui Move, if you have a linked_table or similar table structure, you typically need a reference to the table object to iterate over its keys. The usual pattern is to call a function that takes a reference to the table and iterates through its keys (see the example below):
fun foo(table: &linked_table::LinkedTable<u64, u8>): u64 {
let mut res = 0;
let mut option_key = table.front();
while (option_key.is_some()) {
let key = *option_key.borrow();
res = res + key;
option_key = table.next(key);
};
res
}
Source: context #5
2. PTB Limitation: No Chaining of Returned Object References
You are correct: Programmable Transaction Blocks (PTBs) do not currently allow you to use object references returned from one command as input to another command in the same PTB. This means you cannot, for example, create a table in one command and then immediately use its reference in another command within the same PTB.
This limitation makes it impossible to:
- Dynamically create a table and then iterate over its keys in a single PTB.
- Chain operations that require a reference to an object just created or returned in the same transaction block.
3. How Does This Affect Reading All Keys?
- If you already have the table object reference (e.g., it’s stored in your account), you can call a function that iterates over its keys.
- If you need to create or fetch the table reference within the same PTB, you cannot use it in subsequent commands in that PTB due to the limitation you mentioned.
4. Workarounds
- Split into Multiple Transactions:
- First, create or fetch the table in one transaction.
- Then, in a separate transaction, use the reference to read or iterate over the keys.
- Store Table References:
- Persist table references in an account or another object so you can use them in future transactions.
5. Summary
- You cannot chain object references in a single PTB.
- To read all keys of a nested table, you must already have the table reference before the transaction starts.
- If you need to create or fetch the table, do it in a separate transaction.
Reading All Keys from Nested Tables in Sui PTBs
You're facing a fundamental limitation in Sui's Programmable Transaction Blocks (PTBs) - they don't allow using object references returned from previous commands. Here's how to work around this for reading nested table keys:
Current Limitation
PTBs indeed don't support using command outputs as inputs to subsequent commands. This prevents chaining operations like:
- Getting a table reference
- Using that reference to read keys
Workaround Solutions
1. Pre-fetch All Required References
// Get all needed references before building PTB
let table1_ref = get_table_reference();
let table2_ref = get_nested_table_reference();
// Build PTB with all known references
let ptb = ProgrammableTransactionBuilder::new();
ptb.command(Command::MoveCall {
arguments: vec![
Argument::Input(0), // table1_ref
Argument::Input(1), // table2_ref
],
// ... other params
});
2. Use Dynamic Field Lookups
// Get keys via dynamic field queries
let parent_object_id: ObjectID = /* your parent object */;
let dynamic_fields = sui_client
.dynamic_field_api()
.get_dynamic_fields(parent_object_id)
.await?;
3. Two-Phase Approach
Phase 1: Get all table references (client-side) Phase 2: Construct PTB with all known references
4. For Nested Tables
// If you know the nesting structure:
let ptb = ProgrammableTransactionBuilder::new();
// Add commands for each level
ptb.command(Command::MoveCall {
arguments: vec![
Argument::Input(0), // root table
Argument::Pure(bcs::to_bytes(&key1)?),
],
// ... get nested table ID
});
// Subsequent commands would need the nested table ID pre-known
Key Insight
You must:
- Know the structure/depth of nesting beforehand
- Pre-fetch all required references before PTB construction
- Use client-side logic to stitch together operations that can't be chained in PTBs
Future Solution
The Sui team is working on PTB improvements that may allow:
- Reference returns between commands
- More flexible command chaining
For now, you'll need to design your workflow around these constraints, either by pre-fetching data or restructuring your tables to require fewer nested lookups.
PTBs cannot return object references, so you cannot directly read all keys from a nested Table in a single transaction. Instead, maintain a separate vector or Table that stores the keys explicitly when inserting into the nested structure. This allows you to query the key list via an independent read or include it as a transaction output. Dynamic fields or custom indexing patterns are required to work around the limitation of Table enumeration in Sui Move.
PTBs can't return table refs, but you can use sui::table::borrow in a view function or a custom indexer to read all keys of a nested table off-chain. On-chain iteration isn't supported yet.
Currently, PTBs don’t support using object references returned mid-transaction, so you can’t dynamically fetch nested table keys in one go.
Workarounds:
- Pre-fetch keys – Read them beforehand if possible.
- Multi-step process – Split into separate transactions.
- Off-chain indexing – Use a custom indexer to track nested keys.
Future Fix? Sui may improve PTB flexibility later, but no ETA.
PTBs can't return object references mid-execution, but you can:
Workaround
- Pre-fetch all keys in a separate query before the PTB.
- Use dynamic fields (if possible) for nested data.
// Example: Fetch keys via `table::length` + loop (off-chain)
let len = table::length(&table);
let keys = vector[];
let i = 0;
while (i < len) {
keys.push_back(table::borrow(table, i));
i = i + 1;
}
Workaround for Nested Table Keys:
-
Pre-Fetch All References
Get table IDs before constructing the PTB:const parentTable = await client.getObject({ id: PARENT_TABLE_ID }); const nestedTableIds = parentTable.data?.content.fields.nested_tables; -
Use
InputInstead ofResult
Pass all required table IDs as PTB inputs:let txb = TransactionBuilder::new(); // Use pre-fetched IDs as Input arguments txb.move_call( package_id, module, function, type_args, [Input(nestedTable1), Input(nestedTable2)] ); -
Alternative: Client-Side Joins
// 1. Fetch all tables separately const tables = await Promise.all( tableIds.map(id => client.getDynamicFields({ parentId: id })) ); // 2. Merge keys client-side const allKeys = tables.flat();
Why This Works:
- Avoids PTB's reference-return limitation by treating tables as known inputs
- Uses Sui's parallel
multiGetAPIs for efficiency - Matches Sui's object-centric model (tables are separate objects)
You’ve hit a current limitation of Sui’s Programmable Transaction Blocks (PTBs): they don’t let you grab an object reference created by one command and then feed it directly into another command inside the same PTB. That’s why you can’t just “walk” through a nested table on-chain and pull out all its keys during a transaction.
Here’s how this usually plays out:
Why You Can’t Enumerate Keys in PTBs
- PTBs are deterministic and single-pass: once you create or return a reference, you can’t reuse it to run arbitrary iteration within the same transaction block.
- Move tables don’t expose key iteration natively: they’re optimized for lookups by known keys, not for on-chain enumeration. Reading “all keys” isn’t supported because it would break gas predictability and parallelism guarantees.
Common Workarounds
- Off-chain indexing: Use an indexer (like Sui Explorer APIs, custom indexers, or SDKs) to read and cache all keys off-chain. You then query off-chain and only pass back the keys you care about into PTBs.
- Application-level design: Instead of storing a large opaque nested table, store a parallel list (vector of keys, or an owned object with references to children) that you maintain as part of contract logic. That way you can enumerate keys by reading this index object.
- Event-driven tracking: Emit an event whenever you insert a new key. Your off-chain indexer or client listens to events and maintains the full list, which you can later query without on-chain iteration.
Do you know the answer?
Please log in and share it.
Sui is a Layer 1 protocol blockchain designed as the first internet-scale programmable blockchain platform.
- How to Maximize Profit Holding SUI: Sui Staking vs Liquid Staking616
- Why does BCS require exact field order for deserialization when Move structs have named fields?65
- Multiple Source Verification Errors" in Sui Move Module Publications - Automated Error Resolution55
- Sui Move Error - Unable to process transaction No valid gas coins found for the transaction419
- Sui Transaction Failing: Objects Reserved for Another Transaction410