0

Deployer

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./launchpad.sol";

contract Deployer is Ownable {
    uint256 public launchpadCount;
    uint256 public deployCost = 0.01 ether;
    uint256 public totalInvested;
    uint256 public totalParticipants;
    uint256 public ownerFee;

    address public senseiLocker;

    mapping(uint256 => address) public launchpadById;
    mapping(address => uint256) public launchpadIdByAddress;
    mapping(address => bool) private isLaunchpad;
    mapping(address => address) public launchpadByToken;
    mapping(address => address[]) public userLaunchpadInvested;
    mapping(address => mapping(address => bool)) isLaunchpadAdded;

    event launchpadDeployed(address newTokenAddress, address deployer);

    constructor() {
        // Constructor logic can be added here, if needed.
    }

    function createLaunchpad(
        uint256[] memory _prices,
        uint256[] memory _caps,
        uint256[] memory _limits,
        uint256[] memory _times,
        uint256 _lockupPeriod,
        uint256 _liquidityPerc,
        string memory _URIData,
        address _token,
        bool whitelist
    ) public payable {
        require(msg.value >= deployCost, "Not enough BNB to deploy");
        require(launchpadByToken[_token] == address(0), "Launchpad already created");

        address[] memory _addresses = new address[](3);
        _addresses[0] = _token;
        _addresses[1] = msg.sender;
        _addresses[2] = senseiLocker;

        launchPad newLaunchpad = new launchPad(
            _prices,
            _caps,
            _times,
            _URIData,
            ownerFee,
            _token,
            owner(),
            address(this),
            whitelist  // Pass 'whitelist' argument here
        );
        newLaunchpad.transferOwnership(owner());

        uint256 tokensToDistribute = _prices[0] * _caps[1];
        uint256 tokensToLiquidity = (_prices[1] * _caps[1] * _liquidityPerc) / 100;
        uint256 tokensNeeded = ((tokensToDistribute + tokensToLiquidity) * 10**ERC20(_token).decimals()) / 100000000;

        ERC20(_token).transferFrom(msg.sender, address(newLaunchpad), tokensNeeded);

        payable(owner()).transfer(msg.value);

        launchpadById[launchpadCount] = address(newLaunchpad);
        launchpadIdByAddress[address(newLaunchpad)] = launchpadCount;
        launchpadCount++;

        launchpadByToken[_token] = address(newLaunchpad);
        isLaunchpad[address(newLaunchpad)] = true;

        emit launchpadDeployed(address(newLaunchpad), msg.sender);
    }

    function getDeployedLaunchpads(uint256 startIndex, uint256 endIndex)
        public
        view
        returns (address[] memory)
    {
        if (endIndex >= launchpadCount) {
            endIndex = launchpadCount - 1;
        }

        uint256 arrayLength = endIndex - startIndex + 1;
        uint256 currentIndex;
        address[] memory launchpadAddress = new address[](arrayLength);

        for (uint256 i = startIndex; i <= endIndex; i++) {
            launchpadAddress[currentIndex] = launchpadById[startIndex + i];
            currentIndex++;
        }

        return launchpadAddress;
    }

    function setPrice(uint256 _price) external onlyOwner {
        deployCost = _price;
    }

    function getInfo(uint256 id)
        external
        view
        returns (
            uint256[] memory data,
            uint8 _status,
            uint8 decimals,
            uint256 _score,
            string memory _URIData,
            string[] memory symbol,
            address _contractAdmin,
            address _tokenAddress,
            address contractAddress,
            bool[] memory booleans
        )
    {
        contractAddress = launchpadById[id];

        launchPad instance = launchPad(contractAddress);

        data = instance.getContractNumbers();

        (
        uint8 _status,
        uint256[] memory numbers,
        string memory _URIData,
        address _contractAdmin,
        address _tokenAddress,
        bool _kyc
    ) = instance.getContractInfo();


        symbol = new string[](2);

        decimals = ERC20(_tokenAddress).decimals();
        symbol[0] = ERC20(_tokenAddress).symbol();
        symbol[1] = ERC20(_tokenAddress).name();
    }

    function addToUserLaunchpad(address _user, address _launchpad) external {
        require(isLaunchpad[msg.sender], "Only launchpads can add");

        if (!isLaunchpadAdded[_user][_launchpad]) {
            userLaunchpadInvested[_user].push(_launchpad);
            isLaunchpadAdded[_user][_launchpad] = true;
        }
    }

    function updateStats(uint256 _invested, uint256 _contributors) external {
        require(isLaunchpad[msg.sender], "Only launchpads can add");

        totalInvested += _invested;
        totalParticipants += _contributors;
    }

    function removeLaunchpad(address _token, address _launchpad) external {
        require(isLaunchpad[msg.sender], "Only launchpads can remove");

        launchpadByToken[_token] = address(0);
        isLaunchpad[_launchpad] = false;
    }

    function getUserContributions(address _user)
        external
        view
        returns (uint256[] memory ids, uint256[] memory contributions)
    {
        uint256 count = userLaunchpadInvested[_user].length;
        ids = new uint256[](count);
        contributions = new uint256[](count);

        for (uint256 i; i < count; i++) {
            address launchpadaddress = userLaunchpadInvested[_user][i];
            ids[i] = launchpadIdByAddress[launchpadaddress];
            contributions[i] = launchPad(launchpadaddress).depositedAmount(
                _user
            );
        }
    }

    function getStats()
        external
        view
        returns (
            uint256 _projects,
            uint256 _invested,
            uint256 _participants
        )
    {
        _projects = launchpadCount;
        _invested = totalInvested;
        _participants = totalParticipants;
    }

    function changeLockerAddress(address _newLocker) external onlyOwner {
        senseiLocker = _newLocker;
    }
}

