Search Apps Documentation Source Content File Folder Download Copy Actions Download

launchpad_withdraw.gno

4.36 Kb · 154 lines
  1package v1
  2
  3import (
  4	"chain"
  5	"chain/runtime"
  6	"time"
  7
  8	ufmt "gno.land/p/nt/ufmt/v0"
  9
 10	"gno.land/r/gnoswap/common"
 11	"gno.land/r/gnoswap/emission"
 12	"gno.land/r/gnoswap/halt"
 13	"gno.land/r/gnoswap/launchpad"
 14
 15	gov_staker "gno.land/r/gnoswap/gov/staker"
 16)
 17
 18// CollectDepositGns withdraws the original GNS deposit after the lock period ends.
 19//
 20// Parameters:
 21//   - depositID: ID of the deposit to withdraw
 22//
 23// Returns amount withdrawn and any error.
 24// Only callable by deposit owner after tier lock period ends.
 25func (lp *launchpadV1) CollectDepositGns(_ int, rlm realm, depositID string) (int64, error) {
 26	if !rlm.IsCurrent() {
 27		return 0, errSpoofedRealm
 28	}
 29
 30	halt.AssertIsNotHaltedWithdraw()
 31
 32	previousRealm := rlm.Previous()
 33
 34	caller := previousRealm.Address()
 35	lp.assertIsDepositOwner(depositID, caller)
 36
 37	emission.MintAndDistributeGns(cross(rlm))
 38
 39	deposit := lp.mustGetDeposit(depositID)
 40	currentHeight := runtime.ChainHeight()
 41	currentTime := time.Now().Unix()
 42
 43	// Collect reward before withdrawal
 44	rewardTokenPath, rewardAmount, err := lp.collectDepositReward(0, rlm, deposit, currentHeight, currentTime)
 45	if err != nil {
 46		panic(err)
 47	}
 48
 49	recipient, withdrawalAmount, err := lp.withdrawDeposit(0, rlm, deposit, runtime.ChainHeight(), currentTime)
 50	if err != nil {
 51		panic(err.Error())
 52	}
 53
 54	// Cross-realm governance update before token transfers
 55	unStakeGovernance(0, rlm, recipient, withdrawalAmount)
 56
 57	// Transfer reward token to depositor
 58	if rewardAmount > 0 {
 59		common.SafeGRC20Transfer(cross(rlm), rewardTokenPath, deposit.Depositor(), rewardAmount)
 60
 61		chain.Emit(
 62			"CollectRewardByDepositId",
 63			"prevAddr", caller.String(),
 64			"prevRealm", previousRealm.PkgPath(),
 65			"depositId", depositID,
 66			"amount", formatInt(rewardAmount),
 67		)
 68	}
 69
 70	// Transfer the original GNS deposit back to the depositor
 71	common.SafeGRC20Transfer(cross(rlm), GNS_PATH, deposit.Depositor(), withdrawalAmount)
 72
 73	chain.Emit(
 74		"CollectDepositGns",
 75		"prevAddr", caller.String(),
 76		"prevRealm", previousRealm.PkgPath(),
 77		"depositId", depositID,
 78		"amount", formatInt(withdrawalAmount),
 79	)
 80
 81	return withdrawalAmount, nil
 82}
 83
 84// withdrawDeposit withdraws a deposit and updates the reward manager.
 85func (lp *launchpadV1) withdrawDeposit(_ int, rlm realm, deposit *launchpad.Deposit, currentHeight, currentTime int64) (address, int64, error) {
 86	// Input validation
 87	if deposit == nil {
 88		return "", 0, makeErrorWithDetails(errNotExistDeposit, "deposit is nil")
 89	}
 90
 91	if currentTime <= 0 {
 92		return "", 0, makeErrorWithDetails(errInvalidTime, "currentTime must be positive")
 93	}
 94
 95	// State validation
 96	if deposit.IsWithdrawn() {
 97		return "", 0, makeErrorWithDetails(errAlreadyCollected, ufmt.Sprintf("(%s)", deposit.ID()))
 98	}
 99
100	if !deposit.IsEnded(currentTime) {
101		return "", 0, makeErrorWithDetails(errNotYetEndedProject, ufmt.Sprintf("(%s)", deposit.ID()))
102	}
103
104	// Get project and tier information
105	project, err := lp.getProject(deposit.ProjectID())
106	if err != nil {
107		return "", 0, err
108	}
109
110	projectTier, err := getProjectTier(project, deposit.Tier())
111	if err != nil {
112		return "", 0, err
113	}
114
115	// Get reward manager and update rewards before withdrawal
116	rewardManager, err := lp.getProjectTierRewardManager(projectTier.ID())
117	if err != nil {
118		return "", 0, err
119	}
120
121	// Update rewards with current deposit amount
122	err = updateRewardPerDepositX128(rewardManager, getTierCurrentDepositAmount(projectTier), currentTime)
123	if err != nil {
124		return "", 0, err
125	}
126
127	// Process withdrawal from project tier
128	withdrawToTier(projectTier, deposit)
129	project.SetTier(deposit.Tier(), projectTier)
130
131	// Finalize withdrawal
132	withdrawalAmount := withdrawDeposit(deposit, currentHeight, currentTime)
133
134	// Remove reward state from BPTree after withdrawal
135	// This improves iteration performance for calculateClaimableRewardsForActiveDeposits
136	// and prevents accessing reward state for already withdrawn deposits
137	removeRewardState(rewardManager, deposit.ID())
138
139	// Store updated deposit in state
140	deposits := lp.store.GetDeposits()
141	deposits.Set(deposit.ID(), deposit)
142
143	// Save the modified state back
144	if err := lp.store.SetDeposits(0, rlm, deposits); err != nil {
145		return "", 0, err
146	}
147
148	return project.Recipient(), withdrawalAmount, nil
149}
150
151// unStakeGovernance removes the staked amount from governance system
152func unStakeGovernance(_ int, rlm realm, recipient address, withdrawalAmount int64) {
153	gov_staker.SetAmountByProjectWallet(cross(rlm), recipient, withdrawalAmount, false)
154}