Make it safe · Advanced
Security pitfalls
Most NEAR contract exploits are not exotic. They come from a handful of patterns that behave differently from the EVM: cross-contract calls that are asynchronous and never roll back the caller, callbacks that anyone can call, access keys that can act without a wallet prompt, storage that the contract pays for, and gas limits that turn a growing loop into a dead feature.
Each lesson shows the bug as real near-sdk 5.x Rust, explains why NEAR’s execution model allows it, and gives the fixed version. Where there is an EVM analog — tx.origin, reentrancy, payable — it is shown side by side, so Solidity developers can map what they already know.
The module ends with a one-page review checklist and a sandbox test you can adapt to check failure paths before you deploy.
12 lessons~80 min36 quiz questionsFree · no sign-up
What you’ll learn
- Write cross-contract calls whose callbacks restore state when the call fails
- Close the double-spend window between a call and its callback
- Lock callbacks down with
#[private]and keep them panic-free - Authorize with the predecessor, not the signer, and require 1 yoctoNEAR for sensitive actions
- Audit access keys and decide who can upgrade a contract
- Handle deposits, refunds and failed transfers without stranding funds
- Defend against storage cost attacks and unbounded iteration
- Migrate state safely when the contract layout changes
Lessons
1. Async & callbacks
- Callbacks don’t roll back the callerNEAR smart contract security: why a failed cross-contract call does not roll back the caller’s state, and how to restore balances in a callback.
- Reentrancy across receiptsNEAR reentrancy explained: other transactions run between a cross-contract call and its callback. Avoid double spends with deduct-first and lock patterns.
- Private, panic-free callbacksNEAR callback security: mark callbacks #[private], read the promise result correctly, never panic in the undo path, and reserve enough gas for it.
2. Access & identity
- Predecessor vs signerNEAR access control: authorize with env::predecessor_account_id(), not signer_account_id() — the NEAR equivalent of the Solidity tx.origin phishing bug.
- Access keys as an attack surfaceNEAR access key security: full-access vs function-call keys, why contract accounts with full-access keys can be rewritten, and how to audit stale keys.
- The 1 yoctoNEAR guardWhy NEAR contracts require exactly 1 yoctoNEAR with assert_one_yocto(): it forces a full-access-key wallet signature for transfers and other sensitive calls.
3. Money
- Deposits, refunds and transfersHandle NEAR deposits safely: #[payable] only where needed, checked NearToken math, refunding overpayment, and transfers that fail for missing accounts.
- Storage staking attacksNEAR storage cost attacks: if anyone can make your contract store data, they lock your balance. Charge per byte, use NEP-145 deposits and cap input sizes.
4. State & limits
- Overflow, panics and require!Rust integer overflow in NEAR contracts: keep overflow-checks on in release, use checked math, and understand what a panic reverts (and what it doesn’t).
- Unbounded iteration bricks featuresLooping over a growing collection in a NEAR change method eventually exceeds the per-call gas limit. Use pagination, bounded batches and per-user state instead.