Quantum Audit Logo

Is gitlawb Safe?

On-chain security analysis — is it a scam or legit?

gitlawb GITLAWB
0x5f98…dba3
Base Not verifiedLast checked 3d ago 1 audit on record
Executive SummaryAI Copilot

The DERC20 token contract implements an ERC20 token with voting, permit, and Ownable features. It includes a vesting mechanism for initial token distribution and a time-based inflation mechanism controlled by the owner. While the contract utilizes standard OpenZeppelin libraries and includes custom error handling, a critical vulnerability was identified where vested tokens are minted to the contract but cannot be released to recipients due to a missing function. Additionally, the pool locking mechanism is non-functional, and significant centralized control by the owner poses economic risks.

1 Critical2 High1 Medium2 Low1 Informational
Volume 24h
$119.9K
Liquidity
$527.7K
Price
$0.000027
Token Age
4mo
Top 10 Holders
37.2%

Security Findings

Critical

Missing Vesting Release Functionality

C-01The constructor mints `vestedTokens` to `address(this)` (the contract itself) for later distribution to recipients based on `getVestingDataOf` and `vestingStart`/`vestingDuration`. However, there is no public function provided in the contract that allows these recipients to claim or release their vested tokens after the vesting period has started or ended. This renders the entire vesting mechanism non-functional, as tokens are locked in the contract indefinitely.
IssueThe constructor mints `vestedTokens` to `address(this)` (the contract itself) for later distribution to recipients based on `getVestingDataOf` and `vestingStart`/`vestingDuration`. However, there is no public function provided in the contract that allows these recipients to claim or release their vested tokens after the vesting period has started or ended. This renders the entire vesting mechanism non-functional, as tokens are locked in the contract indefinitely.
FixImplement a `release` or `claimVestedTokens` function that allows recipients to claim their vested tokens. This function should calculate the amount of tokens eligible for release based on `block.timestamp`, `vestingStart`, `vestingDuration`, `totalAmount`, and `releasedAmount` in `getVestingDataOf`, and then transfer the calculated amount to the recipient. Ensure proper checks to prevent releasing more than `totalAmount` or before `vestingStart`.
StatusUnresolved
High

Non-functional Pool Locking Mechanism

H-01The contract includes `lockPool` and `unlockPool` functions that set the `pool` address and toggle the `isPoolUnlocked` boolean. The stated purpose is to prevent tokens from being transferred into the pool while locked. However, the standard ERC20 `_transfer` function (inherited from OpenZeppelin) does not check the `isPoolUnlocked` state or the `pool` address. Consequently, tokens can still be transferred to the designated `pool` address even when `isPoolUnlocked` is `false`, making the locking mechanism entirely ineffective.
IssueThe contract includes `lockPool` and `unlockPool` functions that set the `pool` address and toggle the `isPoolUnlocked` boolean. The stated purpose is to prevent tokens from being transferred into the pool while locked. However, the standard ERC20 `_transfer` function (inherited from OpenZeppelin) does not check the `isPoolUnlocked` state or the `pool` address. Consequently, tokens can still be transferred to the designated `pool` address even when `isPoolUnlocked` is `false`, making the locking mechanism entirely ineffective.
FixTo make the pool locking mechanism functional, override the `_transfer` function (or a similar internal transfer function) to include a check. If `isPoolUnlocked` is `false`, prevent transfers where the `to` address is the `pool` address. Alternatively, if this feature is not critical, remove the `pool`, `isPoolUnlocked`, `lockPool`, and `unlockPool` variables and functions to reduce complexity and potential for misunderstanding.
StatusUnresolved
High

Centralized Control by Owner

H-02The `owner` of the contract, as defined by the `Ownable` pattern, has extensive control over critical token parameters and supply. The owner can call `mintInflation()` to mint new tokens to themselves, `burn()` tokens from their own address, `lockPool()`/`unlockPool()`, and `updateMintRate()` to change the yearly inflation rate. This high degree of centralization introduces significant governance and economic risks, as a compromised owner key or a malicious owner could manipulate the token supply and value without community consensus.
IssueThe `owner` of the contract, as defined by the `Ownable` pattern, has extensive control over critical token parameters and supply. The owner can call `mintInflation()` to mint new tokens to themselves, `burn()` tokens from their own address, `lockPool()`/`unlockPool()`, and `updateMintRate()` to change the yearly inflation rate. This high degree of centralization introduces significant governance and economic risks, as a compromised owner key or a malicious owner could manipulate the token supply and value without community consensus.
FixConsider implementing a multi-signature wallet for the `owner` role to distribute control and require multiple approvals for critical operations. For `updateMintRate`, consider adding a time-lock mechanism or a governance vote to allow the community to react to proposed changes. For `mintInflation`, consider making it callable by a neutral, audited contract or a DAO, rather than directly by the owner.
StatusUnresolved
Medium

