Skip to the content.

Generic Type Confusion

Category: Abilities & Types · Covered by: Load, once the type says it; yours, to make the type say it · Requires: any Aptos Move version

What this prevents

A value that records a quantity without recording what the quantity is in. A receipt says a hundred is owed, a settlement function checks that a hundred is returned, and nothing in either establishes that the hundred coming back is the same asset as the hundred that went out.

The classic shape is a flash loan: borrow an expensive asset, repay the same count of a cheap one, and the accounting balances. It generalises to anything that hands out a claim, a handle, or an obligation and later redeems it.

Coverage

The verifier enforces type equality exactly, and that is the whole point: this failure is not a missing check, it is a type that was never asked to carry the distinction.

Once the asset is in the receipt’s type, Receipt<Gold> and Receipt<Dust> are different types and the mismatch is a compile error. Nothing can be done at run time to get past it, and no check needs writing.

Until then, the type system is doing its job correctly on a type that does not describe the thing it is standing for. That is a design decision, not an enforcement gap, which is why this belongs beside Ability Declarations as an Access Decision: both are cases where the platform is at full strength and the question it is answering is the wrong one.

What is left to you

Deciding what the type has to say.

For each value that stands for a claim on something, ask which asset, which account, which pool, which market. If the answer matters at redemption and is not in the type, it must be carried in a field and checked, and a field check is weaker: it runs at run time, it can be forgotten in one of several settlement paths, and it is only as good as the comparison written.

Reach for phantom when the parameter exists to distinguish types rather than to store anything. Receipt<phantom T> has no field of type T; the parameter is there so two receipts are not the same type. Without phantom the compiler requires T to appear in a field, which forces a meaningless field into the struct.

A single settlement function per asset type is a smell worth noticing. Two functions that differ only in which coin they accept usually means the receipt is not carrying what it should, and unifying them under one generic signature is the fix and the test at once.

Sample and test

See src/sources/GenericTypeConfusion.move, exercised by src/tests/GenericTypeConfusion_test.move. It carries both designs: a loose receipt with an amount and no asset, and the same receipt with the asset in its type.

Five cases. Two show the loose receipt behaving normally and still checking the amount. One deliberately passes: a hundred gold is borrowed, a hundred dust settles it, the amount matches, the receipt is consumed, and the borrower walks away holding the gold. That is the failure shown working. Two more show the tied receipt settling in its own asset and still checking amounts.

The defence is a compile-time failure and so cannot be asserted from a test. Attempting the same attack against the tied receipt fails to build:

error: cannot pass `generic_type_confusion::Receipt<generic_type_confusion::Gold>`
       to a function which expects argument of type
       `generic_type_confusion::Receipt<generic_type_confusion::Dust>`

Key excerpt:

/// Says what is owed and not what it is owed in.
struct LooseReceipt { amount: u64 }

/// The same, with the asset carried in the type. `phantom` because the
/// parameter appears in no field: it exists to make two receipts different
/// types, not to store anything.
struct Receipt<phantom T> { amount: u64 }

Review questions

Trade-offs

Historical note

Flash loans made this class visible, because they compress borrow and repay into one transaction where a mismatch is immediately profitable. The underlying mistake predates them and is not specific to lending: any protocol issuing a claim and redeeming it later can issue one that does not say what it is a claim on.

References