AgentStack
SKILL verified MIT Self-run

Solidity Security

skill-clawdbotatg-clawdviction-solidity-security · by clawdbotatg

Master smart contract security best practices to prevent common vulnerabilities and implement secure Solidity patterns. Use when writing smart contracts, auditing existing contracts, or implementing security measures for blockchain applications.

No reviews yet
0 installs
0 views
view→install

Install

$ agentstack add skill-clawdbotatg-clawdviction-solidity-security

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures
Are you the author of Solidity Security? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.

About

Solidity Security

Master smart contract security best practices, vulnerability prevention, and secure Solidity development patterns.

When to Use This Skill

  • Writing secure smart contracts
  • Auditing existing contracts for vulnerabilities
  • Implementing secure DeFi protocols
  • Preventing reentrancy, overflow, and access control issues
  • Optimizing gas usage while maintaining security
  • Preparing contracts for professional audits
  • Understanding common attack vectors

Critical Vulnerabilities

1. Reentrancy

Attacker calls back into your contract before state is updated.

Vulnerable Code:

// VULNERABLE TO REENTRANCY
contract VulnerableBank {
    mapping(address => uint256) public balances;

    function withdraw() public {
        uint256 amount = balances[msg.sender];

        // DANGER: External call before state update
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success);

        balances[msg.sender] = 0;  // Too late!
    }
}

Secure Pattern (Checks-Effects-Interactions):

contract SecureBank {
    mapping(address => uint256) public balances;

    function withdraw() public {
        uint256 amount = balances[msg.sender];
        require(amount > 0, "Insufficient balance");

        // EFFECTS: Update state BEFORE external call
        balances[msg.sender] = 0;

        // INTERACTIONS: External call last
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success, "Transfer failed");
    }
}

Alternative: ReentrancyGuard

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

contract SecureBank is ReentrancyGuard {
    mapping(address => uint256) public balances;

    function withdraw() public nonReentrant {
        uint256 amount = balances[msg.sender];
        require(amount > 0, "Insufficient balance");

        balances[msg.sender] = 0;

        (bool success, ) = msg.sender.call{value: amount}("");
        require(success, "Transfer failed");
    }
}

2. Integer Overflow/Underflow

**Vulnerable Code (Solidity uint256) public balances;

function transfer(address to, uint256 amount) public { // No overflow check - can wrap around balances[msg.sender] -= amount; // Can underflow! balances[to] += amount; // Can overflow! } }


**Secure Pattern (Solidity >= 0.8.0):**

```solidity
// Solidity 0.8+ has built-in overflow/underflow checks
contract SecureToken {
    mapping(address => uint256) public balances;

    function transfer(address to, uint256 amount) public {
        // Automatically reverts on overflow/underflow
        balances[msg.sender] -= amount;
        balances[to] += amount;
    }
}

**For Solidity uint256) public balances;

function transfer(address to, uint256 amount) public { balances[msg.sender] = balances[msg.sender].sub(amount); balances[to] = balances[to].add(amount); } }


### 3. Access Control

**Vulnerable Code:**

```solidity
// VULNERABLE: Anyone can call critical functions
contract VulnerableContract {
    address public owner;

    function withdraw(uint256 amount) public {
        // No access control!
        payable(msg.sender).transfer(amount);
    }
}

Secure Pattern:

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

contract SecureContract is Ownable {
    function withdraw(uint256 amount) public onlyOwner {
        payable(owner()).transfer(amount);
    }
}

// Or implement custom role-based access
contract RoleBasedContract {
    mapping(address => bool) public admins;

    modifier onlyAdmin() {
        require(admins[msg.sender], "Not an admin");
        _;
    }

    function criticalFunction() public onlyAdmin {
        // Protected function
    }
}

4. Front-Running

Vulnerable:

// VULNERABLE TO FRONT-RUNNING
contract VulnerableDEX {
    function swap(uint256 amount, uint256 minOutput) public {
        // Attacker sees this in mempool and front-runs
        uint256 output = calculateOutput(amount);
        require(output >= minOutput, "Slippage too high");
        // Perform swap
    }
}

Mitigation:

contract SecureDEX {
    mapping(bytes32 => bool) public usedCommitments;

    // Step 1: Commit to trade
    function commitTrade(bytes32 commitment) public {
        usedCommitments[commitment] = true;
    }

    // Step 2: Reveal trade (next block)
    function revealTrade(
        uint256 amount,
        uint256 minOutput,
        bytes32 secret
    ) public {
        bytes32 commitment = keccak256(abi.encodePacked(
            msg.sender, amount, minOutput, secret
        ));
        require(usedCommitments[commitment], "Invalid commitment");
        // Perform swap
    }
}

Security Best Practices

Checks-Effects-Interactions Pattern

contract SecurePattern {
    mapping(address => uint256) public balances;

    function withdraw(uint256 amount) public {
        // 1. CHECKS: Validate conditions
        require(amount  0, "Amount must be positive");

        // 2. EFFECTS: Update state
        balances[msg.sender] -= amount;

        // 3. INTERACTIONS: External calls last
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success, "Transfer failed");
    }
}