Precision Loss in Inflation Calculation

M-01The `mintInflation` function calculates `yearMint` and `partialYearMint` using integer division: `(supply * yearlyMintRate_ * time) / (1 ether * 365 days)`. While `yearlyMintRate_` is expressed in WAD (1 ether = 10^18), integer division inherently truncates decimal parts. For very small `supply` values, very low `yearlyMintRate_`, or short time periods, this could lead to a minor loss of precision in the minted amount, potentially resulting in slightly less inflation than mathematically precise calculations would yield.
IssueThe `mintInflation` function calculates `yearMint` and `partialYearMint` using integer division: `(supply * yearlyMintRate_ * time) / (1 ether * 365 days)`. While `yearlyMintRate_` is expressed in WAD (1 ether = 10^18), integer division inherently truncates decimal parts. For very small `supply` values, very low `yearlyMintRate_`, or short time periods, this could lead to a minor loss of precision in the minted amount, potentially resulting in slightly less inflation than mathematically precise calculations would yield.
FixWhile often acceptable for inflation mechanisms, if high precision is paramount, consider using a fixed-point math library or adjusting the calculation order to minimize truncation effects. For example, ensure the numerator is as large as possible before the final division. However, given the yearly nature of the rate, the current approach is likely sufficient for practical purposes, but the minor precision loss should be acknowledged.
StatusUnresolved
Low

Confusing `MaxTotalVestedExceeded` Requirement

L-01In the constructor, the `require(vestedTokens < initialSupply, MaxTotalVestedExceeded(...));` check enforces that the total amount of tokens designated for vesting (`vestedTokens`) must be strictly less than the `initialSupply`. This means it's impossible to vest the entire `initialSupply`, as at least one token must always go to the `recipient` specified in the constructor. The error message `MaxTotalVestedExceeded` implies exceeding a maximum, but the condition prevents `vestedTokens` from even reaching `initialSupply`, which might be an unintended restriction or confusing given the error name.
IssueIn the constructor, the `require(vestedTokens < initialSupply, MaxTotalVestedExceeded(...));` check enforces that the total amount of tokens designated for vesting (`vestedTokens`) must be strictly less than the `initialSupply`. This means it's impossible to vest the entire `initialSupply`, as at least one token must always go to the `recipient` specified in the constructor. The error message `MaxTotalVestedExceeded` implies exceeding a maximum, but the condition prevents `vestedTokens` from even reaching `initialSupply`, which might be an unintended restriction or confusing given the error name.
FixClarify the design intent. If it is acceptable for `vestedTokens` to equal `initialSupply` (meaning all initial tokens are vested), change the condition to `vestedTokens <= initialSupply`. If the current strict inequality is intentional, consider renaming the error or adding a comment to explain why `vestedTokens` must always be less than `initialSupply`.
StatusUnresolved
Low

Hardcoded `PERMIT_2` Address

L-02The `PERMIT_2` contract address (0x0000…8BA3) is hardcoded as a constant. While this is a standard and widely used address for Permit2, hardcoding it means that if the canonical Permit2 contract were to change in the future (e.g., due to an upgrade or a critical bug requiring a new deployment), this contract would not be able to interact with the new address without a redeployment.
IssueThe `PERMIT_2` contract address () is hardcoded as a constant. While this is a standard and widely used address for Permit2, hardcoding it means that if the canonical Permit2 contract were to change in the future (e.g., due to an upgrade or a critical bug requiring a new deployment), this contract would not be able to interact with the new address without a redeployment.
FixConsider making the `PERMIT_2` address configurable by the owner or through a governance mechanism. This would allow for flexibility in case the canonical Permit2 address needs to be updated in the future. For non-upgradeable contracts, this might be less critical, but it's a good practice for long-lived protocols.
StatusUnresolved
Info

Lack of Events for Critical Actions

I-01Several critical state-changing functions, such as `mintInflation()`, `burn()`, `lockPool()`, `unlockPool()`, and `updateMintRate()`, do not emit corresponding events. Emitting events is crucial for off-chain monitoring, indexing, and providing transparency into the contract's operations. Without events, it is difficult for users, block explorers, and external systems to track these significant actions.
IssueSeveral critical state-changing functions, such as `mintInflation()`, `burn()`, `lockPool()`, `unlockPool()`, and `updateMintRate()`, do not emit corresponding events. Emitting events is crucial for off-chain monitoring, indexing, and providing transparency into the contract's operations. Without events, it is difficult for users, block explorers, and external systems to track these significant actions.
FixEmit events for all critical state-changing actions. For example, `MintInflation(address indexed minter, uint256 amount)`, `TokensBurned(address indexed burner, uint256 amount)`, `PoolLocked(address indexed poolAddress)`, `PoolUnlocked(address indexed poolAddress)`, and `MintRateUpdated(uint256 oldRate, uint256 newRate)`.
StatusUnresolved