launchpad code

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

import "./IDeployer.sol";

contract launchPad is Ownable, Pausable {
    uint256 public softCap;
    uint256 public hardCap;
    uint256 public startTime;
    uint256 public endTime;
    uint256 public totalDeposits;
    uint256 public contributors;
    uint256 public ownerFee;
    uint8 public status; // 0- open, 1- finish, 2- refund

    string public URIData;

    address public tokenToReceive;
    address public contractAdmin;

    bool public kyc;

    mapping (address => uint256) public depositedAmount;
    mapping (uint256 => address) public contributorsList;

    IDeployer deployer;

    event userDeposit(uint256 amount, address user);
    event userRefunded(uint256 amount, address user);
    event saleClosed(uint256 timeStamp, uint256 collectedAmount);
    event saleCanceled(uint256 timeStamp, address operator);

    constructor(
    uint256[] memory _prices,
        uint256[] memory _caps,
        uint256[] memory _times,
        string memory _URIData,
        uint256 _fee,
        address _tokenToReceive,
        address _contractAdmin,
        address _deployer,
        bool whitelist
    ) {
        // Inisialisasi kontrak dengan parameter yang sesuai
        softCap = (_caps[0] * 10**18) / 10000;
        hardCap = (_caps[1] * 10**18) / 10000;
        startTime = _times[0];
        endTime = _times[1];
        URIData = _URIData;
        ownerFee = _fee;
        tokenToReceive = _tokenToReceive;
        contractAdmin = _contractAdmin;
        deployer = IDeployer(_deployer);
    }

    modifier restricted(){
        require(msg.sender == owner() || msg.sender == contractAdmin, "Caller not allowed");
        _;
    }

    function invest(uint256 _amount) external payable {
        require(status == 0, "Sale is not open");
        require(startTime < block.timestamp, "Sale is not open yet");
        require(endTime > block.timestamp, "Sale is already closed");
        require(_amount + totalDeposits <= hardCap, "Hardcap reached");

        if (tokenToReceive == address(0)) {
            require(_amount == msg.value, "Invalid payment amount");
        } else {
            // Transfer tokens to the contract
            IERC20(tokenToReceive).transferFrom(msg.sender, address(this), _amount);
        }

        if (depositedAmount[msg.sender] == 0) {
            contributorsList[contributors] = msg.sender;
            contributors++;
        }

        depositedAmount[msg.sender] = depositedAmount[msg.sender] + _amount;
        totalDeposits += _amount;

        deployer.addToUserLaunchpad(msg.sender, address(this));

        emit userDeposit(_amount, msg.sender);
    }

    function claimRefund () external {
        require(status == 2, "Refund is not available");
        uint256 deposit = depositedAmount[msg.sender];
        require(deposit > 0, "User doesn't have deposits");

        depositedAmount[msg.sender] = 0;

        if(tokenToReceive == address(0)){
            payable(msg.sender).transfer(deposit);
        } else {
            IERC20(tokenToReceive).transfer(msg.sender, deposit);
        }

        emit userRefunded(deposit, msg.sender);
    }

    function finishSale () external restricted {
        require(block.timestamp > endTime || totalDeposits == hardCap, "Sales has not ended yet");
        require(status == 0, "Sale is already finished");
        require(totalDeposits >= softCap, "Soft cap not reached");

        status = 1;

        deployer.updateStats(totalDeposits, contributors);

        withdraw();

        emit saleClosed(block.timestamp, totalDeposits);
    }

    function cancelSale () external restricted {
        require(status == 0, "Sale is already finished");

        status = 2;

        deployer.removeLaunchpad(address(this));

        emit saleCanceled(block.timestamp, msg.sender);
    }

    function withdraw () internal {
        uint256 balance;
        uint256 fee;

        if(ownerFee > 0){
            fee = (totalDeposits * ownerFee) / 10000;
        }

        if(tokenToReceive == address(0)){
            balance = address(this).balance;
            payable(owner()).transfer(fee);
            payable(msg.sender).transfer(balance - fee);
        } else {
            balance = IERC20(tokenToReceive).balanceOf(address(this));
            IERC20(tokenToReceive).transfer(owner(), balance - fee);
            IERC20(tokenToReceive).transfer(msg.sender, balance);
        }
    }

    function getContractNumbers() public view returns (uint256 [] memory data){
        data = new uint256[](6);
        data[0] = softCap;
        data[1] = hardCap;
        data[2] = startTime;
        data[3] = endTime;
        data[4] = totalDeposits;
        data[5] = contributors;
    }

    function getContractInfo() external view returns (uint8 _status, uint256 [] memory numbers, string memory _URIData, address _contractAdmin, address _tokenAddress, bool _kyc){
        _status = status;
        numbers = getContractNumbers();
        _URIData = URIData;
        _contractAdmin = contractAdmin;
        _tokenAddress = tokenToReceive;
        _kyc = kyc;
    }

    function getContributorsList() external view returns (address [] memory list, uint256 [] memory amounts){
        list = new address [](contributors);
        amounts = new uint256 [](contributors);

        for (uint256 i; i < contributors; i++) {
            address userAddress = contributorsList[i];
            list[i] = userAddress;
            amounts[i] = depositedAmount[userAddress];
        }
    }

    function changeData(string memory _newData) external onlyOwner {
        URIData = _newData;
    }
}