Pull Over Push Pattern

// Prefer this (pull)
contract SecurePayment {
    mapping(address => uint256) public pendingWithdrawals;

    function recordPayment(address recipient, uint256 amount) internal {
        pendingWithdrawals[recipient] += amount;
    }

    function withdraw() public {
        uint256 amount = pendingWithdrawals[msg.sender];
        require(amount > 0, "Nothing to withdraw");

        pendingWithdrawals[msg.sender] = 0;
        payable(msg.sender).transfer(amount);
    }
}

// Over this (push)
contract RiskyPayment {
    function distributePayments(address[] memory recipients, uint256[] memory amounts) public {
        for (uint i = 0; i  0, "Amount must be positive");
        require(amount  {
      const [deployer] = await ethers.getSigners();

      const VictimBank = await ethers.getContractFactory("SecureBank");
      bank = (await VictimBank.deploy()) as SecureBank;
      await bank.waitForDeployment();

      const Attacker = await ethers.getContractFactory("ReentrancyAttacker");
      attackerContract = (await Attacker.deploy(await bank.getAddress())) as ReentrancyAttacker;
      await attackerContract.waitForDeployment();
    });

    it("Should prevent reentrancy attack", async function () {
      // Deposit funds
      await bank.deposit({ value: ethers.parseEther("10") });

      // Attempt reentrancy attack
      await expect(
        attackerContract.attack({ value: ethers.parseEther("1") }),
      ).to.be.revertedWith("ReentrancyGuard: reentrant call");
    });
  });

  describe("Integer Overflow Protection", function () {
    let token: SecureToken;

    before(async () => {
      const Token = await ethers.getContractFactory("SecureToken");
      token = (await Token.deploy()) as SecureToken;
      await token.waitForDeployment();
    });

    it("Should prevent integer overflow", async function () {
      const [, attacker] = await ethers.getSigners();

      // Attempt overflow
      await expect(token.transfer(attacker.address, ethers.MaxUint256))
        .to.be.reverted;
    });
  });

  describe("Access Control", function () {
    let contract: SecureContract;

    before(async () => {
      const Contract = await ethers.getContractFactory("SecureContract");
      contract = (await Contract.deploy()) as SecureContract;
      await contract.waitForDeployment();
    });

    it("Should enforce access control", async function () {
      const [, attacker] = await ethers.getSigners();

      // Attempt unauthorized withdrawal
      await expect(contract.connect(attacker).withdraw(100)).to.be.revertedWith(
        "Ownable: caller is not the owner",
      );
    });
  });
});

Audit Preparation

contract WellDocumentedContract {
    /**
     * @title Well Documented Contract
     * @dev Example of proper documentation for audits
     * @notice This contract handles user deposits and withdrawals
     */

    /// @notice Mapping of user balances
    mapping(address => uint256) public balances;

    /**
     * @dev Deposits ETH into the contract
     * @notice Anyone can deposit funds
     */
    function deposit() public payable {
        require(msg.value > 0, "Must send ETH");
        balances[msg.sender] += msg.value;
    }

    /**
     * @dev Withdraws user's balance
     * @notice Follows CEI pattern to prevent reentrancy
     * @param amount Amount to withdraw in wei
     */
    function withdraw(uint256 amount) public {
        // CHECKS
        require(amount <= balances[msg.sender], "Insufficient balance");

        // EFFECTS
        balances[msg.sender] -= amount;

        // INTERACTIONS
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success, "Transfer failed");
    }
}

Common Pitfalls

  1. Using tx.origin for Authentication: Use msg.sender instead
  2. Unchecked External Calls: Always check return values
  3. Delegatecall to Untrusted Contracts: Can hijack your contract
  4. Floating Pragma: Pin to specific Solidity version
  5. Missing Events: Emit events for state changes
  6. Excessive Gas in Loops: Can hit block gas limit
  7. No Upgrade Path: Consider proxy patterns if upgrades needed

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.