The protocol is designed to facilitate a token sale with distinct pre-sale and public sale phases. Here's an overview of its functionality:
The contract is associated with an ERC20 token, which is specified at the time of contract deployment.
There are two distinct sale phases:
- Pre-Sale: A phase where tokens can be purchased up to a certain cap
PRE_SALE_CAP
with minimum and maximum contribution limits per participantPRESALE_MINIMUM_CONTRIBUTION_PER_PARTICIPANT
andPRESALE_MAXIMUM_CONTRIBUTION_PER_PARTICIPANT
. - Public Sale: A phase with a higher cap
PUBLIC_SALE_CAP
and different minimum and maximum contribution limits per participantPUBLICSALE_MINIMUM_CONTRIBUTION_PER_PARTICIPANT
andPUBLICSALE_MAXIMUM_CONTRIBUTION_PER_PARTICIPANT
. - Sale Activation: The contract owner can activate or deactivate each sale phase using
changePreSaleStatus
andchangePublicSaleStatus
functions.
Participants can buy tokens by sending ETH to the buyTokens
function. The number of tokens received is determined by the _calculateTokens
function, which converts the ETH amount to a token amount (in this case, at a fixed rate of 1 ETH = 10 tokens).
Contributions are tracked per address, and the total contributions are recorded to ensure caps are not exceeded.
The contract owner can distribute tokens to any address using the distributeTokens
function.
Participants can request refunds through the refund
function, which requires them to return the purchased tokens. Refunds are only processed when the pre-sale is not active, and the public sale is either active or has ended, and the participant's contribution is below the minimum threshold.
- Install Foundry
First, run the command below to get Foundryup, the Foundry toolchain installer:
curl -L https://foundry.paradigm.xyz | bash
Then, in a new terminal session or after reloading your PATH, run it to get the latest forge and cast binaries:
foundryup
- Clone This Repo and install dependencies
git clone https://github.com/anjanayraina/Assigment1
cd Assigment1
forge install
- Run the Tests
forge test
The TokenSaleContract
design reflects several choices aimed at creating a structured and manageable token sale event. Here's a brief explanation of the key design choices:
The contract distinguishes between pre-sale and public sale phases to cater to different groups of investors. Pre-sales often offer better terms to early backers or smaller investors, while public sales are open to a wider audience.
The contract enforces hard caps on the total amount that can be raised (PRE_SALE_CAP
and PUBLIC_SALE_CAP
) and sets minimum and maximum contribution limits per participant for each phase. These limits help prevent individual investors from dominating the sale and ensure wider distribution of tokens.
The ability for the contract owner to toggle the sale phases on and off provides control over the timing of the sale and the ability to pause or end a sale phase in response to external factors.
Contributions are tracked per address to enforce individual contribution limits and to facilitate refunds if necessary.
The _calculateTokens
function uses a fixed exchange rate for simplicity. In a real-world scenario, this could be replaced with a more complex pricing mechanism that accounts for dynamic pricing, bonuses, or tiered discounts.
The distributeTokens
function allows the owner to distribute tokens outside of the sale mechanism, which can be used for airdrops, rewards, or compensating team members and advisors.
The refund
function allows participants to get their ETH back if certain conditions are met. This provides a level of protection for participants and can be a trust-building feature.
The use of custom errors instead of traditional require
statements with string messages saves gas and provides clearer error handling.
Events are emitted for key actions, providing transparency and enabling off-chain services to monitor and react to contract activity.
The use of OpenZeppelin's SafeERC20 library and Reentrancy Gaurd for token and ETH transfers helps prevent reentrancy attacks, which are common vulnerabilities in smart contracts that handle cryptocurrency transactions.
By setting hard caps on the total amount that can be raised and individual contribution limits, the contract prevents excessive contributions that could lead to a monopoly of the token supply and mitigates the risk of a single entity exerting too much influence over the token.
The contract inherits from OpenZeppelin's Ownable contract, which provides a secure implementation of ownership and access control. This ensures that only the contract owner can activate or deactivate sale phases and distribute tokens, reducing the risk of unauthorized access.
The contract follows the checks-effects-interactions pattern, where state changes are made before external calls (e.g., token transfers), reducing the surface for reentrancy attacks.
The contract does not make external calls in its constructor, which is a good practice to avoid attacks during deployment.
The use of constant state variables for caps and contribution limits ensures that these values cannot be altered after contract deployment, providing assurance to participants about the rules of the token sale.
The contract does not have functions that could run out of gas due to unbounded loops or excessive computation, which is important for preventing denial-of-service (DoS) attacks.
The contract uses OpenZeppelin's SafeERC20 library to safely interact with the ERC20 token contract, protecting against reentrancy and other token-related vulnerabilities.