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
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)
createLaunchpad
failed. Check if the gas limit is enough.