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}