Critical Grep Patterns
#### Unsafe Code
```bash
# Unsafe blocks - valid only for FFI or specific optimizations
grep -rn "unsafe" src/
# Generally forbidden in smart contracts unless justified
```
Risk: Memory corruption, undefined behavior
Action: Require justification for each unsafe block
#### Panic Inducers
```bash
# Direct unwrap - can panic
grep -rn "\.unwrap()" src/
# Expect - also panics
grep -rn "\.expect(" src/
# Index access - can panic on out of bounds
grep -rn "\[.*\]" src/ | grep -v "storage_mapper"
```
Risk: Contract halts, potential DoS
Action: Replace with unwrap_or_else(|| sc_panic!(...)) or proper error handling
#### Floating Point Arithmetic
```bash
# f32 type
grep -rn "f32" src/
# f64 type
grep -rn "f64" src/
# Float casts
grep -rn "as f32\|as f64" src/
```
Risk: Non-deterministic behavior, consensus failure
Action: Use BigUint/BigInt for all calculations
#### Unchecked Arithmetic
```bash
# Direct arithmetic operators
grep -rn "[^_a-zA-Z]\+ [^_a-zA-Z]" src/ # Addition
grep -rn "[^_a-zA-Z]\- [^_a-zA-Z]" src/ # Subtraction
grep -rn "[^_a-zA-Z]\* [^_a-zA-Z]" src/ # Multiplication
# Without checked variants
grep -rn "checked_add\|checked_sub\|checked_mul" src/
```
Risk: Integer overflow/underflow
Action: Use BigUint or checked arithmetic for all financial calculations
#### Map Iteration (DoS Risk)
```bash
# Iterating storage mappers
grep -rn "\.iter()" src/
# Especially dangerous patterns
grep -rn "for.in.\.iter()" src/
grep -rn "\.collect()" src/
```
Risk: Gas exhaustion DoS
Action: Add pagination or bounds checking
Logical Pattern Analysis (Manual Review)
#### Token ID Validation
Search for payment handling:
```bash
grep -rn "call_value()" src/
grep -rn "all_esdt_transfers" src/
grep -rn "single_esdt" src/
```
For each occurrence, verify:
- [ ] Token ID checked against expected value
- [ ] Token nonce validated (for NFT/SFT)
- [ ] Amount validated (non-zero, within bounds)
```rust
// VULNERABLE
#[payable("*")]
fn deposit(&self) {
let payment = self.call_value().single_esdt();
self.balances().update(|b| *b += payment.amount);
// No token ID check! Accepts any token
}
// SECURE
#[payable("*")]
fn deposit(&self) {
let payment = self.call_value().single_esdt();
require!(
payment.token_identifier == self.accepted_token().get(),
"Wrong token"
);
require!(payment.amount > 0, "Zero amount");
self.balances().update(|b| *b += payment.amount);
}
```
#### Callback State Assumptions
Search for callbacks:
```bash
grep -rn "#\[callback\]" src/
```
For each callback, verify:
- [ ] Does NOT assume async call succeeded
- [ ] Handles error case explicitly
- [ ] Reverts state changes on failure if needed
```rust
// VULNERABLE - assumes success
#[callback]
fn on_transfer(&self) {
self.transfer_count().update(|c| *c += 1);
}
// SECURE - handles both cases
#[callback]
fn on_transfer(&self, #[call_result] result: ManagedAsyncCallResult<()>) {
match result {
ManagedAsyncCallResult::Ok(_) => {
self.transfer_count().update(|c| *c += 1);
},
ManagedAsyncCallResult::Err(_) => {
// Handle failure - funds returned automatically
}
}
}
```
#### Access Control
Search for endpoints:
```bash
grep -rn "#\[endpoint\]" src/
grep -rn "#\[only_owner\]" src/
```
For each endpoint, verify:
- [ ] Appropriate access control applied
- [ ] Sensitive operations restricted
- [ ] Admin functions documented
```rust
// VULNERABLE - public sensitive function
#[endpoint]
fn set_fee(&self, new_fee: BigUint) {
self.fee().set(new_fee);
}
// SECURE - restricted
#[only_owner]
#[endpoint]
fn set_fee(&self, new_fee: BigUint) {
self.fee().set(new_fee);
}
```
#### Reentrancy (CEI Pattern)
Search for external calls:
```bash
grep -rn "\.send()\." src/
grep -rn "\.tx()" src/
grep -rn "async_call" src/
```
Verify Checks-Effects-Interactions pattern:
- [ ] All checks (require!) before state changes
- [ ] State changes before external calls
- [ ] No state changes after external calls in same function