For every generic function and parameterized type in the protocol:
STEP PRIORITY: Steps 4 (OTW Analysis) and 6 (Coin/Balance Type Safety) are where HIGH/CRITICAL severity findings most commonly hide. Do NOT rush these steps. If constrained, skip conditional sections before skipping 4 or 6.
1. Generic Function Inventory
Enumerate ALL functions with type parameters across all modules:
Module
Function
Type Params
Constraints
Public?
Phantom?
Notes
{mod}
{func}
<T>, <T: store>, etc.
{ability constraints}
YES/NO
YES/NO
{context}
Sui type parameter semantics:
<T> -- unconstrained. T can be ANY type. Maximally permissive.
<T: key + store> -- T must be an object that can be freely transferred.
<T: drop> -- T can be discarded. Often used for witness patterns.
<phantom T> -- T is not used at runtime, only for type distinction (e.g., Coin<phantom T>). No ability constraints enforced on phantom params at the struct level.
2. Type Parameter Constraint Analysis
For each generic function, verify constraints are sufficient:
2a. Under-Constrained Parameters
Function
Param
Constraint
Actually Used As
Sufficient?
{func}
T
none
stored in dynamic field (needs store)
NO
{func}
T
store
used as Coin<T> balance
NO -- needs further check
Check: For each type parameter, trace how it is actually used in the function body:
If stored in a struct field -> needs at minimum store
If used as an object -> needs key
If discarded without explicit destruction -> needs drop
If the function works correctly with ANY type -> unconstrained is correct
2b. Over-Constrained Parameters (Info-Level)
Function
Param
Constraint
Actually Needed
Over-Constrained?
{func}
T
key + store + drop
only store
YES -- limits usability
Note: Over-constraining is not a security issue but limits composability. Document as Informational.
2c. Phantom Type Correctness
Struct
Phantom Param
Used in Runtime Logic?
Type-Level Distinction Sound?
{struct}
phantom T
YES (BUG) / NO
YES/NO
Rule: Phantom type parameters MUST NOT be used in runtime field types (non-phantom positions). If a phantom param is used in a non-phantom position, the compiler rejects it. But check: is the phantom param providing meaningful type distinction, or can an attacker substitute any type?
2d. Mutable Reference Assignment Correctness
For every function that destructures a mutable reference or binds &mut field references, verify assignments update values rather than rebinding references:
Function
Source of &mut
Ref Variables
Assignment
Uses *lhs = *rhs?
Wrong Field Possible?
Checks:
If destructuring &mut Struct, treat destructured fields as references, not copied values.
Assignment like left = limit rebinds the local reference; it does not copy limit into the left field. Confirm the intended code uses *left = *limit or equivalent.
After any reference rebinding, trace subsequent *ref = ... writes. Do they now write to the original field or to another field?
Prioritize reset/quota/epoch/cooldown/accounting logic, where a wrong-field write can permanently corrupt limits or lock users out.
3. Type Witness Pattern Audit
Identify all witness patterns (structs used for one-time authorization):
Witness Type
Module
Created In
Consumed In
Abilities
Singleton?
{name}
{mod}
{function}
{function}
drop only / none
YES/NO
Witness security checks:
Is the witness created ONLY in the intended function? (Check all new / constructor paths.)
Is the witness consumed (dropped or destructured) immediately after use?
Can the witness be stored? (If it has store -> it can persist beyond its intended scope -> FINDING.)
Can the witness be copied? (If it has copy -> it can be reused -> FINDING.)
Is the witness type public? (If the struct is public, external modules can potentially construct it if they can satisfy its fields.)
3a. Witness Forgery Check
For each witness type:
Can an attacker construct this witness type?
1. Is the struct definition public (`public struct`)? -> External modules CAN create instances if fields are accessible
2. Does the struct have fields? -> If no fields (unit struct), only the defining module can create it
3. Are all field types accessible to external modules? -> If yes, external construction possible
4. Is construction gated by `init` or capability? -> Check the gate
Rule: A witness with a public struct definition and publicly-accessible field types is forgeable from external modules. This is a CRITICAL finding if the witness gates value creation (coin minting, capability issuance, etc.).
4. One-Time Witness (OTW) Analysis
Identify all OTW patterns:
Module
OTW Type
init Signature
OTW Consumed?
Package Upgrade Safe?
{mod}
{MODULE_NAME} (uppercase)
init(otw: MODULE_NAME, ctx: &mut TxContext)
YES/NO
YES/NO
Sui OTW rules:
OTW type name MUST match the module name in UPPERCASE.
OTW MUST have drop ability (and typically no other abilities).
OTW is automatically created by the Sui runtime and passed to init on module publish.
OTW MUST be consumed in init (typically passed to coin::create_currency or similar).
Security checks:
Does the OTW have ONLY drop ability? If it has copy -> can be duplicated (should be impossible due to Sui's OTW rules, but verify). If it has store -> can be persisted past init -> FINDING.
Is the OTW consumed (used as a move value, not just referenced) in init? If stored instead of consumed -> it can be reused.
Package upgrade: Sui package upgrades do NOT re-run init. Is the protocol relying on init for setup that should be repeatable? If the module uses OTW to create a TreasuryCap or Publisher, those are one-time-only.
Can the OTW check be bypassed? Functions that accept T: drop as a witness without verifying it is the actual OTW type -> can be called with any drop-able type.
4a. OTW Verification Pattern
Check if the protocol uses sui::types::is_one_time_witness<T>() to verify OTW:
Function
Accepts Generic Witness?
OTW Verification?
Bypass Possible?
{func}
<T: drop>(witness: T)
is_one_time_witness(&witness) / NO
YES/NO
Rule: Any public function that accepts a generic witness <T: drop> without calling is_one_time_witness can be called with any droppable type, not just the actual OTW. This is a HIGH finding if the function creates currencies, capabilities, or other privileged objects.
5. Generic Type Confusion Attacks
Model attacks where an attacker substitutes an unexpected type:
5a. Function-Level Type Confusion
For each public generic function:
Can an attacker call function<MaliciousType>() where the protocol expects function<ExpectedType>()?
1. What type does the protocol intend?
2. What constraints prevent substitution?
3. What happens if a different type is passed?
Function
Expected Type
Constraint
Substitute Possible?
Impact
{func}
SUI
none (just <T>)
YES -- any type
{impact}
{func}
USDC
<T: store>
YES -- any store type
{impact}
{func}
specific coin
runtime check on CoinMetadata
NO
N/A
5b. Struct-Level Type Confusion
For each generic struct:
Pool<T> { balance: Balance<T>, ... }
Can an attacker create Pool<FakeToken> and interact with Pool<RealToken>'s functions?
Check: Does the protocol use type parameters to distinguish pools/vaults? If yes:
Are operations on Pool<A> and Pool<B> fully isolated?
Can an attacker drain Pool<A> by exploiting Pool<FakeA>?
Is there a registry/mapping that validates the type parameter? (e.g., Table<TypeName, PoolConfig>)
5c. Generic Type to Config/Object Binding
Move generics prove that Pool<BTC> holds BTC, but they do NOT prove that a runtime selector, config row, position, or receipt supplied in the same call belongs to that generic type. For every public/package function accepting both generic type parameters and runtime identity inputs, build this table:
Function
Generic Type(s)
Runtime Selector / Config / Object
Binding Check
Missing Binding Impact
withdraw<T>
T
asset: u8, Pool<T>
type_name::get<T>() == config.coin_type?
wrong asset accounting / wrong pool withdrawal
liquidate<X,Y,SX>
SX
Position, SupplyPool<X,SX>
pool ID or debt-share type matches position?
free collateral / debt not repaid
Checks:
If a function accepts T plus an asset index/config ID, does it validate type_name::get<T>() against the stored config row?
If an index can be derived from T, does the function derive it instead of trusting a user-supplied index?
If a position/receipt/order was created with a specific pool, does the later function assert object::id(pool) == stored_pool_id or equivalent?
If there are multiple dependent type parameters (X, Y, SX, LP), does the function bind all of them to the stored position/config, not just the coin type?
If no binding exists, model attacker flow with a valid object of the wrong semantic pool/config and mark as finding if value, collateral, debt, or accounting can move under the wrong identity.
6. Coin/Balance Type Safety
Specific analysis for Coin<T> and Balance<T> patterns:
6a. Coin Type Verification
Function
Accepts Coin<T>
T Verified?
Verification Method
Bypass?
{func}
YES
YES/NO
{method or NONE}
YES/NO
Sui Coin safety model:
Coin<T> is parameterized by the coin type T.
Creating Coin<T> requires a TreasuryCap<T>, which is created via OTW in init.
An attacker CANNOT create Coin<SUI> because they don't have TreasuryCap<SUI>.
But an attacker CAN create Coin<ATTACKER_TOKEN> and pass it to a function expecting Coin<T> if T is generic.
Checks:
Do functions that handle coins use specific types (Coin<SUI>) or generic (Coin<T>)?
If generic: is T validated against an allowed set?
Can Balance<FakeToken> be joined with Balance<RealToken>? (NO -- type system prevents this. But verify no unsafe transmutation exists.)
Are Coin split/merge operations type-safe? (coin::split preserves T.)
6b. Balance Accounting Type Safety
Operation
Input Type
Output Type
Type Preserved?
Accounting Impact
deposit
Coin<T>
Balance<T> (internal)
YES/NO
{impact if mismatch}
withdraw
Balance<T> (internal)
Coin<T>
YES/NO
{impact if mismatch}
swap
Coin<A> -> Coin<B>
both types
YES/NO
{impact if mismatch}
Check: At every point where Balance<T> is converted to/from Coin<T>, is the type parameter T consistent? The compiler enforces this for concrete types, but for generic functions operating on Balance<T>, trace that T remains the same throughout the flow.
7. Publisher and Package Authority
Analyze Publisher object usage:
Module
Publisher Created?
Used For
Stored/Shared?
Transfer Restricted?
{mod}
YES/NO
{display, transfer policy, etc.}
{how stored}
YES/NO
Security checks:
Publisher proves package authorship. Functions that accept &Publisher trust the caller is the package publisher.
Is Publisher stored in a shared object (accessible to anyone with the right reference)?
Can Publisher be transferred to a malicious actor?
Are there functions that accept Publisher from external callers? (These trust the caller is a publisher.)
Package upgrade: After an upgrade, the original Publisher remains valid. Does the protocol account for this?
Finding Template
**ID**: [TS-N]
**Severity**: [CRITICAL if coin forgery/capability bypass, HIGH if type confusion with value, MEDIUM if witness issue]
**Step Execution**: check1,2,3,4,5,6,7 | X(reasons) | ?(uncertain)
**Rules Applied**: [R4:Y, R5:Y, R10:Y, ...]
**Depth Evidence**: [VARIATION:T=FakeToken vs T=SUI], [TRACE:generic_fn<Attacker>->state_corruption]
**Location**: module::function
**Title**: [Type safety issue] in [function] enables [attack/bypass]
**Description**: [Specific type parameter exploitation path with concrete substitute type]
**Impact**: [Unauthorized coin minting, capability forgery, pool drainage, accounting corruption]
Step Execution Checklist (MANDATORY)
CRITICAL: You MUST report completion status for ALL sections. Findings with incomplete sections will be flagged for depth review.
Section
Required
Completed?
Notes
1. Generic Function Inventory
YES
Y/X/?
All modules
2. Type Parameter Constraint Analysis
YES
Y/X/?
2c. Phantom Type Correctness
IF phantom params
Y/X(N/A)/?
2d. Mutable Reference Assignment Correctness
IF &mut destructuring/ref assignment
Y/X(N/A)/?
3. Type Witness Pattern Audit
IF witness patterns
Y/X(N/A)/?
3a. Witness Forgery Check
IF witness patterns
Y/X(N/A)/?
4. OTW Analysis
IF init with witness
Y/X(N/A)/?
HIGH PRIORITY
4a. OTW Verification Pattern
IF generic witness functions
Y/X(N/A)/?
5. Generic Type Confusion Attacks
YES
Y/X/?
5b. Struct-Level Type Confusion
IF generic structs
Y/X(N/A)/?
5c. Generic Type to Config/Object Binding
IF generic + runtime selector/object
Y/X(N/A)/?
6. Coin/Balance Type Safety
IF Coin/Balance used
Y/X(N/A)/?
HIGH PRIORITY
6b. Balance Accounting Type Safety
IF Balance used
Y/X(N/A)/?
7. Publisher and Package Authority
IF Publisher used
Y/X(N/A)/?
Cross-Reference Markers
After Section 3 (Witness Pattern Audit):
Cross-reference with ABILITY_ANALYSIS Section 4 (Capability Pattern Audit) -- witnesses often gate capabilities
IF witness is forgeable -> escalate to CRITICAL and cross-reference all functions that accept it
After Section 4 (OTW Analysis):
Cross-reference with ABILITY_ANALYSIS Section 8 (Module Initializer Audit) -- OTW is consumed in init
IF OTW not consumed -> check if TreasuryCap or Publisher can be created multiple times
After Section 6 (Coin/Balance Type Safety):
Cross-reference with TOKEN_FLOW_TRACING for multi-token accounting
IF generic coin functions lack type verification -> model type confusion attack with concrete substitute
After Section 7 (Publisher Authority):
IF Publisher stored in shared object -> cross-reference with SEMI_TRUSTED_ROLES (who can access it?)
IF package upgrade possible -> document Publisher persistence across upgrades