Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

launchpad_withdraw.gno

5.47 Kb · 177 lines
  1package launchpad
  2
  3import (
  4	"chain"
  5	"chain/runtime"
  6	"time"
  7
  8	gnsmath "gno.land/p/gnoswap/gnsmath/v1"
  9	"gno.land/p/gnoswap/utils/v1"
 10	ufmt "gno.land/p/nt/ufmt/v0"
 11
 12	"gno.land/r/gnoswap/access/v1"
 13	"gno.land/r/gnoswap/common"
 14	"gno.land/r/gnoswap/emission"
 15	"gno.land/r/gnoswap/halt/v1"
 16	"gno.land/r/gnoswap/launchpad"
 17
 18	gov_staker "gno.land/r/gnoswap/gov/staker"
 19)
 20
 21// CollectDepositGns withdraws the original GNS deposit after the lock period
 22// ends. Before returning the principal, it settles any claimable project-token
 23// reward for the deposit.
 24//
 25// Parameters:
 26//   - _: Noncrossing implementation-call discriminator; pass 0.
 27//   - rlm: Current realm context forwarded by the launchpad proxy; a spoofed context panics.
 28//   - depositID: ID of the deposit to withdraw.
 29//
 30// Returns:
 31//   - amount: original GNS deposit amount returned to the depositor, in base units.
 32//   - err: always nil; withdrawal failures panic before returning.
 33//
 34// Only callable by deposit owner when the current time is strictly after the
 35// selected tier's end time.
 36func (lp *launchpadV1) CollectDepositGns(_ int, rlm realm, depositID string) (int64, error) {
 37	access.AssertIsRlmCurrent(0, rlm)
 38
 39	halt.AssertIsNotHaltedWithdraw()
 40
 41	previousRealm := rlm.Previous()
 42
 43	caller := previousRealm.Address()
 44	lp.assertIsDepositOwner(depositID, caller)
 45
 46	deposit := lp.mustGetDeposit(depositID)
 47	targetProjectTierID := deposit.ProjectTierID()
 48	currentHeight := runtime.ChainHeight()
 49	currentTime := time.Now().Unix()
 50
 51	// check deposit state
 52	assertIsDepositNotWithdrawn(deposit)
 53	assertIsDepositEnded(deposit, currentTime)
 54
 55	emission.MintAndDistributeGns(cross(rlm))
 56
 57	// Collect reward before withdrawal
 58	rewardTokenPath, rewardAmount, err := lp.collectDepositReward(0, rlm, deposit, currentHeight, currentTime)
 59	if err != nil {
 60		panic(err)
 61	}
 62
 63	beforeTotalGNSStakedAmount := lp.store.GetTotalGNSStakedAmount()
 64
 65	recipient, withdrawalAmount, err := lp.withdrawDeposit(0, rlm, deposit, runtime.ChainHeight(), currentTime)
 66	if err != nil {
 67		panic(err.Error())
 68	}
 69
 70	afterTotalGNSStakedAmount := gnsmath.SafeSubInt64(beforeTotalGNSStakedAmount, withdrawalAmount)
 71	if err := lp.store.SetTotalGNSStakedAmount(0, rlm, afterTotalGNSStakedAmount); err != nil {
 72		panic(err.Error())
 73	}
 74
 75	// Cross-realm governance update before token transfers
 76	unStakeGovernance(0, rlm, recipient, withdrawalAmount)
 77
 78	// Transfer reward token to depositor
 79	if rewardAmount > 0 {
 80		common.SafeGRC20Transfer(0, rlm, rewardTokenPath, deposit.Depositor(), rewardAmount)
 81
 82		chain.Emit(
 83			"CollectRewardByDepositId",
 84			"prevAddr", caller.String(),
 85			"prevRealm", previousRealm.PkgPath(),
 86			"depositId", depositID,
 87			"targetProjectTierId", targetProjectTierID,
 88			"amount", utils.FormatInt(rewardAmount),
 89		)
 90	}
 91
 92	// Transfer the original GNS deposit back to the depositor
 93	common.SafeGRC20Transfer(0, rlm, GNS_TOKEN_KEY, deposit.Depositor(), withdrawalAmount)
 94
 95	chain.Emit(
 96		"CollectDepositGns",
 97		"prevAddr", caller.String(),
 98		"prevRealm", previousRealm.PkgPath(),
 99		"depositId", depositID,
100		"targetProjectTierId", targetProjectTierID,
101		"amount", utils.FormatInt(withdrawalAmount),
102		"totalGNSStakedAmount", utils.FormatInt(afterTotalGNSStakedAmount),
103		"recipient", recipient.String(),
104	)
105
106	return withdrawalAmount, nil
107}
108
109// withdrawDeposit withdraws a deposit and updates the reward manager.
110func (lp *launchpadV1) withdrawDeposit(_ int, rlm realm, deposit launchpad.Deposit, currentHeight, currentTime int64) (address, int64, error) {
111	// Input validation
112	if currentTime <= 0 {
113		return "", 0, makeErrorWithDetails(errInvalidTime, "currentTime must be positive")
114	}
115
116	// State validation
117	if deposit.IsWithdrawn() {
118		return "", 0, makeErrorWithDetails(errAlreadyCollected, ufmt.Sprintf("(%s)", deposit.ID()))
119	}
120
121	if !deposit.IsEnded(currentTime) {
122		return "", 0, makeErrorWithDetails(errNotYetEndedProject, ufmt.Sprintf("(%s)", deposit.ID()))
123	}
124
125	// Get project and tier information
126	project, err := lp.getProject(deposit.ProjectID())
127	if err != nil {
128		return "", 0, err
129	}
130
131	projectTier, err := getProjectTier(project, deposit.Tier())
132	if err != nil {
133		return "", 0, err
134	}
135
136	// Get reward manager and update rewards before withdrawal
137	rewardManager, err := lp.getProjectTierRewardManager(projectTier.ID())
138	if err != nil {
139		return "", 0, err
140	}
141
142	// Update rewards with current deposit amount
143	err = updateRewardPerDepositX128(rewardManager, getTierCurrentDepositAmount(projectTier), currentTime)
144	if err != nil {
145		return "", 0, err
146	}
147
148	// Process withdrawal from project tier
149	withdrawToTier(projectTier, deposit)
150	project.SetTier(deposit.Tier(), projectTier)
151
152	// Finalize withdrawal
153	deposit, withdrawalAmount := withdrawDeposit(deposit, currentHeight, currentTime)
154
155	// Remove the reward state and its reserved active claim after withdrawal.
156	if err := removeRewardState(rewardManager, deposit.ID()); err != nil {
157		return "", 0, err
158	}
159
160	// Persist the updated value; the caller's deposit value is unchanged.
161	deposits := lp.store.GetDeposits()
162	deposits.Set(deposit.ID(), deposit)
163
164	// Save the modified state back
165	if err := lp.store.SetDeposits(0, rlm, deposits); err != nil {
166		return "", 0, err
167	}
168
169	emitUpdateLaunchpadRewardAccumulation(projectTier.ID(), rewardManager, getTierCurrentDepositAmount(projectTier))
170
171	return project.Recipient(), withdrawalAmount, nil
172}
173
174// unStakeGovernance removes the staked amount from governance system
175func unStakeGovernance(_ int, rlm realm, recipient address, withdrawalAmount int64) {
176	gov_staker.SetAmountByProjectWallet(cross(rlm), recipient, withdrawalAmount, false)
177}