Selendra

Documentation

Security Practices

Smart contract security

Security critical for smart contracts. Bugs expensive after deployment. Follow best practices.

Common Vulnerabilities

Reentrancy

Check-effects-interactions pattern:

function withdraw(uint256 amount) external {
    require(balances[msg.sender] >= amount);
    balances[msg.sender] -= amount; // Update state first
    (bool success,) = msg.sender.call{value: amount}("");
    require(success);
}

Or use ReentrancyGuard:

import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

contract MyContract is ReentrancyGuard {
    function withdraw(uint256 amount) external nonReentrant {
        // Withdrawal logic
    }
}

Access Control

import "@openzeppelin/contracts/access/Ownable.sol";

contract MyToken is Ownable {
    function mint(address to, uint256 amount) external onlyOwner {
        _mint(to, amount);
    }
}

tx.origin Phishing

Never use tx.origin for authorization:

require(msg.sender == owner); // CORRECT
// Never: require(tx.origin == owner);

Unchecked External Calls

import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

using SafeERC20 for IERC20;
token.safeTransfer(to, amount); // Reverts on failure

Denial of Service

Avoid unbounded loops. Use pull pattern:

// GOOD - pull pattern
mapping(address => uint256) public rewards;

function claimReward() external {
    uint256 reward = rewards[msg.sender];
    rewards[msg.sender] = 0;
    payable(msg.sender).transfer(reward);
}

Security Tools

Slither

pip3 install slither-analyzer
slither contracts/MyContract.sol

Finds common vulnerabilities automatically.

Mythril

pip3 install mythril
myth analyze contracts/MyContract.sol

Code Review Checklist

  • ✓ Sensitive functions protected
  • ✓ No tx.origin usage
  • ✓ External call return values checked
  • ✓ Reentrancy protected
  • ✓ State updates before external calls
  • ✓ No unbounded loops
  • ✓ Zero addresses checked
  • ✓ Bounds verified
  • ✓ Important changes emit events

Emergency Controls

Pause Functionality

import "@openzeppelin/contracts/utils/Pausable.sol";

contract MyContract is Pausable, Ownable {
    function emergencyPause() external onlyOwner {
        _pause();
    }

    function transfer(address to, uint256 amount) external whenNotPaused {
        // Transfer logic
    }
}

Circuit Breakers

uint256 public maxDailyWithdraw = 1000000;
uint256 public dailyWithdrawn;
uint256 public lastResetTime;

function withdraw(uint256 amount) external {
    if (block.timestamp > lastResetTime + 1 days) {
        dailyWithdrawn = 0;
        lastResetTime = block.timestamp;
    }

    require(dailyWithdrawn + amount <= maxDailyWithdraw, "Daily limit");
    dailyWithdrawn += amount;
    _withdraw(amount);
}

Best Practices

  • Use proven libraries: OpenZeppelin audited extensively
  • Keep it simple: Complexity increases risk
  • Test everything: Complete test suite catches bugs
  • Document assumptions: Help auditors understand code

Self-Audit Steps

  1. Automated tools: Run Slither and Mythril
  2. Manual review: Read code line by line
  3. Peer review: Fresh eyes catch issues
  4. Test coverage: Aim for 90%+

Same Risks as Ethereum

EVM compatible means same vulnerabilities. All Ethereum exploits possible. Low fees enable thorough testing—deploy multiple test versions affordably.

Next Steps

Getting Started | Deployment

Contribute

Found an issue or want to contribute?

Help us improve this documentation by editing this page on GitHub.

Edit this page on GitHub