launchpad_reward.gno

package launchpad

import (
	"chain"
	"chain/runtime"
	"errors"
	"time"

	gnsmath "gno.land/p/gnoswap/gnsmath"
	"gno.land/p/gnoswap/utils"

	"gno.land/r/gnoswap/common"
	"gno.land/r/gnoswap/halt"
	"gno.land/r/gnoswap/launchpad"
)

// CollectRewardByDepositId collects reward from a specific deposit.
//
// Parameters:
//   - depositID: ID of the deposit to collect from
//
// Returns amount of reward collected.
// Only callable by deposit owner.
func (lp *launchpadV1) CollectRewardByDepositId(_ int, rlm realm, depositID string) int64 {
	if !rlm.IsCurrent() {
		panic(errors.New(errSpoofedRealm))
	}

	halt.AssertIsNotHaltedWithdraw()

	previousRealm := rlm.Previous()

	caller := previousRealm.Address()
	lp.assertIsDepositOwner(depositID, caller)

	deposit := lp.mustGetDeposit(depositID)
	targetProjectTierID := deposit.ProjectTierID()
	currentHeight := runtime.ChainHeight()
	currentTime := time.Now().Unix()

	rewardTokenPath, rewardAmount, err := lp.collectDepositReward(0, rlm, deposit, currentHeight, currentTime)
	if err != nil {
		panic(err)
	}

	// Transfer reward token to depositor
	if rewardAmount > 0 {
		common.SafeGRC20Transfer(cross(rlm), rewardTokenPath, deposit.Depositor(), rewardAmount)
	}

	chain.Emit(
		"CollectRewardByDepositId",
		"prevAddr", caller.String(),
		"prevRealm", previousRealm.PkgPath(),
		"depositId", depositID,
		"targetProjectTierId", targetProjectTierID,
		"amount", utils.FormatInt(rewardAmount),
	)

	return rewardAmount
}

// collectDepositReward calculates and collects the reward for a deposit.
func (lp *launchpadV1) collectDepositReward(_ int, rlm realm, deposit *launchpad.Deposit, currentHeight, currentTime int64) (string, int64, error) {
	if currentTime <= 0 {
		return "", 0, makeErrorWithDetails(errInvalidTime, "currentTime must be positive")
	}

	// Get project and tier data
	project, err := lp.getProject(deposit.ProjectID())
	if err != nil {
		return "", 0, err
	}

	projectTier, err := getProjectTier(project, deposit.Tier())
	if err != nil {
		return "", 0, err
	}

	// Get reward manager
	rewardManager, err := lp.getProjectTierRewardManager(projectTier.ID())
	if err != nil {
		return "", 0, err
	}

	// Update reward state before collection
	err = updateRewardPerDepositX128(rewardManager, getTierCurrentDepositAmount(projectTier), currentTime)
	if err != nil {
		return "", 0, err
	}

	// Collect reward
	rewardAmount, err := collectReward(rewardManager, deposit.ID(), currentTime)
	if err != nil {
		return "", 0, err
	}

	// Update total collected amount for the tier
	if rewardAmount > 0 {
		projectTier.SetTotalCollectedAmount(gnsmath.SafeAddInt64(projectTier.TotalCollectedAmount(), rewardAmount))
		project.SetTier(deposit.Tier(), projectTier)

		projects := lp.store.GetProjects()
		if err := lp.store.SetProjects(0, rlm, projects); err != nil {
			return "", 0, err
		}
	}

	emitUpdateLaunchpadRewardAccumulation(projectTier.ID(), rewardManager, getTierCurrentDepositAmount(projectTier))

	return project.TokenPath(), rewardAmount, nil
}