launchpad_deposit.gno
5.99 Kb · 211 lines
1package launchpad
2
3import (
4 "chain"
5 "chain/runtime"
6 "time"
7
8 "gno.land/r/gnoswap/access/v1"
9
10 gnsmath "gno.land/p/gnoswap/gnsmath/v1"
11 "gno.land/p/gnoswap/utils/v1"
12
13 "gno.land/r/gnoswap/common"
14 "gno.land/r/gnoswap/emission"
15 "gno.land/r/gnoswap/gns"
16 gov_staker "gno.land/r/gnoswap/gov/staker"
17 "gno.land/r/gnoswap/gov/xgns"
18 "gno.land/r/gnoswap/halt/v1"
19 "gno.land/r/gnoswap/launchpad"
20 "gno.land/r/gnoswap/referral/v1"
21)
22
23// DepositGns deposits GNS tokens to a launchpad project tier.
24//
25// Parameters:
26// - _: leading integer discriminator for the forwarded realm call; callers pass 0.
27// - rlm: propagated realm context; the implementation validates it is the current realm.
28// - targetProjectTierID: project-tier identifier in `{projectID}:{tierDuration}` form.
29// - depositAmount: amount of GNS to deposit into the tier.
30// - referrer: optional referral address associated with the deposit.
31//
32// Returns:
33// - depositID: identifier assigned to the newly created deposit.
34func (lp *launchpadV1) DepositGns(_ int, rlm realm, targetProjectTierID string, depositAmount int64, referrer string) string {
35 access.AssertIsRlmCurrent(0, rlm)
36
37 halt.AssertIsNotHaltedLaunchpad()
38
39 previousRealm := rlm.Previous()
40
41 assertIsValidAmount(depositAmount)
42
43 emission.MintAndDistributeGns(cross(rlm))
44
45 projectID, tierDuration := parseProjectTierID(targetProjectTierID)
46 caller := previousRealm.Address()
47
48 project, err := lp.getProject(projectID)
49 if err != nil {
50 panic(err.Error())
51 }
52
53 beforeTotalGNSStakedAmount := lp.store.GetTotalGNSStakedAmount()
54
55 deposit, rewardState, isFirstDeposit, distributeAmountPerSecondX128, err := lp.depositGns(
56 0,
57 rlm,
58 project,
59 tierDuration,
60 depositAmount,
61 caller,
62 )
63 if err != nil {
64 panic(err.Error())
65 }
66
67 afterTotalGNSStakedAmount := gnsmath.SafeAddInt64(beforeTotalGNSStakedAmount, depositAmount)
68 if err := lp.store.SetTotalGNSStakedAmount(0, rlm, afterTotalGNSStakedAmount); err != nil {
69 panic(err.Error())
70 }
71
72 actualReferrer := referral.TryRegister(cross(rlm), caller, referrer)
73
74 if isFirstDeposit {
75 chain.Emit(
76 "FirstDepositForProjectTier",
77 "prevAddr", previousRealm.Address().String(),
78 "prevRealm", previousRealm.PkgPath(),
79 "targetProjectTierId", targetProjectTierID,
80 "amount", utils.FormatInt(depositAmount),
81 "depositId", deposit.ID(),
82 "claimableTime", utils.FormatInt(rewardState.ClaimableTime()),
83 "tierAmountPerSecondX128", distributeAmountPerSecondX128,
84 "totalGNSStakedAmount", utils.FormatInt(afterTotalGNSStakedAmount),
85 "recipient", project.Recipient().String(),
86 )
87 }
88
89 chain.Emit(
90 "DepositGns",
91 "prevAddr", previousRealm.Address().String(),
92 "prevRealm", previousRealm.PkgPath(),
93 "targetProjectTierId", targetProjectTierID,
94 "amount", utils.FormatInt(depositAmount),
95 "depositId", deposit.ID(),
96 "claimableTime", utils.FormatInt(rewardState.ClaimableTime()),
97 "referrer", actualReferrer,
98 "totalGNSStakedAmount", utils.FormatInt(afterTotalGNSStakedAmount),
99 "recipient", project.Recipient().String(),
100 )
101
102 launchpadAddress := rlm.Address()
103
104 // stake governance token to the project
105 err = lp.stakeGovernance(0, rlm, project.Recipient(), depositAmount, launchpadAddress, caller)
106 if err != nil {
107 panic(err.Error())
108 }
109
110 return deposit.ID()
111}
112
113// depositGns deposits GNS to a project tier.
114func (lp *launchpadV1) depositGns(
115 _ int,
116 rlm realm,
117 project *launchpad.Project,
118 tierDuration int64,
119 depositAmount int64,
120 callerAddress address,
121) (launchpad.Deposit, *launchpad.RewardState, bool, string, error) {
122 balanceOfFn := func(tokenPath string, caller address) int64 {
123 if tokenPath == GOV_XGNS_PATH {
124 return xgns.BalanceOf(caller)
125 }
126
127 return common.BalanceOf(tokenPath, caller)
128 }
129
130 err := checkProjectConditions(project, callerAddress, balanceOfFn)
131 if err != nil {
132 return launchpad.DefaultDeposit(), nil, false, "", err
133 }
134
135 projectTier, err := getProjectTier(project, tierDuration)
136 if err != nil {
137 return launchpad.DefaultDeposit(), nil, false, "", err
138 }
139
140 currentTime := time.Now().Unix()
141 currentHeight := runtime.ChainHeight()
142
143 if !isTierDepositable(projectTier, currentTime) {
144 return launchpad.DefaultDeposit(), nil, false, "", makeErrorWithDetails(errInactiveProject, project.ID())
145 }
146
147 assertHasTierReward(projectTier)
148
149 depositID := lp.nextDepositID()
150 deposit := launchpad.MakeDeposit(
151 depositID,
152 project.ID(),
153 tierDuration,
154 callerAddress,
155 depositAmount,
156 currentHeight,
157 currentTime,
158 projectTier.EndTime(),
159 )
160
161 // Store a value in the deposits tree and persist later updates explicitly.
162 deposits := lp.store.GetDeposits()
163 deposits.Set(depositID, deposit)
164
165 rewardManager, err := lp.getProjectTierRewardManager(projectTier.ID())
166 if err != nil {
167 return launchpad.DefaultDeposit(), nil, false, "", err
168 }
169
170 isFirstDeposit := !isRewardManagerInitialized(rewardManager)
171
172 // update rewards before adding deposit to reward manager
173 err = updateRewardPerDepositX128(rewardManager, getTierCurrentDepositAmount(projectTier), currentTime)
174 if err != nil {
175 return launchpad.DefaultDeposit(), nil, false, "", err
176 }
177
178 // add reward state to reward manager
179 rewardState := addRewardStateByDeposit(rewardManager, deposit)
180
181 // update tier data after adding reward state to reward manager
182 depositToTier(projectTier, deposit)
183 project.SetTier(tierDuration, projectTier)
184
185 // Save the modified state back
186 if err := lp.store.SetDeposits(0, rlm, deposits); err != nil {
187 return launchpad.DefaultDeposit(), nil, false, "", err
188 }
189
190 emitUpdateLaunchpadRewardAccumulation(projectTier.ID(), rewardManager, getTierCurrentDepositAmount(projectTier))
191
192 return deposit,
193 rewardState,
194 isFirstDeposit,
195 rewardManager.DistributeAmountPerSecondX128().ToString(),
196 nil
197}
198
199// stakeGovernance stakes governance token to the project.
200func (lp *launchpadV1) stakeGovernance(_ int, rlm realm, recipient address, depositAmount int64, launchpadAddress address, callerAddress address) error {
201 gov_staker.SetAmountByProjectWallet(cross(rlm), recipient, depositAmount, true)
202
203 gns.TransferFrom(
204 cross(rlm),
205 callerAddress,
206 launchpadAddress,
207 depositAmount,
208 )
209
210 return nil
211}