To run tests, use the command: yarn hardhat test or yarn test
Example of testing a single test file: yarn hardhat test test/Holders/DamBlockingHolderContractTests.ts
To set up and run this project locally, follow these steps:
Ensure you have yarn installed. If not, you can install it via npm:
npm install --global yarn- Clone the repository:
git clone https://github.com/Datamine-Crypto/datamine-smart-contract-tests.git cd datamine-smart-contract-tests - Install the project dependencies:
yarn install
To compile the smart contracts and generate TypeChain types:
yarn hardhat compileTo execute all unit tests:
yarn testOr, to run tests with detailed stack traces:
npx hardhat test --show-stack-tracesTo run a specific test file:
npx hardhat test test/Holders/DamBlockingHolderContractTests.tsTo check for linting issues:
yarn lintTo automatically format the code:
yarn formatThis project leverages the following key technologies and frameworks:
- Hardhat: An Ethereum development environment for compiling, deploying, testing, and debugging your smart contracts.
- Ethers.js: A complete and compact library for interacting with the Ethereum Blockchain and its ecosystem.
- Chai: A BDD / TDD assertion library for Node.js and the browser, used for writing robust tests.
- TypeScript: A superset of JavaScript that adds static types, enhancing code quality and maintainability.
- ESLint: A pluggable linting utility for JavaScript and TypeScript.
- Prettier: An opinionated code formatter.
- OpenZeppelin Contracts: A library for secure smart contract development (used for ERC777 interfaces, as noted in
GEMINI.md). - hardhat-erc1820: A Hardhat plugin for working with the ERC1820 registry, essential for ERC777 hooks.
- hardhat-typechain: Generates TypeScript bindings for smart contracts.
- hardhat-verify: Verifies contracts on Etherscan and other explorers.
- hardhat-keystore: Manages account keystores.
This project features a comprehensive suite of unit tests designed to ensure the security, functionality, and intended behavior of the Datamine Network ecosystem smart contracts. Our testing philosophy, which we call "Vibe Unit Testing," emphasizes intuitive, natural language test generation with the help of Gemini.
Instead of writing test code manually, you can describe the scenario you want to test, and Gemini will generate the necessary contracts and test files. This approach allows for rapid exploration of edge cases and attack vectors, ensuring robust contract security.
How to Vibe Test:
- Open the project in your editor.
- Start a chat with the Gemini assistant.
- Provide a clear, descriptive prompt outlining the test you want to add. Be specific about the contracts involved, the actions to be taken, and the expected outcome (e.g., a revert with a specific message, or a change in state).
Example Prompt:
Here is an example of a prompt that was used to generate a new "attack" test for this project. You can use it as a template for your own requests:
Can you add some "attack" tests to show that the contract is behaving as expected. Specfically I want you to add tests that are not covered around the core Locking/burning/minting logic. For example a test that tries to cheat the system somehow to get an unfair advantage by gaming the system. Expect a revert as you should not be able to get an unfair advantage.
Before you start changing code let me know what I found and we can discuss further.
Gemini will then analyze the existing codebase, propose a plan, and, upon your approval, generate and add the new test files to the test/ directory.
Our tests rigorously cover the following aspects of the Datamine Network smart contracts:
- Core Token Functionalities: Comprehensive testing of DAM, FLUX, and LOCK token operations, including transfers, approvals, and balance management.
- Deployment and Migration: Verification of correct contract deployment, initial state, and migration parameters.
- Operator Patterns and Delegated Actions: Thorough checks of ERC777 operator mechanisms, ensuring secure delegated control over tokens.
- ERC777 Hooks and Security: Extensive testing of
tokensToSendandtokensReceivedhooks, with a strong focus on preventing re-entrancy and other vulnerabilities. This includes dedicated attack tests likeDamBlockingHolderContractTests.tsandFluxTokenAttackTests.ts. - Failsafe Mechanisms: Validation of protective measures that limit token operations during critical periods, enhancing system stability.
- Minting and Burning Logic: Detailed testing of FLUX and LOCK token minting based on locked DAM, as well as various burning scenarios and their impact on token supply and multipliers.
- Multipliers and Incentives: Verification of time and burn multipliers that incentivize participation and contribute to the token's economic model.
- Edge Cases and Revert Conditions: Robust testing of invalid operations, ensuring contracts revert with appropriate error messages to maintain integrity.
The test suite is organized logically within the test/ directory, mirroring the contract structure to provide clear navigation and understanding of test coverage.
test/<ContractName>/: Each primary contract (or group of related contracts, likeHoldersforDamHolderandDamBlockingHolder) has its own subdirectory. These directories contain test files (.ts) dedicated to that contract's functionalities, deployment, migration, and specific attack scenarios.test/helpers/: This directory centralizes reusable code, utility functions, and common test setups. It includes:common.ts: Defines constants, enums for contract names, event names, and revert messages, along with general utility functions likemineBlocksandparseUnits.deployHelpers.ts: Contains functions to deploy various contracts consistently across tests.setupHelpers.ts: Provides helpers for setting up complex test scenarios, such as authorizing operators, locking tokens, and preparing contracts for re-entrancy tests.commonTests.ts: Encapsulates common test logic, liketestTokenBurn, to reduce redundancy.index.ts: Re-exports all helper modules for simplified imports in test files.
This modular structure enhances maintainability, readability, and allows for focused testing of individual contract components while leveraging shared utilities.
This project is configured to work with Gemini, allowing you to add new tests using natural language promptsβa process we call "vibe testing." Instead of writing test code manually, you can describe the scenario you want to test, and Gemini will generate the necessary contracts and test files.
- Open the project in your editor.
- Start a chat with the Gemini assistant.
- Provide a clear, descriptive prompt outlining the test you want to add. Be specific about the contracts involved, the actions to be taken, and the expected outcome (e.g., a revert with a specific message, or a change in state).
Here is an example of a prompt that was used to generate a new "attack" test for this project. You can use it as a template for your own requests:
Can you add some "attack" tests to show that the contract is behaving as expected. Specfically I want you to add tests that are not covered around the core Locking/burning/minting logic. For example a test that tries to cheat the system somehow to get an unfair advantage by gaming the system. Expect a revert as you should not be able to get an unfair advantage.
Before you start changing code let me know what you found and we can discuss further.
Gemini will then analyze the existing codebase, propose a plan, and, upon your approval, generate and add the new test files to the test/ directory.
The test suite is organized into logical components, verifying core features, migration parameters, re-entrancy prevention, and game-theory attack scenarios:
Tests covering the core ERC777/ERC20 functionality of the base DamToken contract:
DamToken Burningshould ensure supply burns properly via operator: Verifies that an operator can successfully burn DAM tokens.
DamToken DeploymentShould have the correct name and symbol: Verifies the token's name is "Datamine" and symbol is "DAM".Should assign the total supply of tokens to the owner: Verifies owner starts with 100% of supply.Should have the correct initial supply: Verifies initial supply is 25,000,000.
DAM Token Migration Testsshould ensure proper construction parameters with 25m premine: Verifies construction properties are correct for migration.should emit Minted and Transfer events on deployment: Verifies initial minting events are emitted correctly on deployment.should ensure supply burns properly via operator: Verifies burning supply via operator works correctly for migration.
DamToken Operator OperationsShould allow authorizing an operator: Verifies operator authorization works.Should allow revoking an operator: Verifies operator revocation works.Should revert when authorizing self as operator: Verifies self cannot be authorized as operator.Should revert when revoking self as operator: Verifies self cannot be revoked as operator.Should allow an authorized operator to send tokens: Verifies authorized operator can transfer tokens on behalf of holder.Should revert when an unauthorized operator tries to send tokens: Verifies unauthorized operator cannot transfer tokens.Should return an empty array for default operators: Verifies defaultOperators has 0 operators.
DamToken Send OperationsShould allow sending tokens to another address: Verifies normalsend()function transfers tokens and emitsSentevent.Should revert when sending more tokens than balance: Verifies balance constraint revert.Should revert when sending tokens to the zero address: Verifies sending to zero address is disallowed.
Tests for the DamHolder contract, representing an on-chain entity holding DAM and interacting with Lockquidity:
DamHolder DeploymentShould allow sending 100 DAM tokens to a DamHolder contract: Verifies sending DAM to the holder contract works and updates balance.Should allow DamHolder to lock DAM tokens after receiving from owner: Verifies locking DAM from holder to Lockquidity works.Should correctly register ERC1820 interfaces in constructor: Verifies registry registersTokensSenderandTokensRecipientinterfaces.
DamHolder FunctionalityShould successfully authorize an operator: Verifies authorizing an operator on behalf of the holder.Should fail to lock if operator is not authorized: Verifies revert when trying to lock DAM if Lockquidity is not authorized.Should fail to lock more than 100 tokens during failsafe period: Verifies failsafe locking amount limit.Should fail to lock more tokens than balance: Verifies balance constraint revert.Should receive Ether via the receive() fallback function: Verifies contract can receive ETH.Should allow any address to trigger lock if operator is authorized: Verifies that if operator is authorized, any caller can trigger the lock of holder's tokens.Should fail to lock zero tokens: Verifies lock must be positive.Should fail when authorizing self as operator: Verifies self-operator authorization fails.Should fail to authorize operator for a non-ERC777 token: Verifies that authorizing operator fails if the token is not an ERC777 token (reverts).
DamHolder HooksShould emit TokensReceivedCalled event with correct arguments when receiving tokens: VerifiesTokensReceivedhook.Should emit TokensToSendCalled event with correct arguments when sending tokens: VerifiesTokensToSendhook.
Advanced tests demonstrating the contract's resilience against re-entrancy attacks using ERC777 hooks:
Re-Entry TestsRe-Entry Test: DamBlockingHolder should prevent unlock() inside lock() with mutex AND allow send() before lock() finishes: Verifies that re-entrant calls tounlock()during alock()operation are blocked by the mutex, while normal token transfers during hooks still work.Re-Entry Test: Should revert if lock amount + hook send amount is greater than balance: Prevents combined transactions from overdrawing contract balance.Re-Entry Test: Should revert if hook send amount is greater than balance after lock: Prevents overdrawing balance after lock amount has been deducted.Re-Entry Test: DamBlockingHolder should prevent unlock() inside unlock() via tokensReceived hook: Verifies that re-entrant calls tounlock()during anotherunlock()operation are blocked by the recursion mutex.
Tests covering the Lockquidity factory, vault, and ERC20/ERC777 token (LockquidityToken) minted by locking DAM:
LockToken Attack Scenariosshould not be possible to mint tokens for a past lock period after re-locking: Verifies that historical block numbers from a previous lock period cannot be re-used to mint rewards after an unlock and re-lock.
LockToken - Attack ScenariosShould prevent re-entrancy on burnToAddress and not burn twice: Re-entrancy attack simulation onburnToAddressvia ERC777 hooks; mutex successfully prevents double-burning.Should revert if lock amount is 0: Lock amount must be > 0.Should revert if burn amount is 0: Burn amount must be > 0.Should revert if burning to an unlocked address: Target ofburnToAddressmust have active lock.Should revert if a locked attacker tries to mint from another users locked tokens (minter delegation theft): A locked attacker (validator) attempts to callmintToAddressusing the victim's address assourceAddressto steal their accrued rewards. Reverts because the attacker is not the delegated minter for the victim's lock.Should dilute other validators multipliers if an attacker locks 1 wei and burns a massive amount: Multiplier Dilution Attack test for LockToken β mirrors the FluxToken test. Verifies that an attacker locking 1 wei and burning massive LOCK dilutes the honest validator's burn multiplier. LockToken uses_percentBurnMultiplier=1so the base multiplier is 10001 (vs FluxToken's 20000).Should allow multiplier retroactivity / supercharging in the same block: Verifies that burning LOCK and minting in the same block applies the boosted burn multiplier retrospectively to the entire accrual period on LockToken.Should not allow double-minting in the same block by stepping block target: Prevents callingmintToAddresstwice in the same block on LockToken by shifting target blocks. ConfirmslastMintBlockNumberstate update prevents double-extraction.
LockToken BurnShould revert if a user tries to burn more tokens than they have: Prevents burning more LOCK than the user possesses.
LockToken DeploymentShould deploy LockquidityFactory and its internal contracts: Verifies deployment of factory, token, and vault contracts.Should allow locking DAM tokens: Verifies locking DAM into Lockquidity.Should allow locking, minting, and burning LOCK tokens: Verifies end-to-end locking DAM, minting LOCK rewards, and burning LOCK.
LockToken FailsafeShould prevent locking more than 100 tokens during failsafe period: Enforces the failsafe limit of 100 tokens.
LockToken MintShould revert if targetBlock is in the future: Target block must be <= current block.Should revert if targetBlock is before lastMintBlockNumber: Target block must be ahead of last mint.Should revert if caller is not the minterAddress: Caller must be the delegated minter.Should revert if sourceAddress has no locked tokens: Source address must have locked DAM to mint LOCK.
LockToken MultipliersShould calculate the time multiplier correctly: Verifies time multiplier increases with blocks.Should calculate the burn multiplier correctly: Verifies burn multiplier increases when LOCK is burned.
LockToken UnlockShould allow a user to unlock their tokens: Verifies unlocking DAM restores user balance.Should revert if a user tries to unlock without having locked tokens: Reverts if unlocking without an active lock.Should revert when attempting to lock tokens when already locked: Reverts if locking again without unlocking first.Should revert when attempting to lock and unlock/lock in the same block: Reverts if attempting lock and unlock/lock operations in the same block.
Tests covering FluxToken, the primary utility token minted by locking DAM:
FluxToken Attack Scenariosshould not be possible to mint tokens for a past lock period after re-locking: Verifies that historical block numbers cannot be re-used to mint rewards after an unlock/re-lock cycle.
FluxToken - Attack ScenariosShould prevent re-entrancy on burnToAddress and not burn twice: Malicious contract attempts re-entry onburnToAddressvia ERC777 hooks; mutex prevents double-burning.Should revert if lock amount is 0: Lock amount must be > 0.Should revert if burn amount is 0: Burn amount must be > 0.Should revert if trying to unlock without locked tokens: Unlock requires active lock.Should revert if burning to an unlocked address: Target ofburnToAddressmust have active lock.Should not allow double-minting in the same block by stepping block target: Prevents callingmintToAddresstwice in the same block by shifting targets (viaevm_setAutomine(false)).Should dilute other validators multipliers if an attacker locks 1 wei and burns a massive amount: Multiplier Dilution Attack test verifies that an attacker locking 1 wei of DAM and burning a large quantity of FLUX dilutes everyone else's yield multipliers.Should allow multiplier retroactivity / supercharging in the same block: Verifies that a user can wait many blocks to accrue rewards, burn FLUX to increase their multiplier, and retrospectively apply the boosted multiplier to the entire accrual period when minting in the same block.Should revert if a locked attacker tries to mint from another users locked tokens (minter delegation theft): A locked attacker (validator) attemptsmintToAddress(victim, attacker, block)to steal the victim's accrued FLUX rewards. Reverts because the attacker is not the victim's delegated minter.Should revert if someone tries to send DAM directly to the FluxToken contract (bypassing lock): An attacker uses ERC777send()to inject DAM directly into the FluxToken contract, bypassinglock(). ThetokensReceivedhook blocks this because only the FluxToken contract itself (as operator) can receive DAM.Should preserve burned amount across unlock/re-lock cycles (no free multiplier reset): Verifies thatburnedAmountpersists across unlock/re-lock cycles, confirming this is by design β the ecosystem rewards long-term participation by preserving burn history and multiplier state.
FluxToken DeploymentShould lock DAM tokens: Verifies deploying/locking DAM works and moves tokens to FluxToken contract.
FLUX Token Migration Testsshould ensure proper construction parameters with 0 premined coins: Verifies name, symbol, and 0 initial supply.ensure DAM holder can lock DAM in FLUX smart contract: Verifies locking DAM records locked amount and block number.ensure after locking-in DAM into FLUX you can unlock 100% of DAM back: Verifies unlocking recovers 100% of DAM.ensure failsafe works: Verifies that lock amount is capped during failsafe period, and the cap is lifted after failsafe period passes.ensure FLUX can be minted after DAM lock-in to another address: Verifies delegated minting to another address works.ensure FLUX can be target-burned: Verifies burning FLUX to increase a specific locked address'sburnedAmountworks and accumulates.should not be possible to lock and unlock/lock in the same block: Verifies that sending lock and unlock in same block reverts the second transaction.should revert when attempting to lock tokens when already locked: Verifies locking again without unlocking first reverts.
FluxToken MintShould mint tokens to the target address: Verifies basic minting of accrued rewards works.Should revert if targetBlock is in the future: Verifies target block must be <= current block.Should revert if targetBlock is before lastMintBlockNumber: Verifies target block must be ahead of last mint.Should revert if caller is not the minterAddress: Verifies caller must be the delegated minter.Should revert if owner tries to mint after delegating minter to another address: Verifies owner cannot mint if they delegated to another address.Should revert if sourceAddress has no locked tokens: Verifies source address must have locked DAM to mint.
Tests for the helper utility allowing compounding yield and managing minting delegations in batch operations:
BatchMinter Functionalityshould allow a user to batch burn when no delegated minter is set: Verifies that the lock owner can callbatchBurndirectly on theBatchMintercontract when no delegated minter has been set, successfully minting and burning FLUX to increase their multiplier.should allow a delegated minter to batch burn: Verifies that a delegated minter set by the owner can successfully executebatchBurnon behalf of the owner.should send tokens to targetAddress with normalMintTo: Verifies thatnormalMintTosuccessfully mints the reward FLUX and transfers them to the specified target address.should revert if an unauthorized caller attempts to batchBurn: Verifies that an arbitrary address cannot callbatchBurnon a user's lock.should revert if the lock owner attempts to batchBurn after delegating the minter: Verifies that once a delegated minter is set, the original lock owner can no longer executebatchBurndirectly; only the delegated minter can.should revert if an unauthorized caller attempts to normalMintTo: Verifies that an arbitrary address cannot callnormalMintToon a user's lock.
BatchMinter Yield Comparisonshould calculate total burned amount for normal minting: Simulates waiting and doing a single large mint/burn (normal minting).should calculate total burned amount for batch burning: Simulates waiting and doing a series of smaller batch-burns over the same block range, helping compare the yield difference (compounding).
Comprehensive tests for the gamified rewards system where players burn FLUX to claim jackpots:
HodlClickerRush Burn Edge Casesshould return NothingToMint if no FLUX is available to mint: ReturnsNothingToMintif block has not progressed since locking.should return ValidatorMinBlockNotMet if current block is less than minBlockNumber: ReturnsValidatorMinBlockNotMetif the validator's minimum block height setting has not been met yet.should return ValidatorMinBurnAmountNotMet if actual burn amount is less than minBurnAmount: ReturnsValidatorMinBurnAmountNotMetif the reward amount to be burned is less than validator's minimum burn amount setting.
HodlClickerRush DeploymentShould initialize with zero balances: Verifies initial variables (locked amount, rewards amount) are 0.
HodlClickerRush Depositshould allow depositing and calculate actualAmountToDeposit correctly when contract is empty: Verifies first deposit is credited at 1:1 ratio.should calculate actualAmountToDeposit correctly when rewards have been added to the pool: Verifies deposits are scaled correctly when pool contains rewards, preserving withdrawal ratio.should revert when depositing with rewardsPercent above maximum: Verifies deposit reverts if rewards percentage > 100%.should store minBlockNumber and minBurnAmount correctly: Verifies setting and storing validator parameters.should result in 0 actualAmountToDeposit when depositing a very small amount (dust) under high rewards-to-locked ratio: Verifies rounding down behavior on tiny deposit amounts when rewards ratio is high.
HodlClickerRush Front-runningshould allow a player to front-run validator normalMintToAddress and claim the jackpot, causing validator to revert: Verifies same-block transaction priority ordering where player front-runs a validator's direct mint transaction to claim the jackpot, causing the validator's transaction to revert.should handle multi-player jackpot gas war where only the highest gas price player succeeds and other players return NothingToMint silently: Verifies same-block jackpot claiming gas wars where only the highest gas price transaction succeeds in claiming the jackpot, while lower gas price claims execute successfully (no revert) but returnNothingToMintand get 0 rewards.
HodlClickerRush Pauseshould allow an address to pause itself: Verifies pausing.should allow an address to unpause itself: Verifies unpausing.should prevent a paused address from burning tokens: Verifies burning to a paused validator returnsValidatorPaused.should allow an unpaused address to burn tokens: Verifies burning to an unpaused validator succeeds.should emit PausedChanged event: Verifies pause/unpause events are emitted.
HodlClickerRush Rewardsshould give a jackpot and update rewards correctly: Verifies player receives jackpot (50% of tip) and contract updates pools.should correctly calculate total tip and jackpot amount using getTipAndJackpotAmount function: Verifies helper calculations.
HodlClickerRush Scenariosshould correctly handle the user-specified reward scenario: A full end-to-end walkthrough of deposits, block mining, burning (jackpot generation), and user withdrawal, verifying all balances.should correctly handle batch burning via burnTokensFromAddresses: Verifies processing multiple burn requests in a single transaction, skipping paused validators gracefully.
HodlClickerRush Simple Burnshould return InsufficientContractBalance if not enough FLUX is deposited: ReturnsInsufficientContractBalanceif the reward pool is empty or too small.should successfully burn tokens if enough FLUX is deposited: ReturnsSuccessif pool is sufficient.
HodlClickerRush Withdrawshould allow a user to withdraw their proportional share of rewards: Verifies a user can withdraw their rewards pool share.should not allow withdrawing more than earned rewards: Verifies withdraw reverts if rewards balance is 0.should not allow withdrawing zero amount: Verifies withdraw reverts if no rewards are earned.
This project is licensed under the MIT License - see the LICENSE file for details.