/ptb-composability
Trigger Pattern PTB flag (always for Sui -- Programmable Transaction Blocks are the Sui transaction model) - Inject Into Breadth agents, depth-external, depth-state-trace
$ npx -y skills add PlamenTSV/plamen --skill ptb-composability --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/ptb-composability
Context preview
The summary Claude sees to decide when to auto-load this skill.
Trigger Pattern PTB flag (always for Sui -- Programmable Transaction Blocks are the Sui transaction model) - Inject Into Breadth agents, depth-external, depth-state-trace
SKILL.md
ptb-composability.SKILL.mdname: "ptb-composability"
description: "Trigger Pattern PTB flag (always for Sui -- Programmable Transaction Blocks are the Sui transaction model) - Inject Into Breadth agents, depth-external, depth-state-trace"
Skill: PTB_COMPOSABILITY (Sui)
> **Trigger Pattern**: PTB flag (always for Sui -- Programmable Transaction Blocks are the Sui transaction model) > **Inject Into**: Breadth agents, depth-external, depth-state-trace > **Finding prefix**: `[PTB-N]` > **Rules referenced**: R4, R5, R8, R10, R15
Programmable Transaction Blocks (PTBs) are Sui's transaction composition primitive. A single PTB can contain up to 1024 commands, each calling a different function, with return values routed between commands. This enables atomic multi-step sequences that are fundamentally different from EVM's single-entry-point model. Every `public` function is a potential PTB command -- there is no "internal only" visibility equivalent to Solidity's `internal`.
**STEP PRIORITY**: Steps 1 (Single-Call Assumption Audit) and 4 (Atomic Read-Modify-Write) are where HIGH/CRITICAL severity findings most commonly hide. Do NOT rush these steps. If constrained, skip conditional sections (6, 7) before skipping 1, 3, or 4.
---
1. Entry Point Inventory
For EVERY `public` and `entry` function in the protocol, classify composability:
| # | Function | Module | Visibility | Returns Value? | Takes Shared Obj? | Composable in PTB? | Notes | |---|----------|--------|-----------|---------------|-------------------|-------------------|-------| | 1 | {func} | {mod} | public / entry / public(package) | YES / NO | YES / NO | YES / NO | {context} |
**Sui visibility semantics**:
- `entry` functions: callable from PTB but return values CANNOT be used by subsequent commands (consumed or discarded within the command). Objects can only be transferred, not returned.
- `public` functions: fully composable -- return values pass between commands. This is the primary composability surface.
- `public(package)`: callable only by other modules in the same package. NOT callable from PTB. Safe from external composition.
- `fun` (private): Not callable from outside the module. Safe.
**Key observation**: Any function that is `public` (not `entry`, not `public(package)`) is fully composable. Its return values can be routed to ANY other function in the same PTB.
1b. Single-Call Assumption Audit
For EVERY `public` and `entry` function, check whether its security model implicitly assumes it is the ONLY function called in the transaction:
| # | Function | Assumes Single-Call? | What Assumption? | Breakable via PTB? | Impact | |---|----------|---------------------|-----------------|-------------------|--------| | 1 | {func} | YES/NO | {describe assumption} | YES/NO | {impact} |
**Common single-call assumptions that PTBs break**:
- **Post-call state check**: Function reads state, performs action, then a SEPARATE function checks the result. Attacker inserts commands between action and check.
- **Balance snapshot**: Function reads a balance at start, assumes balance changed only due to its work. Another PTB command could have changed the balance.
- **One-operation-per-transaction**: Protocol assumes a user can only deposit OR withdraw in a single transaction. PTB allows deposit + borrow + withdraw atomically.
- **Temporal separation**: Protocol assumes time must pass between action A and action B (cooldown). If both functions are callable, PTB executes them in the same transaction with zero time delta.
- **Reentrancy-like patterns**: Function A updates state partially, function B reads partial state. PTBs naturally enable "call A then call B" -- state IS updated between commands.
**Key question for each function**: "If an attacker calls this function as command N in a PTB, and can execute arbitrary commands 1..N-1 before it and N+1..1024 after it, what can go wrong?"
---
2. Multi-Step Composition Analysis
For each `public` function that returns objects:
| # | Function | Input Objects | Output Objects | Can Output Be Routed To? | Can Be Called Multiple Times? | |---|----------|-------------|---------------|-------------------------|----------------------------| | 1 | {func} | {owned/shared/immutable} | {Coin<T> / Object / HotPotato} | {any function accepting this type} | YES/NO |
**Return value routing checks**:
- **Coin routing**: If function A returns `Coin<T>`, can it be routed to function C (external) instead of intended function B?
- **Coin splitting**: PTB has native `SplitCoins` command. Returned `Coin<T>` can be split. Does protocol assume full amount passed?
- **Coin merging**: PTB has native `MergeCoins` command. Can attacker merge extra coins to inflate amounts?
- **Mutable reference routing**: `&mut Object` references are borrowed for a command and released after. Same object can be passed to multiple commands sequentially. Does command N assume the object wasn't modified by command N-1?
- **Recipient mismatch**: If function returns a value-bearing object, the final `TransferObjects` command determines the recipient.
2b. Value Interception Pattern
Model this specific attack for each function returning value:
1. Attacker calls protocol_function_A() -> returns Coin<USDC> (intended for pool)
2. Attacker routes Coin<USDC> to their own address via TransferObjects
3. Protocol expects the Coin was consumed by the next step but it was intercepted
**Check**: Does the protocol rely on PTB command ordering to ensure returned values reach the right destination? If YES -> the user controls the ordering, not the protocol.
---
3. Flash Loan via PTB
Without explicit flash loan protocols, PTBs enable flash-loan-like patterns:
1. [Command 1] Split large Coin<SUI> from attacker's balance
2. [Command 2] Deposit into protocol (inflates TVL/balance)
3. [Command 3] Trigger reward distribution (calculated on inflated balance)
4. [Command 4] Withdraw from protocol
5. [Command 5] Join coins back (net zero capi
Read more
name: "ptb-composability" description: "Trigger Pattern PTB flag (always for Sui -- Programmable Transaction Blocks are the Sui transaction model) - Inject Into Breadth agents, depth-external, depth-state-trace"
Skill: PTB_COMPOSABILITY (Sui)
> **Trigger Pattern**: PTB flag (always for Sui -- Programmable Transaction Blocks are the Sui transaction model) > **Inject Into**: Breadth agents, depth-external, depth-state-trace > **Finding prefix**: `[PTB-N]` > **Rules referenced**: R4, R5, R8, R10, R15
Programmable Transaction Blocks (PTBs) are Sui's transaction composition primitive. A single PTB can contain up to 1024 commands, each calling a different function, with return values routed between commands. This enables atomic multi-step sequences that are fundamentally different from EVM's single-entry-point model. Every `public` function is a potential PTB command -- there is no "internal only" visibility equivalent to Solidity's `internal`.
**STEP PRIORITY**: Steps 1 (Single-Call Assumption Audit) and 4 (Atomic Read-Modify-Write) are where HIGH/CRITICAL severity findings most commonly hide. Do NOT rush these steps. If constrained, skip conditional sections (6, 7) before skipping 1, 3, or 4.
---
1. Entry Point Inventory
For EVERY `public` and `entry` function in the protocol, classify composability:
| # | Function | Module | Visibility | Returns Value? | Takes Shared Obj? | Composable in PTB? | Notes | |---|----------|--------|-----------|---------------|-------------------|-------------------|-------| | 1 | {func} | {mod} | public / entry / public(package) | YES / NO | YES / NO | YES / NO | {context} |
**Sui visibility semantics**:
- `entry` functions: callable from PTB but return values CANNOT be used by subsequent commands (consumed or discarded within the command). Objects can only be transferred, not returned.
- `public` functions: fully composable -- return values pass between commands. This is the primary composability surface.
- `public(package)`: callable only by other modules in the same package. NOT callable from PTB. Safe from external composition.
- `fun` (private): Not callable from outside the module. Safe.
**Key observation**: Any function that is `public` (not `entry`, not `public(package)`) is fully composable. Its return values can be routed to ANY other function in the same PTB.
1b. Single-Call Assumption Audit
For EVERY `public` and `entry` function, check whether its security model implicitly assumes it is the ONLY function called in the transaction:
| # | Function | Assumes Single-Call? | What Assumption? | Breakable via PTB? | Impact | |---|----------|---------------------|-----------------|-------------------|--------| | 1 | {func} | YES/NO | {describe assumption} | YES/NO | {impact} |
**Common single-call assumptions that PTBs break**:
- **Post-call state check**: Function reads state, performs action, then a SEPARATE function checks the result. Attacker inserts commands between action and check.
- **Balance snapshot**: Function reads a balance at start, assumes balance changed only due to its work. Another PTB command could have changed the balance.
- **One-operation-per-transaction**: Protocol assumes a user can only deposit OR withdraw in a single transaction. PTB allows deposit + borrow + withdraw atomically.
- **Temporal separation**: Protocol assumes time must pass between action A and action B (cooldown). If both functions are callable, PTB executes them in the same transaction with zero time delta.
- **Reentrancy-like patterns**: Function A updates state partially, function B reads partial state. PTBs naturally enable "call A then call B" -- state IS updated between commands.
**Key question for each function**: "If an attacker calls this function as command N in a PTB, and can execute arbitrary commands 1..N-1 before it and N+1..1024 after it, what can go wrong?"
---
2. Multi-Step Composition Analysis
For each `public` function that returns objects:
| # | Function | Input Objects | Output Objects | Can Output Be Routed To? | Can Be Called Multiple Times? | |---|----------|-------------|---------------|-------------------------|----------------------------| | 1 | {func} | {owned/shared/immutable} | {Coin<T> / Object / HotPotato} | {any function accepting this type} | YES/NO |
**Return value routing checks**:
- **Coin routing**: If function A returns `Coin<T>`, can it be routed to function C (external) instead of intended function B?
- **Coin splitting**: PTB has native `SplitCoins` command. Returned `Coin<T>` can be split. Does protocol assume full amount passed?
- **Coin merging**: PTB has native `MergeCoins` command. Can attacker merge extra coins to inflate amounts?
- **Mutable reference routing**: `&mut Object` references are borrowed for a command and released after. Same object can be passed to multiple commands sequentially. Does command N assume the object wasn't modified by command N-1?
- **Recipient mismatch**: If function returns a value-bearing object, the final `TransferObjects` command determines the recipient.
2b. Value Interception Pattern
Model this specific attack for each function returning value:
1. Attacker calls protocol_function_A() -> returns Coin<USDC> (intended for pool) 2. Attacker routes Coin<USDC> to their own address via TransferObjects 3. Protocol expects the Coin was consumed by the next step but it was intercepted
**Check**: Does the protocol rely on PTB command ordering to ensure returned values reach the right destination? If YES -> the user controls the ordering, not the protocol.
---
3. Flash Loan via PTB
Without explicit flash loan protocols, PTBs enable flash-loan-like patterns:
1. [Command 1] Split large Coin<SUI> from attacker's balance 2. [Command 2] Deposit into protocol (inflates TVL/balance) 3. [Command 3] Trigger reward distribution (calculated on inflated balance) 4. [Command 4] Withdraw from protocol 5. [Command 5] Join coins back (net zero capi
Autonomous Web3 security auditor for Claude Code and OpenAI Codex CLI. Orchestrates 18-100 AI agents across 40+ phases to produce audit reports with verified PoC exploits — for smart contracts and L1 node-client infrastructure.
Repo: PlamenTSV/plamen
Other skills on plamen.
- /ability-analysis
Trigger Pattern Always (Aptos Move) - foundational security check - Inject Into Breadth agents, depth agents
Open skill - /bit-shift-safety
Trigger Pattern Always (Aptos Move) - Move VM aborts on shift = bit width - Inject Into Breadth agents, depth-edge-case
Open skill - /centralization-risk
Trigger Protocol has privileged roles (admin, operator, governance, resource account owner) - Covers Single points of failure, privilege escalation, external governance dependen...
Open skill - /cross-chain-timing
Trigger Pattern wormhole|layerzero|ccip|bridge|cross_chain|vaa|guardian|emitter|relay|remote_chain|payload|nonce.sequence - Inject Into Breadth agents, depth-external
Open skill - /dependency-audit
Trigger EXTERNAL_LIB flag detected (protocol uses third-party Move dependencies) - Used by Breadth agents, depth-external
Open skill - /economic-design-audit
Trigger Pattern MONETARY_PARAMETER flag (required) - Inject Into Breadth agents (merged via M4 hierarchy)
Open skill

