The Move security model, and where it stops
Move’s type system rules out whole classes of EVM bugs: resources cannot be copied or silently dropped. Capability leaks, generic confusion, arithmetic aborts and upgrade semantics remain. A review guide for Aptos and Sui.
- Written by
- Quantix Research, Smart Contract Security
- Published
- Reading time
- 6 minutes
- Category
- Smart Contract Vulnerabilities
Primer. Explains a known class of issue. Code is simplified and illustrative, not for production use.

Contents
Move was designed for digital assets. Its type system treats assets as resources that cannot be copied or silently discarded, its modules encapsulate the types they define, and its bytecode is verified before it runs. Several classes of EVM bug, such as accidentally duplicating a balance or losing one, are difficult or impossible to write.
That is a real advantage, and it is easy to overstate. Move programs on Aptos and Sui still contain authority bugs, type confusion, arithmetic errors and upgrade mistakes, and some risks are specific to Move’s object and upgrade models. This primer separates what the language guarantees from what the developer still has to get right.
What Move guarantees
Three properties do most of the work. Resource safety: a struct without the copy ability cannot be duplicated, and one without drop must be explicitly consumed, so a coin cannot be created from nothing or vanish by accident. Encapsulation: only the module that defines a struct can create it, read its fields directly or destroy it. Verification: the bytecode verifier checks type safety, resource safety and reference safety before a module is published, so malformed bytecode cannot bypass the rules.
Note what is not on that list. Move does not know which functions should be privileged, which types a generic function should accept, or whether arithmetic computes what the developer intended.
Abilities decide what a value can do
Every struct declares a subset of four abilities. copy allows duplication, drop allows a value to be discarded, store allows it to be stored inside other values and, on Sui, transferred by any module, and key allows it to exist as a top-level object or resource. Choosing abilities is a security decision: giving a capability store means it can be wrapped, sold or transferred by code outside the defining module, and giving an asset drop means it can be destroyed.
A struct with no abilities, often called a hot potato, cannot be stored, copied or dropped, so it must be consumed by a function of its defining module in the same transaction. Flash loans in Move commonly use this: the loan returns a receipt that can only be destroyed by repaying.
Capabilities: authority as a value
Move code usually expresses authority with capabilities: objects whose possession grants a right. A function that requires &AdminCap can only be called by someone who holds one. The model is strong as long as the capability cannot be obtained any other way.
// Simplified, illustrative Sui Move module. Not production code.module example::vault { use sui::balance::Balance; const EFeeTooHigh: u64 = 1; /// Authority to change parameters. `key` only: outside this module it can be /// held and passed by reference, but not wrapped or freely transferred. public struct AdminCap has key { id: UID } public struct Vault<phantom T> has key { id: UID, funds: Balance<T>, fee_bps: u64, } fun init(ctx: &mut TxContext) { transfer::transfer(AdminCap { id: object::new(ctx) }, ctx.sender()); } /// Privileged: callers must present the capability. public fun set_fee<T>(_: &AdminCap, vault: &mut Vault<T>, fee_bps: u64) { assert!(fee_bps <= 1_000, EFeeTooHigh); vault.fee_bps = fee_bps; } /// Leak: any package can call this and mint itself authority. public fun new_admin_cap(ctx: &mut TxContext): AdminCap { AdminCap { id: object::new(ctx) } }}The final function undoes the whole design: it is public, so any package can call it and receive a fresh capability. Leaks are rarely this blatant. More often a public function returns a capability as part of a larger struct, a capability with store ends up in a shared object anyone can borrow from, or, on Aptos, a module exposes a stored SignerCapability or an object’s ConstructorRef, either of which grants authority that should never leave the module. The Aptos Move security guidelines discuss these cases explicitly.
Generic type confusion
Generic functions let the caller choose a type argument. If a function accepts Coin<T> for any T but the pool it updates is meant for one specific coin, a caller can deposit a worthless coin type and be credited as if it were the real one. The fix is to tie the type parameter to the object, for example by making the pool itself generic as Pool<phantom T> so that only Coin<T> of the same T type-checks, or to verify the type explicitly where that is not possible.
Arithmetic: aborts, shifts and precision
Move’s arithmetic operators abort on overflow, underflow and division by zero, which removes the silent wraparound that older Solidity code suffered from. Two problems remain.
// Integer division truncates toward zero. Order of operations decides the result.let share = stake / total_stake * reward_pool; // 0 whenever stake < total_stakelet share = (stake as u128) * (reward_pool as u128) / (total_stake as u128); // multiply first, widen // Arithmetic operators abort on overflow; a left shift silently drops high bits.let x: u64 = 1 << 63;let y = x << 1; // 0, no abort: shift-based maths needs an explicit overflow checkInteger division truncates, so the order of operations and the width of intermediate values decide how much precision is lost, which matters for reward distribution and share accounting. The staking reward precision case study and the Tamarind Staking research assessment examine this class in a modelled Move protocol.
Left shifts do not abort when bits are shifted out, so fixed-point libraries implement their own overflow checks for shift-based maths, and those checks must be correct. In May 2025, the Sui exchange Cetus was exploited through such a check: according to the project’s account as reported by The Block, an overflow check in a shared math library validated against the wrong bound, which let the attacker add an enormous amount of liquidity for a tiny deposit.
Shared objects and signers
On Sui, an object can be owned by an address, shared, immutable or wrapped inside another object. Any transaction can include a shared object and pass it by mutable reference to any public function that accepts it. The type system guarantees that only the defining module’s functions can modify it, but nothing guarantees that those functions check who is calling: every public function taking &mut of a shared object must enforce its own authority, usually through a capability or the sender’s address.
On Aptos, a &signer represents authority over an account, and functions that receive one can move resources into it. Functions that accept a signer and act on it should be reachable only from the intended entry points; package-internal visibility exists for helpers that must not be called from outside.
Upgrades and old versions
Both platforms support package upgrades under compatibility rules that prevent changing existing public function signatures and struct layouts. On Sui, an upgrade is authorised by an UpgradeCap created at publication, which can be restricted or made immutable, and should be held as carefully as any admin key. Upgrading publishes a new package version while the previous version remains on-chain and callable. Protocols that fix a bug by upgrading must therefore also stop the old code from operating on their objects, typically by storing a version number in shared objects and checking it in every function.
On Aptos, a package’s upgrade policy is declared at publication as compatible or immutable, and the account that published it controls upgrades.
A review checklist for Move
| Check | Why it matters in Move |
|---|---|
| Abilities on every struct, especially capabilities and assets | store enables transfer and wrapping; drop allows destruction; copy on value is a duplication bug |
| Every function that creates or returns a capability | One public constructor defeats the capability model |
| Generic parameters on value-handling functions | Callers choose type arguments unless the object ties them down |
| Authority checks on functions taking shared objects or signers | Encapsulation does not restrict who calls public functions |
| Division order, widening and custom overflow checks | Truncation and unchecked shifts are not caught by the language |
| Upgrade capability custody and version checks | Old package versions remain callable after an upgrade |
Conclusions
Move eliminates by construction the bugs that come from duplicating or losing assets, and it makes authority explicit through capabilities and signers. The bugs that remain are about intent: which functions are public, what they return, which types they accept, how arithmetic is ordered, and what happens to old code after an upgrade.
Reviews of Move code should spend less time on the classes the language prevents and more on the places where the developer’s intent is expressed only by convention.
Recommendations
Developers
- Give capabilities the minimum abilities they need, usually
keyalone on Sui, and never expose a public constructor. - Tie generic type parameters to the objects they act on, using phantom type parameters where possible.
- Multiply before dividing, widen intermediates, and test custom overflow checks at their boundaries.
- Add version checks to shared objects before the first upgrade, not after the first bug.
Reviewers
- List every public function that takes a shared object or signer, and identify the authority check in each.
- Search for every expression that constructs a capability and confirm it is reachable only from initialisation or a privileged path.
Bibliography
Primary sources: standards, official documentation, incident reports and papers.
- 1Move security guidelines(opens in a new tab)Aptos documentation
- 2Object ownership(opens in a new tab)Sui documentation
- 3Upgrading packages(opens in a new tab)Sui documentation
- 4The Move Book(opens in a new tab)Move community
- 5Sui DEX Cetus says overlooked flaw in open-source library used by smart contract led to $223 million exploit(opens in a new tab)The Block, May 2025
Applied on this site
Assessments, case studies and tools that apply this publication.







