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