Back to skill

Security audit

ERC20 Tokenomics Builder

Security checks for vulnerabilities and agentic risk

Overview

This skill is a coherent tokenomics guide, but its vesting deployment examples can create contracts and transfer real tokens without enough safety checks.

Review this skill carefully before installing or using it for deployment work. Treat its vesting scripts as illustrative only: test on a testnet first, verify chain ID and addresses, use SafeERC20 or equivalent transfer checks, read token decimals with parseUnits, confirm balances and allowances, validate every beneficiary and schedule, and require explicit confirmation before broadcasting transactions or transferring treasury tokens.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Warning
Location
references/vesting-factory.md:91
Finding
Unchecked ERC20 Transfer Result Can Leave Vesting Wallets Unfunded<![CDATA[ ## Vulnerability Details **File Location**: `references/vesting-factory.md:91` **Vulnerability Type**: Unchecked ERC20 transfer return value **Risk Level**: Medium ### Vulnerable Code ```solidity IERC20(token).transfer(wallet, _getAllocation(entries[i].label)); ``` ### Technical Analysis The Foundry deployment example invokes `IERC20.transfer` without checking its Boolean return value. Although many ERC20 implementations revert when a transfer fails, some compliant and legacy tokens instead return `false`. If such a token is used, the script can continue after a failed transfer and log the newly created vesting wallet as though it had been funded. The wallet would exist and its schedule would appear valid, but it would not hold the promised allocation. The use of OpenZeppelin `SafeERC20` is recommended because it supports tokens that return `false`, tokens that return no value, and tokens that revert. ### Attack Path 1. The operator configures the deployment script with a token that returns `false` on failed transfers. 2. A vesting wallet is successfully created for a beneficiary. 3. The funding transfer fails because of insufficient balance, token restrictions, a pause, a blacklist, or adversarial token behavior. 4. The script does not inspect the returned value and therefore continues execution. 5. The wallet is logged or published as created and funded even though it has no corresponding token allocation. 6. The beneficiary later attempts to release vested tokens but receives nothing. ### Impact Assessment This issue does not grant additional system privileges. Its scope is the token funding operation performed by the deployment script. Affected beneficiaries may receive unfunded or underfunded vesting wallets. This can result in failed token distributions, inaccurate cap-table records, breach of investor commitments, operational recovery costs, and financial loss. An adversarial or nonstandard token can deliberately trigger this failur ...[truncated 88 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Use OpenZeppelin `SafeERC20` and verify the resulting wallet balance: ```solidity import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; using SafeERC20 for IERC20; uint256 allocation = _getAllocation(entries[i].label); uint256 balanceBefore = IERC20(token).balanceOf(wallet); IERC20(token).safeTransfer(wallet, allocation); require( IERC20(token).balanceOf(wallet) == balanceBefore + allocation, "Unexpected vesting-wallet funding amount" ); ``` Additional hardening measures: 1. Validate that the token address contains contract code before starting deployment. 2. Check the sender's available token balance against the total planned allocation. 3. Reject zero beneficiary addresses, zero allocations, and invalid schedule parameters. 4. Record the intended and actual funded amounts in deployment output. 5. Abort the entire batch on a funding mismatch instead of continuing with later entries. 6. Add tests using tokens that return `false`, return no value, charge transfer fees, pause transfers, or enforce blacklists. 7. For fee-on-transfer or rebasing tokens, explicitly reject them or calculate allocations from verified balance changes. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
references/vesting-factory.md:121
Finding
Hardcoded 18-Decimal Conversion Can Misallocate Vesting Tokens<![CDATA[ ## Vulnerability Details **File Location**: `references/vesting-factory.md:121` **Vulnerability Type**: Incorrect token decimal handling **Risk Level**: High ### Vulnerable Code ```javascript await token.transfer(walletAddr, ethers.utils.parseEther(v.tokens.toString())); ``` ### Technical Analysis `ethers.utils.parseEther` always converts a human-readable value into 18-decimal base units. ERC20 tokens are not required to use 18 decimals; common tokens use 6, 8, or other decimal precision. Consequently, the submitted transfer amount may differ substantially from the intended allocation. For example, when used with a 6-decimal token, parsing an allocation with `parseEther` produces an amount that is `10^12` times larger than the correct base-unit value. If the deployer has enough tokens and the token permits the transfer, the beneficiary wallet can be overfunded. Otherwise, the transaction will revert and interrupt the deployment batch. Tokens with more than 18 decimals would instead be underfunded. ### Attack Path 1. The operator supplies an ERC20 token whose decimals value is not 18. 2. A vesting entry specifies its allocation in human-readable token units. 3. The script converts that allocation with `parseEther`, regardless of the token's actual precision. 4. The transfer is submitted with the incorrectly scaled base-unit amount. 5. For a token with fewer than 18 decimals, the transfer either overfunds the vesting wallet or reverts because the deployer lacks the inflated amount. 6. If overfunded, the beneficiary can release the excess tokens according to the vesting schedule unless the error is identified and corrected before tokens become releasable. ### Impact Assessment This issue does not provide operating-system or contract-administration privileges. Its scope is the deployer's ERC20 balance and the vesting wallets funded by this script. A successful oversized transfer can allocate substantially more tokens than authorized and dilute ...[truncated 273 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Read the token's decimal precision and use `parseUnits`: ```javascript const decimals = await token.decimals(); const intendedAmount = ethers.utils.parseUnits( v.tokens.toString(), decimals ); const balanceBefore = await token.balanceOf(walletAddr); const transferTx = await token.transfer(walletAddr, intendedAmount); await transferTx.wait(); const balanceAfter = await token.balanceOf(walletAddr); if (!balanceAfter.sub(balanceBefore).eq(intendedAmount)) { throw new Error(`Unexpected funded amount for ${v.label}`); } ``` Additional hardening measures: 1. Represent allocation values as strings in JSON to avoid JavaScript floating-point precision loss. 2. Query and display the token symbol, decimals, total planned allocation, and deployer balance before broadcasting. 3. Require explicit operator confirmation of the normalized base-unit amounts. 4. Verify that the sum of all allocations does not exceed the authorized distribution budget. 5. Abort if the token's reported decimals fall outside the project's approved configuration. 6. Validate every post-transfer balance before recording a vesting wallet as funded. 7. Add automated tests covering 6-, 8-, and 18-decimal tokens, very large allocations, and fractional token amounts. 8. Reject fee-on-transfer tokens unless the funding logic intentionally accounts for the amount actually received. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (3)

Ae4

Medium
Category
analysis-evasion
Confidence
80% confidence
Finding
Suspicious Unicode normalization or mixed-script content

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The Foundry and Hardhat examples perform live contract creation and token transfers in loops with no safety checks, dry-run guidance, or explicit confirmation steps. In a vesting context, mistakes in beneficiary addresses, timestamps, durations, or token amounts are irreversible on-chain and can permanently misallocate treasury assets or lock them into unintended wallets.

Intent-Code Divergence

Medium
Confidence
95% confidence
Finding
The Release Keeper example calls `factory.allWallets(0, count)`, but the factory shown only exposes the public array getter `allWallets(uint256)` and `totalWallets()`. An operator copying this automation will either fail at runtime or implement ad hoc workarounds, which can break release automation and leave vested tokens unreleased until manually corrected.

Static analysis

No suspicious patterns detected.