diff --git a/docs/smart-contracts/anatomy/yield-resume.md b/docs/smart-contracts/anatomy/yield-resume.md
index d3fc042801b..0eae6a5b504 100644
--- a/docs/smart-contracts/anatomy/yield-resume.md
+++ b/docs/smart-contracts/anatomy/yield-resume.md
@@ -207,7 +207,7 @@ It is best practice to check the validity of the response within the function wh
:::info
-Check more docs on [callback security](../security/callbacks.md#async-callbacks) and [reentrancy attacks](../security/reentrancy.md) to avoid common pitfalls when dealing with asynchronous calls
+Check more docs on [callback security](../security/callbacks.md) and [reentrancy attacks](../security/reentrancy.md) to avoid common pitfalls when dealing with asynchronous calls.
:::
diff --git a/docs/smart-contracts/security/callbacks.md b/docs/smart-contracts/security/callbacks.md
index 132ca42d285..e934f3ac1b8 100644
--- a/docs/smart-contracts/security/callbacks.md
+++ b/docs/smart-contracts/security/callbacks.md
@@ -4,47 +4,107 @@ title: Cross-Contract Calls
description: "Learn about callback security in NEAR smart contracts, including proper error handling, state management, and preventing callback-related vulnerabilities."
---
-In NEAR, smart contracts can call each other. This is a powerful feature that allows you to build complex applications by composing smaller contracts. However, it also introduces some security considerations that you need to be aware of.
+## Overview
-While writing cross-contract calls there is a significant aspect to keep in mind: all the calls are **independent** and **asynchronous**. In other words:
+NEAR Protocol smart contracts can call each other through cross-contract calls. This powerful feature enables building complex decentralized applications by composing smaller contracts together. However, cross-contract calls introduce critical security considerations that developers must understand and implement correctly.
-- The method in which you make the call and method for the callback are **independent**.
-- Between the call and the callback, people could interact with the contract.
+---
+
+## Fundamental Principle: Asynchronous and Independent Calls
+
+All cross-contract calls in NEAR are **independent** and **asynchronous**. This means:
-This has important implications on how you should handle the callbacks. Particularly:
+- The method that initiates the cross-contract call and the callback method that handles the response are **completely independent** execution contexts
+- Between the initial call and the callback execution, **anyone can interact with your contract** - other users can call any public method
+- The contract state can change between the call and callback, creating potential race conditions.
-1. Your callback method needs to be public, but you want to make sure only your contract can call it.
-2. Make sure you don't leave the contract in a exploitable state between the call and the callback.
-3. Manually rollback any changes to the state in the callback if the external call failed.
+**Security Implications:**
+
+1. **Callback Access Control**: Callback methods must be public to receive responses, but should only be callable by your contract itself
+2. **State Management**: Never leave the contract in an exploitable or inconsistent state between the call and callback
+3. **Error Handling**: Manually rollback any state changes in the callback if the external cross-contract call failed.
---
-## Private Callbacks
-In order for your contract to call itself when a cross-contract call is done, you need to make the callback method public. However, most of the times you would want it to be private. You can make it private while keeping it public by asserting that the `predecessor` is `current_account`. In rust this is done automatically by adding the `#[private]` decorator.
+## Private Callbacks: Securing Callback Methods
+
+**Problem**: When a cross-contract call completes, your contract needs to receive the callback. This requires the callback method to be public, but you typically want it to be private to prevent unauthorized access.
+
+**Solution**: Verify that the `predecessor` (the account that called the method) equals `current_account` (your contract's account). This ensures only your contract can invoke the callback.
+
+**Implementation in Rust**: Use the `#[private]` decorator macro, which automatically adds the predecessor check:
+
+```rust
+#[private]
+pub fn callback_method(&mut self) {
+ // Only your contract can call this
+}
+```
---
-## User's Money
-When a method panics, the money attached to that transaction returns to the `predecessor`. This means that, if you make a cross-contract call and it fails, then the money **returns to your contract**. If the money came from a user calling your contract, then you should transfer it back during the callback.
+## Handling User Funds in Callbacks
+
+**Critical Rule**: When a method panics or fails, any attached NEAR tokens automatically return to the `predecessor` (the account that initiated the transaction).
-
-*If the user attached money, we need to manually return it in the callback*
+**Scenario**:
+1. User calls your contract and attaches 10 NEAR
+2. Your contract makes a cross-contract call to another contract
+3. The external call fails or panics
+4. The 10 NEAR returns to **your contract** (not the original user).
-:::caution
-Make sure you pass have enough GAS in the callback to make the transfer
-:::
+**Security Requirement**: If the money originally came from a user calling your contract, you **must manually transfer it back** to the user in the callback handler.
+
+**Example Flow**:
+- User sends 10 NEAR → Your contract receives it
+- Your contract calls external contract (fails)
+- 10 NEAR returns to your contract automatically
+- **You must transfer 10 NEAR back to user in callback**.
+
+**Critical Warning**: Always ensure your callback has enough GAS allocated to perform the refund transfer. If the callback runs out of gas before completing the refund, the user's funds may be stuck.
---
-## Async Callbacks
-Between a cross-contract call and its callback **any method of your contract can be executed**. Not taking this into account is one of the main sources of exploits. It is so common that it has its own name: reentrancy attacks.
+## Async Callbacks and Reentrancy Attacks
+
+**Critical Vulnerability**: Between a cross-contract call and its callback, **any public method of your contract can be executed** by anyone. This creates a window for reentrancy attacks, which are one of the most common and dangerous security vulnerabilities in smart contracts.
+
+
+
+### Reentrancy Attack Example: deposit_and_stake
-Imagine that we develop a `deposit_and_stake` with the following **wrong logic**: (1) The user sends us money, (2) we add it to its balance, (3) we try to stake it in a validator, (4) if the staking fails, we remove the balance in the callback. Then, a user could schedule a call to withdraw between (2) and (4), and, if the staking failed, we would send money twice to the user.
+**Vulnerable Implementation (WRONG)**:
+1. User sends money to your contract
+2. Contract immediately adds money to user's balance
+3. Contract attempts to stake money in validator
+4. If staking fails, callback removes balance.
-
-*Between a cross-contract call and the callback anything could happen*
+**Attack Vector**:
+- Attacker calls `deposit_and_stake` with 10 NEAR
+- Contract adds 10 NEAR to attacker's balance (step 2)
+- Contract makes cross-contract call to validator (step 3)
+- **Before callback executes**, attacker calls `withdraw` method
+- Attacker withdraws 10 NEAR
+- If staking fails, callback removes balance, but attacker already withdrew
+- **Result**: Attacker receives money twice, contract loses funds.
+
+**Secure Implementation (CORRECT)**:
+1. User sends money to your contract
+2. **Do NOT add to balance yet** - store in temporary state
+3. Contract attempts to stake money in validator
+4. In callback: **only if staking succeeded**, then add money to user's balance
+5. If staking failed, return money to user.
+
+**Key Principle**: Delay state changes until the callback confirms the external operation succeeded. Never update balances or critical state before the cross-contract call completes.
+
+---
-Luckily for us the solution is rather simple. Instead of immediately adding the money to our user’s balance, we wait until the callback. There we check, and if the staking went well, then we add it to their balance.
+## Best Practices Summary
-
-*Correct way to handle deposits in a cross-contract call*
\ No newline at end of file
+1. **Use `#[private]` decorator** for all callback methods in Rust
+2. **Refund user funds** in callbacks if external calls fail
+3. **Allocate sufficient GAS** for callback operations, especially refunds
+4. **Delay state updates** until callback confirms success
+5. **Never update balances** before cross-contract call completion
+6. **Validate all inputs** in callback methods
+7. **Check external call results** before committing state changes.
diff --git a/docs/smart-contracts/security/checklist.md b/docs/smart-contracts/security/checklist.md
index 44994fafbb4..59e46596dd0 100644
--- a/docs/smart-contracts/security/checklist.md
+++ b/docs/smart-contracts/security/checklist.md
@@ -7,31 +7,327 @@ description: "Best practices for security and common safeguards."
Once you finished developing your smart contract please go through the following list in order to ensure everything is safe for the end user.
:::info
-Check our [security articles](./welcome.md) to understand how to improve the security of your contract.
+Check our [security articles](./introduction.md) to understand how to improve the security of your contract.
:::
---
-## Anatomy
-1. All private methods are decorated as `private`.
+## Overview
-## Environment
-2. `predecessor` and `signer` are used correctly through the entire contract.
+This comprehensive security checklist should be reviewed before deploying any NEAR smart contract to mainnet. Each item addresses a critical security concern that could lead to vulnerabilities, exploits, or loss of funds.
-## Storage
-3. Each time the state grows it is ensured that there is enough balance to cover it
-4. All collections (i.e. Vector, Map, Tree, etc) have a unique id
-5. Check for underflow and overflow!. In rust, you can do this by simply adding the `overflow-checks = true` flag in your `Cargo.toml`.
+---
+
+## Anatomy: Method Visibility and Access Control
+
+### 1. All Private Methods Are Decorated as `private`
+
+**Requirement**: Every method that should only be callable by the contract itself must be marked with the `#[private]` decorator in Rust.
+
+**Why it matters**:
+- Prevents unauthorized external calls
+- Ensures only your contract can invoke internal methods
+- Protects callback methods from being called directly.
+
+**How to verify**:
+```rust
+// ✅ CORRECT
+#[private]
+pub fn internal_method(&mut self) {
+ // Only contract can call this
+}
+
+// ❌ WRONG - Missing #[private]
+pub fn internal_method(&mut self) {
+ // Anyone can call this!
+}
+```
+
+---
+
+## Environment: Predecessor and Signer Usage
+
+### 2. `predecessor` and `signer` Are Used Correctly
+
+**Requirement**: Throughout your contract, ensure you're using the correct environment variables:
+- `predecessor` - The account that called the method (may be a contract)
+- `signer` - The account that signed the transaction (always a human/account)
+
+**Why it matters**:
+- Using wrong variable can allow unauthorized access
+- Critical for access control and authorization
+- Affects who can perform sensitive operations.
+
+**Common mistakes**:
+- Using `predecessor` when you need `signer` (for user verification)
+- Using `signer` when you need `predecessor` (for callback verification)
+- Not checking either when you should.
+
+**How to verify**:
+```rust
+// ✅ CORRECT - Check signer for user operations
+assert_eq!(env::signer_account_id(), user_id, "Unauthorized");
+
+// ✅ CORRECT - Check predecessor for callbacks
+assert_eq!(env::predecessor_account_id(), env::current_account_id(), "Only contract");
+```
+
+---
+
+## Storage: Cost Management and Data Structures
+
+### 3. State Growth Has Sufficient Balance Coverage
+
+**Requirement**: Every time your contract's state grows (stores new data), ensure there is enough contract balance to cover the storage cost. Storage costs are usually covered by the user who stores the data (e.g., when minting a new NFT.
+
+**Why it matters**:
+- Prevents storage drain attacks
+- Ensures contract can continue operating
+- Avoids transaction failures due to insufficient balance.
+
+**How to verify**:
+- Calculate storage cost for each state change
+- Check contract balance before storing data
+- Require users to attach deposit for their storage
+- Monitor contract balance regularly.
+
+
+
+### 4. All Collections Have Unique IDs
+
+**Requirement**: Every collection (Vector, Map, TreeMap, etc.) must have a unique identifier to prevent collisions and data corruption.
+
+**Why it matters**:
+- Prevents data from different collections mixing
+- Avoids state corruption
+- Ensures data integrity.
+
+**How to verify**:
+```rust
+// ✅ CORRECT - Unique collection IDs
+const USERS: StorageKey = StorageKey::new(b"users");
+const ORDERS: StorageKey = StorageKey::new(b"orders");
+
+// ❌ WRONG - Same ID for different collections
+const DATA1: StorageKey = StorageKey::new(b"data");
+const DATA2: StorageKey = StorageKey::new(b"data"); // Collision!
+```
+
+
+
+### 5. Check for Underflow and Overflow
+
+**Requirement**: Enable overflow checks in Rust to prevent integer underflow and overflow vulnerabilities.
+
+**Why it matters**:
+- Prevents arithmetic errors that can be exploited
+- Avoids unexpected behavior from integer wrapping
+- Critical for financial calculations.
+
+**How to verify**:
+Add to `Cargo.toml`:
+```toml
+[profile.release]
+overflow-checks = true
+```
+
+**Alternative**: Use checked arithmetic methods:
+```rust
+// ✅ CORRECT - Checked arithmetic
+let result = a.checked_add(b).expect("Overflow");
+
+// ❌ WRONG - Unchecked arithmetic
+let result = a + b; // Can overflow silently
+```
+
+---
+
+## Actions: Money Transfers and Fund Management
+
+### 6. Leave Enough Balance for Storage Costs
+
+**Requirement**: When sending money from your contract, always leave sufficient balance to cover ongoing storage costs.
+
+**Why it matters**:
+- Prevents contract from becoming unusable
+- Ensures contract can continue operating
+- Avoids storage-related transaction failures.
+
+**How to verify**:
+```rust
+// ✅ CORRECT - Reserve storage balance
+let storage_cost = self.calculate_storage_cost();
+let available = env::account_balance() - storage_cost;
+assert!(amount <= available, "Insufficient balance after storage reserve");
+Promise::new(receiver_id).transfer(amount);
+
+// ❌ WRONG - Send all balance
+Promise::new(receiver_id).transfer(env::account_balance()); // Leaves nothing!
+```
+
+
+
+### 7. Deduct User Funds Before Sending
+
+**Requirement**: If you're tracking user funds in your contract state, **always deduct them from the state before sending money back to the user**.
+
+**Why it matters**:
+- Prevents reentrancy attacks
+- Ensures state consistency
+- Follows checks-effects-interactions pattern.
+
+**How to verify**:
+```rust
+// ✅ CORRECT - Deduct first, then send
+let user_balance = self.get_balance(&user_id);
+self.set_balance(&user_id, 0); // Deduct first
+Promise::new(user_id).transfer(user_balance); // Then send
+
+// ❌ WRONG - Send first, then deduct (vulnerable to reentrancy)
+Promise::new(user_id).transfer(user_balance);
+self.set_balance(&user_id, 0); // Too late!
+```
+
+---
+
+## Callbacks: Cross-Contract Call Security
+
+### 8. All Private Callbacks Are Marked as `private`
+
+**Requirement**: Every callback method that should only be callable by your contract must use the `#[private]` decorator.
+
+**Why it matters**: Prevents external actors from calling your callbacks directly and bypassing security checks.
+
+```rust
+// ✅ CORRECT - Callback method can be called only by contract itself
+#[private]
+pub fn callback_after_stake(&mut self, #[callback_result] result: Result<(), PromiseError>) {
+ // Attacker can call this and manipulate state!
+ match result {
+ Ok(_) => {
+ self.balances
+ .insert(self.pending_user.clone(), self.pending_amount);
+ }
+ Err(_) => {
+ // Attacker can trigger this to rollback legitimate operations
+ }
+ }
+}
+
+// ❌ WRONG - Callback without #[private] - can be called directly
+pub fn callback_after_stake(&mut self, #[callback_result] result: Result<(), PromiseError>) {
+ // Attacker can call this and manipulate state!
+ match result {
+ Ok(_) => {
+ self.balances
+ .insert(self.pending_user.clone(), self.pending_amount);
+ }
+ Err(_) => {
+ // Attacker can trigger this to rollback legitimate operations
+ }
+ }
+}
+```
+
+
+
+
+### 9. All Cross-Contract Calls Have Callbacks
+
+**Requirement**: Every cross-contract call must have a corresponding callback to handle the response.
+
+**Why it matters**:
+- Allows error handling
+- Enables state rollback on failure
+- Ensures proper completion of async operations.
+
+
+
+### 10. Callbacks Check for Errors and Roll Back State
+
+**Requirement**: All callbacks must check if the external call succeeded or failed, and roll back any state changes if the call failed.
+
+**Why it matters**:
+- Prevents inconsistent state
+- Ensures atomicity of operations
+- Protects against partial failures.
+
+**How to verify**:
+```rust
+#[private]
+pub fn callback(&mut self, result: Result<(), String>) {
+ match result {
+ Ok(_) => {
+ // External call succeeded, commit state
+ self.commit_state();
+ }
+ Err(_) => {
+ // External call failed, rollback state
+ self.rollback_state();
+ }
+ }
+}
+```
+
+
+
+### 11. Callbacks Return Money to Predecessor If Necessary
+
+**Requirement**: If user funds were involved in a failed cross-contract call, the callback must return the money to the original user (predecessor).
+
+**Why it matters**:
+- Prevents user fund loss
+- Ensures proper refund handling
+- Maintains user trust.
+
+
+
+### 12. Callbacks Are Free of `panic!`
+
+**Requirement**: Callback methods should never use `panic!` or cause panics. Use proper error handling instead.
+
+**Why it matters**:
+- Panics can leave contract in inconsistent state
+- Prevents proper error recovery
+- May cause user fund loss.
+
+**How to verify**: Search codebase for `panic!` in callback methods and replace with proper error handling.
+
+
+
+### 13. Callbacks Have Sufficient GAS
+
+**Requirement**: All callbacks must be allocated enough GAS to execute completely, including any refunds or state updates.
+
+**Why it matters**:
+- Prevents partial execution
+- Ensures refunds can complete
+- Avoids stuck transactions.
+
+**How to verify**: Calculate GAS needs for callback operations and ensure sufficient allocation.
+
+
+
+### 14. Contract Not Left in Exploitable State Between Call and Callback
+
+**Requirement**: Between a cross-contract call and its callback, the contract must not be in a state that can be exploited by other transactions.
+
+**Why it matters**:
+- Prevents reentrancy attacks
+- Ensures state consistency
+- Protects against race conditions.
+
+**How to verify**: Review state changes - ensure critical updates happen in callbacks, not before external calls.
+
+---
+
+## Additional Security Considerations
-## Actions
-6. When sending money, you leave enough in the contract to cover the storage cost
-7. If you are tracking user's fund, you **deduct them before** sending them back to the user.
+Beyond this checklist, also consider:
-## Callbacks
-8. All private callbacks are marked as `private`
-9. All cross-contract calls have a callback
-10. All callbacks check for errors and roll back the state if necessary
-11. All callbacks return money to the `predecessor` if necessary
-12. Callbacks are free of `panic!`
-13. All the callbacks are given enough GAS to execute entirely
-14. The contract is not left in an exploitable state between a cross-contract call and its callback
+- **Input validation** - Validate all user inputs
+- **Access control** - Verify caller permissions
+- **Economic security** - Ensure economic incentives are correct
+- **Testing** - Comprehensive test coverage
+- **Code review** - External security review
+- **Monitoring** - Post-deployment monitoring
\ No newline at end of file
diff --git a/docs/smart-contracts/security/frontrunning.md b/docs/smart-contracts/security/frontrunning.md
index 7ca8f30b85c..5faadfe0def 100644
--- a/docs/smart-contracts/security/frontrunning.md
+++ b/docs/smart-contracts/security/frontrunning.md
@@ -4,6 +4,196 @@ title: Front Running
description: "Learn about frontrunning attacks in NEAR smart contracts and how to prevent them with proper transaction ordering and MEV protection techniques."
---
-In the NEAR network, validators have access to the transaction pool, and can therefore see them before they execute. This enables validators to analyze transactions for a potential profit and frontrun them with a transaction of their own.
+Frontrunning is a type of attack where validators (or other actors) see pending transactions before they execute and submit their own transactions to profit from the information. In NEAR Protocol, validators have access to the transaction pool, enabling them to frontrun user transactions.
-For example, imagine that you make a game where users are paid for solving puzzles. If not handled carefully, a validator could swap a transaction with the valid answer for one of its own and claim the prize. You can read more about this in [this blog post](https://www.paradigm.xyz/2020/08/ethereum-is-a-dark-forest).
+In the NEAR network:
+- **Validators have access to the transaction pool** - they can see all pending transactions
+- **Transactions are visible before execution** - validators can analyze them
+- **Validators control transaction ordering** - they decide which transactions to include and in what order
+- **Validators can insert their own transactions** - they can submit transactions before user transactions.
+
+---
+
+## The Attack Mechanism
+
+### Basic Frontrunning Process
+
+1. **User submits transaction** - e.g., solving a puzzle for a reward
+2. **Validator sees transaction** - transaction is in the pool, visible to validator
+3. **Validator analyzes** - validator identifies profitable opportunity
+4. **Validator submits own transaction** - validator creates transaction with winning answer
+5. **Validator includes their transaction first** - validator orders transactions to execute their own first
+6. **Validator claims reward** - validator's transaction executes first and claims the prize
+7. **User's transaction fails or gets nothing** - user's transaction executes but reward is already claimed.
+
+**Result**: Validator profits from information they shouldn't have access to, user loses.
+
+---
+
+## Example: Puzzle-Solving Game
+
+### Vulnerable Game Design
+
+Imagine you create a game where:
+- Users submit solutions to puzzles
+- First correct solution wins a prize
+- Solutions are submitted as transactions
+- Contract pays reward to first solver
+
+
+
+### The Attack
+
+**Scenario**:
+1. User solves puzzle and submits transaction with correct answer
+2. Validator sees the transaction in the pool
+3. Validator extracts the correct answer from user's transaction
+4. Validator creates their own transaction with the same answer
+5. Validator includes their transaction before user's transaction
+6. Validator's transaction executes first and claims the prize
+7. User's transaction executes but finds prize already claimed.
+
+```rust
+// ❌ VULNERABILITY: First-come-first-served - validator can frontrun
+// Validator sees user's solution in transaction pool and submits it first
+pub fn solve_puzzle(&mut self, puzzle_id: String, solution: String) {
+ // Validator can see this transaction in pool and extract solution
+ let correct_answer = self.get_puzzle_answer(&puzzle_id);
+
+ if solution == correct_answer {
+ // First solver gets reward - validator can frontrun!
+ if !self.solved_puzzles.contains_key(&puzzle_id) {
+ let solver = env::signer_account_id();
+ self.solved_puzzles.insert(puzzle_id, solver.clone());
+
+ let reward = NearToken::from_near(1);
+ let previous_solver_reward = self.rewards.get(&solver).unwrap_or(&NearToken::ZERO);
+ let solver_reward = previous_solver_reward.saturating_add(reward);
+ self.rewards.insert(solver, solver_reward);
+ }
+ }
+}
+```
+
+**Result**: Validator steals the reward that should have gone to the user.
+
+---
+
+## Types of Frontrunning Attacks
+
+### 1. Simple Frontrunning
+- Validator sees profitable transaction
+- Validator submits identical or similar transaction
+- Validator's transaction executes first
+- Validator profits, user loses.
+
+### 2. Sandwich Attacks
+- Validator sees large trade transaction
+- Validator places transaction before (frontrun) and after (backrun)
+- Validator profits from price impact
+- User gets worse price due to validator's transactions.
+
+### 3. Priority Gas Auctions
+- Multiple actors compete to frontrun
+- They bid higher gas fees for priority
+- Highest bidder gets frontrun position
+- Creates expensive competition.
+
+---
+
+## Real-World Impact
+
+Frontrunning attacks are particularly dangerous for:
+
+- **Reward mechanisms** - Games, competitions, bounties
+- **DEX trades** - Token swaps where price impact matters
+- **NFT minting** - First-come-first-served mints
+- **Auction systems** - Time-based or first-bid auctions
+- **Oracle updates** - Transactions that depend on external data
+
+---
+
+## Prevention Strategies
+
+### 1. Commit-Reveal Schemes
+
+**How it works**:
+1. Users commit to their answer (hash of answer + secret)
+2. After commit period, users reveal their answer and secret
+3. Contract verifies hash matches revealed answer
+4. Winner determined after all reveals.
+
+**Benefits**:
+- Validators cannot see the actual answer in the commit phase
+- Only after reveal can they see answers, but it's too late
+- Prevents frontrunning of answers.
+
+
+
+### 2. Time-Delayed Execution
+
+**How it works**:
+- Users submit transactions
+- Transactions are queued but not executed immediately
+- Execution happens after a delay (e.g., next block)
+- Validators cannot predict final state
+
+**Benefits**:
+- Reduces validator's information advantage
+- Makes frontrunning more difficult
+- Still vulnerable but harder to exploit
+
+
+
+### 3. Private Transaction Pools
+
+**How it works**:
+- Use private transaction submission
+- Transactions not visible in public pool
+- Only executed when included in block
+
+**Limitations**:
+- Requires infrastructure support
+- May not be available on all networks
+- Adds complexity
+
+
+
+### 4. Randomized Execution
+
+**How it works**:
+- Randomize which transactions execute
+- Don't use first-come-first-served
+- Use lottery or random selection
+
+**Benefits**:
+- Reduces advantage of frontrunning
+- Makes attacks less predictable
+- Fairer distribution
+
+
+
+### 5. Economic Disincentives
+
+**How it works**:
+- Require deposits for participation
+- Slash deposits for malicious behavior
+- Make frontrunning unprofitable
+
+---
+
+## Best Practices
+
+1. **Never use first-come-first-served** for valuable rewards
+2. **Use commit-reveal schemes** for games and competitions
+3. **Add time delays** to reduce information advantage
+4. **Randomize selection** when possible
+5. **Require deposits** to discourage malicious behavior
+6. **Test with frontrunning scenarios** before deployment
+7. **Document frontrunning risks** for users.
+
+## Additional Resources
+
+For more information on frontrunning and MEV (Maximal Extractable Value), see:
+- Paradigm's "Ethereum is a Dark Forest" blog post: https://www.paradigm.xyz/2020/08/ethereum-is-a-dark-forest
+- This explains the broader concept of frontrunning in blockchain systems.
diff --git a/docs/smart-contracts/security/welcome.md b/docs/smart-contracts/security/introduction.md
similarity index 98%
rename from docs/smart-contracts/security/welcome.md
rename to docs/smart-contracts/security/introduction.md
index d90162dbf65..a0bdf98a7d7 100644
--- a/docs/smart-contracts/security/welcome.md
+++ b/docs/smart-contracts/security/introduction.md
@@ -1,6 +1,6 @@
---
-id: welcome
-title: Security
+id: introduction
+title: Introduction
description: "Learn about smart contract security best practices on NEAR, including common vulnerabilities, attack vectors, and how to build secure decentralized applications."
---
diff --git a/docs/smart-contracts/security/one_yocto.md b/docs/smart-contracts/security/one_yocto.md
index 6e4c20b5388..fbd9ed12ae9 100644
--- a/docs/smart-contracts/security/one_yocto.md
+++ b/docs/smart-contracts/security/one_yocto.md
@@ -4,14 +4,185 @@ title: Ensure it is the User (1yⓃ)
description: "Learn about the one yocto security pattern in NEAR smart contracts for verifying account ownership and preventing unauthorized access."
---
-NEAR uses a system of [Access Keys](../../protocol/access-keys.md) to simplify handling accounts.There are basically two type of keys: `Full Access`, that have full control over an account (i.e. can perform all [actions](../anatomy/actions.md)), and`Function Call`, that only have permission to call a specified smart contract's method(s) that _do not_ attach Ⓝ as a deposit.
+In NEAR Protocol, verifying that a transaction actually comes from the user (not from a website with a Function Call key) is critical for security, especially when transferring valuable assets. Requiring 1 yoctoNEAR (the smallest unit of NEAR) is a simple and effective way to ensure user authorization.
-When a user [signs in on a website](../../web3-apps/tutorials/web-login/wallet-selector.md#user-sign-in--sign-out) to interact with your contract, what actually happens is
-that a `Function Call` key is created and stored in the website. Since the website has access to the `Function Call` key, it can use it to
-call the authorized methods as it pleases. While this is very user friendly for most cases, it is important to be careful in scenarios involving
-transferring of valuable assets like [NFTs](../../primitives/nft/nft.md) or [FTs](../../primitives/ft/ft.md). In such cases, you need to ensure that
-the person asking for the asset to be transfer is **actually the user**.
+## NEAR Access Key System
-One direct and inexpensive way to ensure that the user is the one calling is by requiring to attach `1 yⓃ`. In this case, the user will be
-redirected to the wallet and be asked to accept the transaction. This is because, once again, only the `Full Access` key can be used to send NEAR.
-Since the `Full Access` key is only in the user's wallet, you can trust that a transaction with `1 yⓃ` was made by the user.
\ No newline at end of file
+NEAR uses [an access key system](../../protocol/access-keys.md) to simplify account management. There are two main types of keys:
+
+### 1. Full Access Keys
+- **Full control** over an account
+- Can perform all [actions](../anatomy/actions.md) (transfers, deployments, etc.)
+- Can attach NEAR as deposit
+- **Stored only in user's wallet** - never shared with websites.
+
+### 2. Function Call Keys
+- **Limited permissions** - can only call specified smart contract methods
+- **Cannot attach NEAR** as deposit
+- Created when users sign in to websites
+- **Stored in the website** - website can use them automatically.
+
+---
+
+## The Security Problem
+
+### How Website Sign-In Works
+
+When a user [signs in to a website](../../web3-apps/tutorials/web-login/wallet-selector.md#user-sign-in--sign-out) to interact with your contract:
+
+1. A `Function Call` key is created
+2. The key is stored in the website (browser storage)
+3. The website can use this key to call authorized methods **automatically**
+4. No user interaction required for each transaction.
+
+
+
+### The Risk
+
+For most operations, this is convenient and secure. However, for **valuable asset transfers** (NFTs, Fungible Tokens, large amounts of NEAR), this creates a security risk:
+
+- **Website has the key** - can initiate transfers without user confirmation
+- **No user awareness** - user might not know a transfer is happening
+- **Malicious websites** - compromised or malicious sites could drain assets
+- **User can't verify** - no way to ensure the user actually authorized the transfer.
+
+**Critical Scenarios**:
+- Transferring NFTs
+- Transferring Fungible Tokens (FTs)
+- Large NEAR transfers
+- Any operation involving valuable assets
+
+---
+
+## Solution: Require 1 YoctoNEAR
+
+### How It Works
+
+Require users to attach **1 yoctoNEAR** (1 yⓃ) to sensitive operations:
+
+```rust
+pub fn transfer_nft(&mut self, token_id: String, receiver_id: AccountId) {
+ // Require 1 yoctoNEAR to ensure user authorization
+ require!(
+ env::attached_deposit() == NearToken::from_yoctonear(1),
+ "Requires attached deposit of exactly 1 yoctoNEAR"
+ )
+
+ // Proceed with transfer
+}
+```
+
+:::tip
+[`near_sdk`](../../tools/sdk.md) provides a helper method to assert one yoctoNEAR deposit:
+
+```rust
+use near_sdk::{assert_one_yocto};
+
+...
+
+pub fn transfer_nft(&mut self, token_id: String, receiver_id: AccountId) {
+ // Require 1 yoctoNEAR to ensure user authorization
+ assert_one_yocto();
+
+ // Proceed with transfer
+}
+
+...
+```
+:::
+
+
+
+### Why This Works
+
+1. **Function Call keys cannot attach NEAR** - they can only call methods without deposits
+2. **Only Full Access keys can attach NEAR** - these are stored in the user's wallet
+3. **Wallet requires user confirmation** - when NEAR is attached, wallet prompts user
+4. **User must approve** - transaction cannot proceed without explicit user approval.
+
+**Result**: If a transaction includes 1 yoctoNEAR, you can trust it was explicitly authorized by the user through their wallet.
+
+---
+
+## Implementation Example
+
+### NFT Transfer with Verification
+
+```rust
+pub fn transfer_nft(&mut self, token_id: String, receiver_id: AccountId) {
+ // Verify user authorization
+ // Require 1 yoctoNEAR to ensure user authorization
+ require!(
+ env::attached_deposit() == NearToken::from_yoctonear(1),
+ "Requires attached deposit of exactly 1 yoctoNEAR"
+ )
+
+ // Verify caller owns the NFT
+ let owner = self.get_token_owner(&token_id);
+ assert_eq!(env::predecessor_account_id(), owner, "Not the owner");
+
+ // Perform transfer
+ self.transfer_token(token_id, receiver_id);
+}
+```
+
+
+
+### Fungible Token Transfer
+
+```rust
+pub fn transfer_ft(&mut self, amount: U128, receiver_id: AccountId) {
+ // Require 1 yoctoNEAR to ensure user authorization
+ require!(
+ env::attached_deposit() == NearToken::from_yoctonear(1),
+ "Requires attached deposit of exactly 1 yoctoNEAR"
+ )
+
+ // Transfer tokens
+ self.internal_transfer(env::predecessor_account_id(), receiver_id, amount.0);
+}
+```
+
+---
+
+## When to Use This Pattern
+
+### Use 1 YoctoNEAR Requirement For:
+
+- ✅ NFT transfers
+- ✅ Fungible Token transfers
+- ✅ Large NEAR transfers
+- ✅ Account ownership changes
+- ✅ Permission modifications
+- ✅ Any operation involving valuable assets
+
+### Not Necessary For:
+
+- ❌ Read-only operations (view methods)
+- ❌ Low-value operations
+- ❌ Operations that don't transfer assets
+- ❌ Public data queries
+
+## Best Practices
+
+1. **Always require 1 yoctoNEAR** for asset transfers
+2. **Document the requirement** - tell users why it's needed
+3. **Return the yoctoNEAR** if you don't need it (optional, but user-friendly)
+4. **Use consistent pattern** - apply to all sensitive operations
+5. **Test with Function Call keys** - ensure they fail without deposit
+
+## Alternative: Return the YoctoNEAR
+
+You can return the 1 yoctoNEAR to the user after verification:
+
+```rust
+pub fn transfer_nft(&mut self, token_id: String, receiver_id: AccountId) {
+ assert!(env::attached_deposit() >= 1, "Verification required");
+
+ // Perform transfer
+ self.transfer_token(token_id, receiver_id);
+
+ // Return the yoctoNEAR (optional)
+ Promise::new(env::predecessor_account_id()).transfer(1);
+}
+```
\ No newline at end of file
diff --git a/docs/smart-contracts/security/random.md b/docs/smart-contracts/security/random.md
index 0ebf24b1317..d82c9afc1be 100644
--- a/docs/smart-contracts/security/random.md
+++ b/docs/smart-contracts/security/random.md
@@ -4,66 +4,184 @@ title: Random Numbers
description: "Learn about secure random number generation in NEAR smart contracts and how to avoid predictable randomness vulnerabilities."
---
-When writing smart contracts in NEAR you have access to a `random seed` that enables you to create random numbers/strings
-within your contract.
+Generating secure random numbers in blockchain environments is challenging because blockchains are deterministic. NEAR Protocol provides a `random seed` mechanism, but understanding its properties and limitations is crucial for building secure applications that rely on randomness.
-This `random seed` is **deterministic and verifiable**: it comes from the validator that produced the block signing the previous
-block-hash with their private key.
+---
+
+## How NEAR Random Seed Works
+
+NEAR provides a `random seed` that enables smart contracts to create random numbers and strings. This seed has unique properties:
+
+### Deterministic and Verifiable
+
+The random seed is **deterministic and verifiable**:
+- It comes from the validator that produced the block
+- The validator signs the previous block-hash with their private key
+- The signature becomes the random seed for the current block
+- Anyone can verify the seed, but only the validator knows it in advance.
+
+---
+
+## Security Properties
-The way the random seed is created implies two things:
+The way the random seed is created provides two important security properties:
-- Only the validator mining the transaction **can predict** which random number will come out. **No one else** could predict it because nobody knows the validator's private key (except the validator itself).
+### 1. Only Validator Can Predict
+- **Only the validator mining the transaction can predict** which random number will be generated
+- **No one else can predict it** because nobody knows the validator's private key (except the validator)
+- This provides some security, but creates a centralization risk.
-- The validator **cannot interfere** with the random number being created. This is because they need to sign the previous block, over which (with a high probability) they had no control.
+### 2. Validator Cannot Interfere
+- The validator **cannot interfere** with the random number being created
+- They must sign the previous block-hash, over which they (with high probability) had no control
+- The previous block was likely produced by a different validator.
+**Important**: While validators cannot directly manipulate the seed, they can still exploit it through other means.
-However, notice that this still leaves room for three types of attacks from the validator:
-1. [Frontrunning](./frontrunning.md), which we cover in another page
-2. Gaming the input
-3. Refusing to mine the block.
+---
+
+## Attack Type 1: Gaming the Input
+
+### The Vulnerability
+
+If your contract asks users to provide an input and rewards them if it matches the random seed, validators can exploit this:
-----
+**Example**:
+- Contract asks user to choose a number between 1-100
+- If user's number matches the random seed, they win a prize
+- Validator knows the random seed before the block is mined
+- Validator creates a transaction with the winning number
+- Validator includes their transaction in the block
+- Validator wins the prize
-## Gaming the Input
-Imagine you have a method that takes an input and gives a reward based on it. For example, you ask the user to choose a number,
-and if it the same as your `random seed` you give them money.
+```rust
+// ❌ VULNERABILITY: Gaming the input - validator can predict and win
+// User provides input and random seed is generated in same block
+pub fn guess_number(&mut self, guess: u8) {
+ let account_id = env::signer_account_id();
-Since the validator knows which `random seed` will come out, it can create a transaction with that specific input and win the prize.
+ // Store user's guess
+ self.bets.insert(account_id.clone(), guess.to_string());
-----
+ // Generate random number in SAME block - validator knows it!
+ let random_seed = env::random_seed();
+ let random_number = (random_seed[0] % 100) as u8;
-## Refusing to Mine the Block
-One way to fix the "gaming the input" problem is to force the user to send the input first, and then decide the result on a different block.
-Let's call these two stages: "bet" and "resolve".
+ // Validator can see user's guess and create winning transaction
+ if guess == random_number {
+ let reward = NearToken::from_near(1);
+ let previous_user_reward = self.rewards.get(&account_id).unwrap_or(&NearToken::ZERO);
+ let user_reward = previous_user_reward.saturating_add(reward);
+ self.rewards.insert(account_id, user_reward);
+ }
+}
+```
-In this way, a validator cannot game the input, since the `random` number against which it will be compared is computed in a different block.
+### Why This Works
+
+Since the validator knows which `random seed` will be generated in their block, they can:
+1. Calculate the winning input
+2. Create a transaction with that input
+3. Include their transaction in the block
+4. Win every time
+
+**Result**: Validators can guarantee wins in single-block games.
+
+---
-However, something that the validator can still do to increase their chance of winning is:
-1. Create a "bet" transaction with an account.
-2. When it's their turn to validate, decide if they want to "resolve" or not.
+## Attack Type 2: Refusing to Mine the Block
-If the validator, on their turn, sees that generating a random number makes them win, they can add the transaction to the block. And if they
-see that they will not, they can skip the transaction.
+### The Two-Stage Solution
-While this does not ensure that the validator will win (other good validators could mine the transaction), it can improve their chance of winning.
+One way to fix "gaming the input" is to use a two-stage process:
-Imagine a flip-coin game, where you choose `heads` or `tails` in the "bet" stage, and later resolve if you won or not. If you are a validator
-you can send a first transaction choosing either input.
+1. **Bet Stage**: User sends their input/choice (e.g., "heads" or "tails")
+2. **Resolve Stage**: Contract generates random number and determines winner (in a later block)
-Then, on your turn to validate, you can check if your chosen input came out. If not, you can simply skip the transaction. This brings your
-probability of winning from `1/2` to `3/4`, that's a 25% increase!
+This prevents validators from gaming the input because:
+- User's choice is locked in the first block
+- Random seed is generated in a different (later) block
+- Validator cannot know the random seed when user makes their choice
-These odds, of course, dilute in games with more possible outcomes.
+### The Remaining Vulnerability
-
-How does the math work here?
+However, validators can still exploit this through selective block mining:
-Imagine you always bet for `heads`.
+**Attack Process**:
+1. Validator creates a "bet" transaction with their account
+2. Validator chooses either input (doesn't matter which)
+3. When it's the validator's turn to validate:
+ - They check what random seed will be generated
+ - If their bet would win, they include the "resolve" transaction in the block
+ - If their bet would lose, they skip the "resolve" transaction
+4. Other validators might mine it, but validator increases their win rate
-In a fair coin-flip game you have 50-50 percent chance of winning, this is because after the coin is flipped there are two possible outcomes:
-`H` and `T`, and you only win in one (`H`).
+**Result**: Validator improves their probability of winning, though doesn't guarantee it.
-However, if you can choose to flip again if `tails` comes out, now there are 4 scenarios: `H H` `T H` `H T` `T T`, and in 3 of those
-you win (all the ones including an `H`)!!!.
+---
+
+## Coin Flip Example: Probability Manipulation
+
+### Fair Coin Flip (Without Attack)
+
+In a fair coin-flip game:
+- You choose "heads" or "tails" in the bet stage
+- Random seed determines outcome in resolve stage
+- **Probability of winning: 50%** (1/2)
+
+### With Refusing to Mine Attack
+
+If you're a validator and can refuse to mine losing blocks:
+
+**Scenarios**:
+- You bet "heads"
+- If random seed = "heads": You mine the block and win ✅
+- If random seed = "tails": You skip the block (other validator might mine it)
+ - If other validator mines: You lose ❌
+ - If no one mines: Transaction delayed, you try again later
+
+**Mathematical Analysis**:
+
+If you always bet "heads" and can choose to flip again when "tails" comes out:
+
+**Possible outcomes** (H = heads, T = tails):
+- H H: Win ✅
+- T H: Win ✅ (retry after T)
+- H T: Win ✅ (retry after H)
+- T T: Lose ❌ (retry after T, then T again)
+
+**Result**: You win in 3 out of 4 scenarios = **75% win rate** (3/4)
+
+**Improvement**: From 50% to 75% = **25% increase in win probability**
+
+**Note**: These odds dilute in games with more possible outcomes (e.g., dice with 6 sides).
+
+---
-
+## Best Practices for Randomness
+
+### 1. Use Multi-Block Randomness
+- Separate bet and resolve into different blocks
+- Prevents input gaming attacks
+- Still vulnerable to selective mining, but reduces risk.
+
+### 2. Use Commit-Reveal Schemes
+- Users commit to their choice (hash of choice + secret)
+- Later reveal the choice and secret
+- Random seed generated after reveal
+- Prevents both input gaming and selective mining.
+
+### 3. Use External Oracles
+- Use trusted external randomness sources
+- Chainlink VRF or similar services
+- More secure but requires external dependencies.
+
+### 4. Accept Validator Advantage
+- For non-critical randomness, accept that validators have slight advantage
+- Use for games where small advantage is acceptable
+- Not suitable for high-stakes applications.
+
+### 5. Use Multiple Validators
+- Distribute randomness across multiple validators
+- Reduces single validator control
+- More decentralized approach.
diff --git a/docs/smart-contracts/security/reentrancy.md b/docs/smart-contracts/security/reentrancy.md
index 9d0dcbc262b..52e43b1e9b4 100644
--- a/docs/smart-contracts/security/reentrancy.md
+++ b/docs/smart-contracts/security/reentrancy.md
@@ -4,22 +4,126 @@ title: Reentrancy Attacks
description: "Learn about reentrancy attacks in NEAR smart contracts and how to prevent them with proper security measures and coding practices."
---
-Between a cross-contract call and its callback **any method of your contract can be executed**. Not taking this into account is one of the main sources of exploits. It is so common that it has its own name: **reentrancy attacks**.
+Reentrancy attacks are one of the most common and dangerous security vulnerabilities in smart contracts. In NEAR Protocol, the asynchronous nature of cross-contract calls creates a window of opportunity for attackers to exploit state inconsistencies.
-Always make sure to keep your state in a consistent state after a method finishes executing. Assume that:
-- Any method could be executed between a method execution and its callback.
-- The same method could be executed again before the callback kicks in.
+Between a cross-contract call and its callback, **any public method of your contract can be executed** by anyone. This fundamental property of NEAR's asynchronous execution model means that:
+
+- **Any method** could be executed between a method execution and its callback
+- **The same method** could be executed multiple times before the callback completes
+- **State changes** made before the callback can be exploited by malicious actors
+
+**Critical Rule**: Always ensure your contract state remains consistent and secure after each method finishes executing, even if a callback is pending.
+
+---
+
+## Fundamental Assumptions
+
+When designing secure smart contracts, you must assume:
+
+1. **Any method can execute** between your method and its callback
+2. **The same method can be re-entered** multiple times before the callback executes
+3. **Attackers will exploit** any state inconsistencies they can find
+4. **State changes are visible** immediately after a method completes, even before callbacks.
+
+---
+
+## Reentrancy Attack Example: deposit_and_stake
+
+### Vulnerable Implementation (WRONG)
+
+Consider a `deposit_and_stake` function with the following flawed logic:
+
+1. User sends money to the contract
+2. Contract **immediately adds money to user's balance** (state change)
+3. Contract makes cross-contract call to stake money in validator
+4. If staking fails, callback removes the balance.
+
+**Attack Scenario**:
+- Attacker calls `deposit_and_stake` with 10 NEAR
+- Contract adds 10 NEAR to attacker's balance (step 2 completes)
+- Contract initiates cross-contract call to validator (step 3)
+- **Before callback executes**, attacker calls `withdraw()` method
+- Attacker successfully withdraws 10 NEAR (balance was already updated)
+- If staking fails, callback removes balance, but attacker already withdrew
+- **Result**: Attacker receives 10 NEAR, contract loses funds.
+
+```rust
+// ❌ VULNERABILITY: Reentrancy attack - state updated before external call
+pub fn deposit_and_stake(&mut self) {
+ let amount = env::attached_deposit();
+ let account_id = env::signer_account_id();
+
+ // VULNERABILITY: Updates balance BEFORE external call completes
+ let balance = self
+ .balances
+ .get(&account_id)
+ .unwrap_or(&NearToken::ZERO)
+ .saturating_add(amount);
+ self.balances.insert(account_id.clone(), balance);
+
+ let _ = Promise::new("validator.near".parse().unwrap())
+ .function_call(
+ "deposit_and_stake".to_string(),
+ NO_ARGS,
+ amount,
+ Gas::from_tgas(10),
+ )
+ .then(
+ Self::ext(env::current_account_id())
+ .with_static_gas(XCC_GAS)
+ .callback_after_stake(),
+ );
+}
+```
+
+**Why This Happens**: The state change (adding to balance) happens before the external call completes. The attacker exploits the time window between the state update and the callback.
+
+
+
+### Secure Implementation (CORRECT)
+
+The solution is to delay state changes until the callback confirms success:
+
+1. User sends money to the contract
+2. **Do NOT add to balance yet** - store in temporary/pending state
+3. Contract makes cross-contract call to stake money in validator
+4. **In callback**: Only if staking succeeded, then add money to user's balance
+5. If staking failed, return money to user (no balance update needed).
+
+**Key Principle**: Never update critical state (like balances) before external operations complete. Always wait for callback confirmation before committing state changes.
---
-### Example
-Imagine that we develop a `deposit_and_stake` with the following **wrong logic**: (1) The user sends us money, (2) we add it to its balance, (3) we try to stake it in a validator, (4) if the staking fails, we remove the balance in the callback. Then, a user could schedule a call to withdraw between (2) and (4), and, if the staking failed, we would send money twice to the user.
+## Prevention Strategies
+
+### 1. Delay State Updates
+- Never update balances or critical state before cross-contract calls
+- Store pending operations in temporary state
+- Only commit state changes in callbacks after confirming success.
+
+### 2. Use Checks-Effects-Interactions Pattern
+- **Checks**: Validate all inputs and preconditions
+- **Effects**: Update state (but only after external calls complete)
+- **Interactions**: Make external calls last.
-
-*Between a cross-contract call and the callback anything could happen*
+### 3. Implement Reentrancy Guards
+- Use flags to prevent re-entry during critical operations
+- Mark methods as "in progress" during execution
+- Clear flags only after all operations complete.
+
+### 4. Validate in Callbacks
+- Always check the result of external calls in callbacks
+- Rollback any state changes if external operations failed
+- Never assume external calls will succeed.
+
+---
-Luckily for us the solution is rather simple. Instead of immediately adding the money to our user’s balance, we wait until the callback. There we check, and if the staking went well, then we add it to their balance.
+## Best Practices
-
-*Correct way to handle deposits in a cross-contract call*
\ No newline at end of file
+1. **Assume reentrancy is possible** - design your contract defensively
+2. **Minimize state changes** before external calls
+3. **Validate everything** in callbacks before committing state
+4. **Test with attack scenarios** - simulate reentrancy attempts
+5. **Review callback logic** carefully - this is where vulnerabilities hide
+6. **Keep state consistent** at all times, even during async operations
\ No newline at end of file
diff --git a/docs/smart-contracts/security/storage.md b/docs/smart-contracts/security/storage.md
index 3b5824b476e..ac75391f1d0 100644
--- a/docs/smart-contracts/security/storage.md
+++ b/docs/smart-contracts/security/storage.md
@@ -1,21 +1,145 @@
---
id: storage
-title: Million Small Deposits
+title: Storage Cost Attacks
description: "Learn about storage security best practices in NEAR smart contracts, including storage costs, state management, and preventing storage-related vulnerabilities."
---
-On NEAR, your contract pays for the storage it uses. This means that the more data you store, the more balance you need to cover for storage. If you don't handle these costs correctly (e.g. asking the user to cover their storage usage), then a million little deposits can drain your contract of its funds.
+In NEAR Protocol, smart contracts pay for the storage they use. This storage cost model creates a potential attack vector where malicious actors can drain contract funds by forcing the contract to pay for excessive storage costs.
-Let's walk through an example:
+---
+
+## How Storage Costs Work in NEAR
+
+NEAR uses a [storage staking model](../../protocol/storage/storage-staking.md) where:
+
+- **Contracts pay for storage** - The more data stored, the more NEAR balance required
+- **Storage is locked** - Balance is locked to cover storage costs, not spent
+- **Storage can be released** - Deleting data releases the locked balance back to the contract
+
+**Critical Rule**: If your contract doesn't require users to cover their own storage costs, attackers can drain your contract's balance by creating many small storage entries.
+
+---
+
+## Attack Scenario: Guest Book Example
+
+### Setup
+
+1. You deploy a guest book smart contract to `example.near`
+2. Users can add messages to the guest book
+3. Users pay only a small gas fee to store their message
+4. **Your contract pays the storage cost** for each message.
+
+
+
+### The Attack
+
+**Problem**: If storing a message costs the user very little (just gas) but costs your contract significantly more (storage rent), this creates an economic imbalance that attackers can exploit.
+
+**Attack Vector**:
+- Attacker creates a script that sends thousands of small messages
+- Each message costs the attacker minimal gas fees
+- Each message forces your contract to lock NEAR for storage
+- After many messages, your contract's balance is locked
+- Contract can no longer function due to insufficient balance.
+
+**Result**: Your contract becomes unusable, and you may need to fund it continuously or delete data to free balance.
+
+
+
+### Example Flow
+
+1. Contract `example.near` starts with 10 NEAR balance
+2. Each message requires 0.001 NEAR locked for storage
+3. Attacker sends 10,000 messages
+4. Contract must lock 10 NEAR (10,000 × 0.001)
+5. Contract balance is fully locked
+6. Contract cannot accept new messages or perform other operations.
+
+---
+
+## Solution: Require Users to Cover Storage
+
+### Implementation
-1. You launch [a guest book app](../../tutorials/examples/guest-book.md), deploying your app's smart contract to the account `example.near`
-2. Visitors to your app can add messages to the guest book. This means your users will pay a small gas fee to **store** their message to your contract.
-3. When a new message comes in, NEAR will check if `example.near` has enough balance to cover the new storage needs. If it does not, the transaction will fail.
+Require users to attach sufficient NEAR to cover the storage cost of their data:
-Note that this can create an attack surface. If sending data to your guest book is inexpensive to the user while costing the contract owner significantly more, a malicious user can exploit the imbalance to make maintaining the contract prohibitively expensive.
+```rust
+pub fn add_message(&mut self, message: String) {
+ let storage_cost = self.calculate_storage_cost(&message);
+ assert!(env::attached_deposit() >= storage_cost, "Insufficient deposit for storage");
+
+ // Store the message
+ // Storage cost is covered by attached deposit
+}
+```
+
+**Key Points**:
+- Calculate the storage cost for the data being stored
+- Require users to attach at least that amount as deposit
+- The attached deposit covers the storage cost automatically
+- Users who want to store data must pay for it.
+
+
+
+### Benefits
+
+1. **Prevents storage drain attacks** - Attackers must pay for their own storage
+2. **Economic sustainability** - Contract doesn't need continuous funding
+3. **Fair cost distribution** - Users pay for what they use
+4. **Prevents abuse** - Makes spam attacks expensive.
+
+---
+
+## Releasing Locked Storage Balance
+
+**Important**: Storage balance is **locked**, not spent. You can release it by deleting data:
+
+- When you delete stored data, the locked balance is returned to the contract
+- This allows you to free up balance if needed
+- Useful for maintenance or if you need to recover locked funds.
+
+**Example**:
+- Contract has 50 NEAR locked for storage
+- You delete old/unused data
+- 50 NEAR is unlocked and available in contract balance.
+
+---
+
+## Best Practices
+
+1. **Always require users to cover storage costs** for data they store
+2. **Calculate storage costs accurately** - ensure attached deposit covers the cost
+3. **Document storage requirements** - tell users how much to attach
+4. **Monitor contract balance** - ensure sufficient funds for operations
+5. **Implement data expiration** - allow deletion of old data to free balance
+6. **Test with attack scenarios** - simulate many small storage operations.
+
+---
+
+## Common Attack Patterns
+
+### 1. Small Deposit Attack
+- Attacker sends many tiny deposits
+- Each requires storage (user records, transaction history)
+- Contract balance drains from storage costs.
+
+### 2. Data Spam Attack
+- Attacker stores excessive amounts of data
+- Contract must lock balance for all storage
+- Contract becomes unusable.
+
+### 3. Collection Growth Attack
+- Attacker adds many items to collections (maps, vectors)
+- Each item requires storage
+- Contract balance depletes.
+
+---
-One possible way to tackle this problem is asking the user to attach money to the call to cover the storage used by their message.
+## Prevention Checklist
-:::tip
-Remember that you can release the *balance locked for storage* by simply deleting data from the contract.
-:::
+- [ ] Users must attach deposit to cover their storage costs
+- [ ] Storage cost calculation is accurate
+- [ ] Contract monitors and maintains sufficient balance
+- [ ] Old data can be deleted to free balance
+- [ ] Storage requirements are documented for users
+- [ ] Attack scenarios have been tested.
diff --git a/docs/smart-contracts/security/sybil.md b/docs/smart-contracts/security/sybil.md
index e660fa86980..4b5073d2915 100644
--- a/docs/smart-contracts/security/sybil.md
+++ b/docs/smart-contracts/security/sybil.md
@@ -4,6 +4,225 @@ title: Sybil Attacks
description: "Learn about sybil attacks in NEAR smart contracts and how to prevent them with proper identity verification and anti-gaming mechanisms."
---
-While developing your smart contract, keep in mind that an individual can potentially create multiple NEAR accounts. This is especially relevant in ecosystems involving crowd decisions, such as [DAOs](../../primitives/dao.md).
+A Sybil attack occurs when a single individual or entity creates multiple accounts to gain disproportionate influence in a system. In NEAR Protocol, account creation is relatively inexpensive and straightforward, making Sybil attacks a significant concern for applications that rely on per-account voting, governance, or resource allocation.
-Imagine that you open the voting to anyone in the community. If each account can cast a vote, a malicious actor could span multiple accounts and gain a disproportionately large influence on the result.
\ No newline at end of file
+
+NEAR Protocol allows anyone to create multiple accounts easily:
+- **Low cost** - Account creation is inexpensive
+- **No identity verification** - No KYC or identity checks required
+- **Unlimited accounts** - No restrictions on number of accounts per person
+- **Full functionality** - Each account has full capabilities.
+
+---
+
+## Attack Scenario: DAO Voting
+
+### Vulnerable Voting System
+
+Imagine you create a [DAO](../../primitives/dao.md) (Decentralized Autonomous Organization) with the following design:
+
+- **Open voting** - Anyone in the community can vote
+- **One vote per account** - Each NEAR account gets one vote
+- **Simple majority** - Decisions made by majority vote
+- **No identity verification** - No checks to ensure one person = one account.
+
+
+
+### The Attack
+
+**Scenario**:
+1. DAO proposal requires community vote
+2. Malicious actor wants proposal to pass (or fail)
+3. Attacker creates 100 NEAR accounts
+4. Attacker votes with all 100 accounts in favor of their preferred outcome
+5. Attacker's 100 votes outweigh legitimate community votes
+6. Attacker controls the outcome.
+
+```rust
+// ❌ VULNERABILITY: One-account-one-vote - attacker can create multiple accounts
+pub fn vote(&mut self, vote: bool) {
+ let voter = env::signer_account_id();
+
+ // No checks for multiple accounts from same person
+ // Attacker can create 100 accounts and vote 100 times
+
+ if !self.votes.contains_key(&voter) {
+ self.votes.insert(voter, vote);
+
+ if vote {
+ self.yes_votes += 1;
+ } else {
+ self.no_votes += 1;
+ }
+ }
+}
+```
+
+**Result**: A single person controls the governance decision through multiple accounts, defeating the purpose of decentralized governance.
+
+---
+
+## Real-World Impact
+
+Sybil attacks are particularly dangerous for:
+
+### 1. DAO Governance
+- **Voting manipulation** - Single person controls multiple votes
+- **Proposal outcomes** - Attackers can force proposals to pass/fail
+- **Treasury control** - Attackers can influence fund allocation
+- **Protocol changes** - Attackers can push harmful protocol updates.
+
+### 2. Airdrops and Token Distribution
+- **Unfair allocation** - Attackers claim multiple airdrops
+- **Resource drain** - Legitimate users get less
+- **Economic manipulation** - Attackers accumulate disproportionate tokens.
+
+### 3. Reputation Systems
+- **Fake reviews** - Multiple accounts create fake positive/negative reviews
+- **Rating manipulation** - Attackers boost or damage reputation scores
+- **Trust systems** - Attackers game trust mechanisms.
+
+### 4. Resource Allocation
+- **Fair distribution** - Attackers claim multiple shares
+- **Quota systems** - Attackers bypass per-account limits
+- **Reward programs** - Attackers claim multiple rewards.
+
+---
+
+## Prevention Strategies
+
+### 1. Proof of Humanity / Identity Verification
+
+**How it works**:
+- Require users to prove they are unique humans
+- Use services that provide identity verification
+- Verify identity before allowing participation.
+
+**Benefits**:
+- Strong protection against Sybil attacks
+- Ensures one person = one vote/account.
+
+**Limitations**:
+- Requires external services
+- May reduce accessibility
+- Privacy concerns.
+
+
+
+### 2. Token-Weighted Voting
+
+**How it works**:
+- Votes weighted by token holdings
+- More tokens = more voting power
+- Still vulnerable but requires economic stake.
+
+**Benefits**:
+- Attackers must acquire tokens (costly)
+- Aligns incentives with economic stake
+- Reduces casual Sybil attacks.
+
+**Limitations**:
+- Wealthy actors still have more influence
+- Doesn't prevent wealthy Sybil attacks
+- May not be suitable for all use cases.
+
+
+
+### 3. Reputation-Based Systems
+
+**How it works**:
+- Build reputation over time
+- Require minimum reputation to vote
+- Reputation earned through legitimate activity.
+
+**Benefits**:
+- Makes Sybil attacks time-consuming
+- Requires sustained legitimate activity
+- Reduces impact of new fake accounts.
+
+**Limitations**:
+- Doesn't prevent long-term Sybil attacks
+- May exclude legitimate new users
+- Complex to implement.
+
+
+
+### 4. Economic Barriers
+
+**How it works**:
+- Require deposits or fees to participate
+- Make account creation expensive
+- Slash deposits for malicious behavior.
+
+**Benefits**:
+- Increases cost of Sybil attacks
+- Deters casual attackers
+- Economic disincentive.
+
+**Limitations**:
+- May exclude legitimate users
+- Wealthy attackers can still afford it
+- Doesn't solve the fundamental problem.
+
+
+
+### 5. Time-Locked Participation
+
+**How it works**:
+- Require accounts to exist for minimum time
+- Delay voting rights for new accounts
+- Prevent immediate Sybil attacks.
+
+**Benefits**:
+- Simple to implement
+- Reduces impact of mass account creation
+- Gives time to detect patterns.
+
+**Limitations**:
+- Doesn't prevent long-term attacks
+- May delay legitimate participation
+- Attackers can plan ahead.
+
+
+
+### 6. Activity-Based Requirements
+
+**How it works**:
+- Require minimum activity before voting
+- Must interact with protocol multiple times
+- Prove engagement before participation.
+
+**Benefits**:
+- Makes Sybil attacks more expensive
+- Requires sustained effort
+- Filters out casual fake accounts.
+
+**Limitations**:
+- Doesn't prevent determined attackers
+- May be gamed with automated activity
+- Complex to define "legitimate activity".
+
+---
+
+## Best Practices
+
+1. **Never use simple one-account-one-vote** for important decisions
+2. **Combine multiple strategies** - Use layered defenses
+3. **Monitor for patterns** - Detect unusual account creation
+4. **Require economic stake** - Make attacks costly
+5. **Use identity verification** for critical governance
+6. **Implement reputation systems** for long-term protection
+7. **Document Sybil risks** - Inform users of limitations.
+
+---
+
+## DAO-Specific Recommendations
+
+For DAO governance systems:
+
+- **Use token-weighted voting** with minimum thresholds
+- **Require identity verification** for major decisions
+- **Implement reputation systems** for ongoing participation
+- **Monitor voting patterns** for suspicious activity
+- **Use multi-sig or time delays** for critical proposals
+- **Combine on-chain and off-chain** governance mechanisms.
\ No newline at end of file
diff --git a/website/sidebars.js b/website/sidebars.js
index d8803b2b1bb..783b3dfb145 100644
--- a/website/sidebars.js
+++ b/website/sidebars.js
@@ -298,11 +298,8 @@ const sidebar = {
{
type: 'category',
label: 'Security',
- link: {
- type: 'doc',
- id: 'smart-contracts/security/welcome',
- },
items: [
+ 'smart-contracts/security/introduction',
'smart-contracts/security/checklist',
'smart-contracts/security/storage',
'smart-contracts/security/callbacks',