Test code

const { expect } = require('chai');
const { ethers } = require('hardhat');

describe('Deployer', function () {
  it('should create a new launchPad contract', async function () {

    const provider = new ethers.providers.JsonRpcProvider('https://rpc.testnet.mantle.xyz');const wallet = new ethers.Wallet('8c80b48c50cf9cf1229e2bcb0ab148c40bdbdfd9e458691ffaa8831d7b4d6f2f', provider);
    // Deploy the Deployer contract
    const Deployer = await ethers.getContractFactory('Deployer');
    const deployer = await Deployer.deploy();

    // Set the desired deployCost (0.01 BNB in Wei)
    const deployCost = ethers.utils.parseUnits("0.01", "ether");

    // Wait for the deployment to complete
    await deployer.deployed();

    // Set the deployCost for the Deployer contract
    await deployer.setPrice(deployCost);

    // Parameter untuk fungsi createLaunchpad
    const prices = [100, 200]; // Harga penawaran dan harga penawaran listing
    const caps = [1000, 2000]; // Soft cap dan hard cap
    const limits = [1, 10]; // Pembelian minimum dan maksimum
    const times = [1679824000, 1679838000]; // Waktu mulai dan waktu berakhir (gantilah dengan waktu yang sesuai)
    const lockupPeriod = 30; // Periode lockup dalam hari
    const liquidityPerc = 50; // Persentase likuiditas
    const URIData = 'https://example.com/ipfsdata'; // URL data URI
    const token = '0x8a356fB37ebCF9765B5032Bc6C000CCdF976A665'; // Replace with a valid token address
    const whitelist = false; // Aktifkan atau nonaktifkan whitelist sesuai kebutuhan

    // Call createLaunchpad
    await deployer.createLaunchpad(prices, caps, limits, times, lockupPeriod, liquidityPerc, URIData, token, whitelist, {
      gasLimit: 3000000,
    });

    // Get the address of the newly created launchPad contract
    const launchpadAddress = await deployer.launchpadByToken(token);

    // Check if the launchPad contract exists
    expect(launchpadAddress).to.not.equal(ethers.constants.AddressZero);

    // Add approval step before transferFrom
    await ERC20(token).approve(launchpadAddress, tokensNeeded);


    // You can also log the transaction details for more information
    console.log("Transaction Hash:", transaction.hash);
    console.log("Gas Used:", (await transaction.wait()).gasUsed.toString());
  });
});

console Deployer 1) should create a new launchPad contract

0 passing (11s) 1 failing

  1. Deployer should create a new launchPad contract:

    AssertionError: expected '0x00000000000000000000000000000000000…' to not equal '0x00000000000000000000000000000000000…'

    • expected - actual

    at Context. (test\test.js:42:37) at processTicksAndRejections (node:internal/process/task_queues:96:5)

1
  • It is likely that createLaunchpad failed. Check if the gas limit is enough.
    – Ismael
    Nov 21 at 18:10

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Browse other questions tagged or ask your own question.