Repository: CryptoBlades/cryptoblades
File: contracts/Launchpad.sol (Solidity)

Code before:
        else {
            maxAllocation = getLaunchAllocationForTier(launchId, getTierForStakedAmount(launchUserStakedAmountSnapshot[launchId][user]));
        }
        maxAllocation += launchUserUnclaimedSkillCommittedValue[launchId][user].mul(vars[VAR_UNCLAIMED_TO_ALLOCATION_MULTIPLIER]);
        maxAllocation = maxAllocation.sub(launchUserInvestment[launchId][user]);
    }

    function getAvailableClaimAmount(uint256 launchId, uint256 vestingId) public onlyPhase1(launchId) view returns (uint256 claimAmount) {
        uint256 decimals = ERC20(launchTokenAddress[launchId]).decimals();
        uint256 totalUserInvestment = launchUserInvestment[launchId][msg.sender].add(launchUserInvestment[launchId + 1][msg.sender]);
        claimAmount = totalUserInvestment.mul(1e18).div(launchTokenPrice[launchId]).mul(launchPeriodicVestingsPercentages[launchId][vestingId]).div(100);
        claimAmount = Common.adjustDecimals(claimAmount, decimals);
    }

    function getLinearClaimAmount(uint256 launchId) public onlyPhase1(launchId) view returns (uint256 claimAmount) {
        uint256 decimals = ERC20(launchTokenAddress[launchId]).decimals();
        uint256 totalUserInvestment = launchUserInvestment[launchId][msg.sender].add(launchUserInvestment[launchId + 1][msg.sender]);
        uint256 lastClaimTimestamp = userLinearVestingClaimTimestamp[msg.sender][launchId];
        if(lastClaimTimestamp == 0) {
            lastClaimTimestamp = launchLinearVestingsStartTimestamps[launchId];
        }
        claimAmount = totalUserInvestment.mul(1e18).div(launchTokenPrice[launchId]).mul(block.timestamp - lastClaimTimestamp).div(launchLinearVestingsDurations[launchId]);
        claimAmount = Common.adjustDecimals(claimAmount, decimals);
    }

    function getLaunchBaseInfo(uint256 launchId) public view returns (
        string memory name,
        string memory tokenSymbol,
        string memory detailsJsonUri,
        string memory imageUrl,
        address fundingTokenAddress,
        uint256 phase) {
        Launch memory l = launches[launchId];
        name = l.name;
        tokenSymbol= l.tokenSymbol;
        detailsJsonUri = l.detailsJsonUri;
        imageUrl = l.imageUrl;
        fundingTokenAddress = l.fundingTokenAddress;
        phase = l.phase;
    }

    function getLaunchDetails(uint256 launchId) public view returns (
        uint256 tokenPrice,
        uint256 startTime,
        uint256 fundsToRaise,
        uint256 maxAllocation,
        uint256 totalRaised,
        address tokenAddress) 
    {
        tokenPrice = launchTokenPrice[launchId];
        startTime = launchStartTime[launchId];

Review comment (suggestion):
I believe the launches mapping can be queried directly to return the struct as an array of values.
This can save contract space if needed, but there is no reason to change this otherwise (i think)

Example with quests struct:
frontend/src/store/quests/index.ts line 47
https://github.com/CryptoBlades/cryptoblades/blob/main/frontend/src/store/quests/index.ts
referencing SimpleQuests.sol struct mapping at line 94
https://github.com/CryptoBlades/cryptoblades/blob/main/contracts/SimpleQuests.sol

Code after:
        }
        else {
            maxAllocation = getLaunchAllocationForTier(launchId, getTierForStakedAmount(launchUserStakedAmountSnapshot[launchId][user]));
        }
        maxAllocation += launchUserUnclaimedSkillCommittedValue[launchId][user].mul(vars[VAR_UNCLAIMED_TO_ALLOCATION_MULTIPLIER]);
        maxAllocation = maxAllocation.sub(launchUserInvestment[launchId][user]);
    }

    function getAvailableClaimAmount(uint256 launchId, uint256 vestingId) public onlyPhase1(launchId) view returns (uint256 claimAmount) {
        uint256 decimals = ERC20(launchTokenAddress[launchId]).decimals();
        uint256 totalUserInvestment = launchUserInvestment[launchId][msg.sender].add(launchUserInvestment[launchId + 1][msg.sender]);
        claimAmount = totalUserInvestment.mul(1e18).div(launchTokenPrice[launchId]).mul(launchPeriodicVestingsPercentages[launchId][vestingId]).div(100);
        claimAmount = Common.adjustDecimals(claimAmount, decimals);
    }

    function getLinearClaimAmount(uint256 launchId) public onlyPhase1(launchId) view returns (uint256 claimAmount) {
        uint256 decimals = ERC20(launchTokenAddress[launchId]).decimals();
        uint256 totalUserInvestment = launchUserInvestment[launchId][msg.sender].add(launchUserInvestment[launchId + 1][msg.sender]);
        uint256 lastClaimTimestamp = userLinearVestingClaimTimestamp[msg.sender][launchId];
        if(lastClaimTimestamp == 0) {
            lastClaimTimestamp = launchLinearVestingsStartTimestamps[launchId];
        }
        claimAmount = totalUserInvestment.mul(1e18).div(launchTokenPrice[launchId]).mul(block.timestamp - lastClaimTimestamp).div(launchLinearVestingsDurations[launchId]);
        claimAmount = Common.adjustDecimals(claimAmount, decimals);
    }

    function getLaunchDetails(uint256 launchId) public view returns (
        uint256 tokenPrice,
        uint256 startTime,
        uint256 fundsToRaise,
        uint256 maxAllocation,
        uint256 totalRaised,
        address tokenAddress) 
    {
        tokenPrice = launchTokenPrice[launchId];
        startTime = launchStartTime[launchId];
        fundsToRaise = launchFundsToRaise[launchId];
        maxAllocation = getOverallMaxAllocation(launchId);
        totalRaised = launchTotalRaised[launchId];
        tokenAddress = launchTokenAddress[launchId];
    }

    function getOverallMaxAllocation(uint256 launchId) public view returns (uint256) {
        return getLaunchAllocationForTier(launchId, vars[VAR_TIERS_AMOUNT]);
    }

    function isSenderWhitelisted(uint256 launchId) public view returns (bool) {
        if(launchId % 2 == 0) launchId = launchId.sub(1);
        return launchEligibleUsersSnapshot[launchId].contains(msg.sender);
    }