launchpad_deposit.gno
5.95 Kb · 209 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 !projectTier.IsActivated(currentTime) {
144 return launchpad.DefaultDeposit(), nil, false, "", makeErrorWithDetails(errInactiveProject, project.ID())
145 }
146
147 depositID := lp.nextDepositID()
148 deposit := launchpad.MakeDeposit(
149 depositID,
150 project.ID(),
151 tierDuration,
152 callerAddress,
153 depositAmount,
154 currentHeight,
155 currentTime,
156 projectTier.EndTime(),
157 )
158
159 // Store a value in the deposits tree and persist later updates explicitly.
160 deposits := lp.store.GetDeposits()
161 deposits.Set(depositID, deposit)
162
163 rewardManager, err := lp.getProjectTierRewardManager(projectTier.ID())
164 if err != nil {
165 return launchpad.DefaultDeposit(), nil, false, "", err
166 }
167
168 isFirstDeposit := !isRewardManagerInitialized(rewardManager)
169
170 // update rewards before adding deposit to reward manager
171 err = updateRewardPerDepositX128(rewardManager, getTierCurrentDepositAmount(projectTier), currentTime)
172 if err != nil {
173 return launchpad.DefaultDeposit(), nil, false, "", err
174 }
175
176 // add reward state to reward manager
177 rewardState := addRewardStateByDeposit(rewardManager, deposit)
178
179 // update tier data after adding reward state to reward manager
180 depositToTier(projectTier, deposit)
181 project.SetTier(tierDuration, projectTier)
182
183 // Save the modified state back
184 if err := lp.store.SetDeposits(0, rlm, deposits); err != nil {
185 return launchpad.DefaultDeposit(), nil, false, "", err
186 }
187
188 emitUpdateLaunchpadRewardAccumulation(projectTier.ID(), rewardManager, getTierCurrentDepositAmount(projectTier))
189
190 return deposit,
191 rewardState,
192 isFirstDeposit,
193 rewardManager.DistributeAmountPerSecondX128().ToString(),
194 nil
195}
196
197// stakeGovernance stakes governance token to the project.
198func (lp *launchpadV1) stakeGovernance(_ int, rlm realm, recipient address, depositAmount int64, launchpadAddress address, callerAddress address) error {
199 gov_staker.SetAmountByProjectWallet(cross(rlm), recipient, depositAmount, true)
200
201 gns.TransferFrom(
202 cross(rlm),
203 callerAddress,
204 launchpadAddress,
205 depositAmount,
206 )
207
208 return nil
209}