Category Ratings

TechnicalMedium6/10

The DERC20 contract demonstrates good architectural practices by inheriting from battle-tested OpenZeppelin contracts (ERC20, ERC20Votes, ERC20Permit, Ownable) and utilizing custom error messages for clarity (7.1 Architecture, 7.2 Code Security). The inflation mechanism, while complex, appears to correctly implement compounded yearly minting. However, a critical flaw exists where vested tokens are minted to the contract but no public function is provided for recipients to claim them (7.1 Architecture, 7.8 Operations). Furthermore, the `isPoolUnlocked` state variable and associated `lockPool`/`unlockPool` functions do not enforce any transfer restrictions, rendering the pool locking mechanism non-functional (7.1 Architecture, 7.2 Code Security).

GovernanceHigh2/10

The contract exhibits a high degree of centralized control, as the `owner` role, inherited from Ownable, possesses significant power (7.3 Access Control, 7.5 Governance). The owner can mint inflation tokens, burn tokens from their own address, and update the `yearlyMintRate`, which directly impacts the token's supply and economics (7.4 Economic). While the `updateMintRate` function includes a safeguard to mint pending inflation before changing the rate, the sole control over these parameters by a single address introduces a substantial governance and economic risk.

UpgradesMedium6/10

The provided contract is not implemented as an upgradeable proxy. Therefore, upgradeability risks are not applicable to this specific deployment. Any changes to the contract logic would require a new deployment and migration of assets.

Security Checklist

Contract VerifiedPass
Ownership RenouncedFail
No Mint FunctionPass
Liquidity LockedFail
Not a ProxyPass

Holder Composition

8.8% in wallets28.4% in contracts
Effective Concentration20.2%

Share held by contracts — treasury, vesting, bridge or staking — is discounted against share held by wallets when the score is computed: a contract cannot decide to sell the way an anonymous holder can, though it can still be drained or voted to sell. Effective concentration is the figure the risk score is actually calculated from.

Liquidity Depth

Show 4 more pairsShow less

The 4 remaining pairs hold $84 between them and are not listed.

The risk score reads depth across every pair. The volume figure and the volume-to-liquidity ratio elsewhere on this page describe only the pair this audit analysed, so the two are not directly comparable.

LP Distribution

Top-1 Unlocked Holder68.2%
Top-3 Unlocked91.2%

Key Addresses

Deployer
0x6c9d…79f1
Unlocked LP Held By
0xcc94…aab30x080c…00940x6fa8…421e0x58ab…66240xed7a…418d0x13cc…bbd60x40b4…9fd20x5458…25e80xd485…b6930x6313…1a60

No privileged address appears among these holders: the unlocked liquidity sits with independent providers, not with the deployer.

What Raised This Score

  • Ownership NOT renounced — owner is a contract (governance/executor, not an EOA)
  • Top-10 concentration > 20% (37.2% total → 20.2% effective; 8.8% in EOAs, 28.4% in contracts — mild)
  • Liquidity not locked, but no owner/deployer address holds LP — market-depth risk, not rug risk
  • LP top1 unlocked holder = 68.2% (independent LP — depth risk, pool = 87% of DEX liquidity)
  • LP top3 unlocked holders = 91.2% (independent LP — depth risk, pool = 87% of DEX liquidity)
  • 1 Critical finding(s) from audit
  • 2 High finding(s) from audit
  • 1 Medium finding(s) from audit
  • 2 Low finding(s) from audit

Each factor is an on-chain fact recorded at the time of this analysis. The score is computed from them by a deterministic function, so the same contract returns the same score for anyone who runs the audit. How scores are computed

Related Audits

ViciCoin (VCNT)High RiskWrapped liquid staked Ether 2.0 (WSTETH)High RiskSuperform (UP)High RiskaeonHigh RiskCoinbase Wrapped Staked ETH (CBETH)High RiskThe Innovation Game (TIG)High Risk

Would You Like a More Detailed Audit of gitlawb?

Our AI-powered scanner gives you a deeper, real-time smart contract analysis — free, with every scoring factor shown.

Get Detailed Audit