Search Apps Documentation Source Content File Folder Download Copy Actions Download

assert.gno

6.44 Kb ยท 225 lines
  1package v1
  2
  3import (
  4	"strconv"
  5	"strings"
  6	"time"
  7
  8	"gno.land/p/nt/ufmt"
  9	"gno.land/r/gnoswap/common"
 10)
 11
 12const (
 13	TIMESTAMP_90DAYS  = int64(7776000)
 14	TIMESTAMP_180DAYS = int64(15552000)
 15	TIMESTAMP_365DAYS = int64(31536000)
 16
 17	MAX_UNIX_EPOCH_TIME = 253402300799 // 9999-12-31 23:59:59
 18)
 19
 20// assertIsValidAmount ensures the amount is non-negative.
 21func assertIsValidAmount(amount int64) {
 22	if amount < 0 {
 23		panic(makeErrorWithDetails(
 24			errInvalidInput,
 25			ufmt.Sprintf("amount(%d) must be positive", amount),
 26		))
 27	}
 28}
 29
 30// assertIsValidRewardAmountFormat ensures the reward amount string is formatted as "tokenPath:amount".
 31func assertIsValidRewardAmountFormat(rewardAmountStr string) {
 32	parts := strings.SplitN(rewardAmountStr, ":", 2)
 33	if len(parts) != 2 {
 34		panic(makeErrorWithDetails(
 35			errInvalidInput,
 36			ufmt.Sprintf("invalid format for SetTokenMinimumRewardAmount params: expected 'tokenPath:amount', got '%s'", rewardAmountStr),
 37		))
 38	}
 39}
 40
 41// assertIsDepositor ensures the caller is the owner of the deposit.
 42func assertIsDepositor(s *stakerV1, caller address, positionId uint64) {
 43	deposit := s.getDeposits().get(positionId)
 44	if deposit == nil {
 45		panic(makeErrorWithDetails(
 46			errDataNotFound,
 47			ufmt.Sprintf("positionId(%d) not found", positionId),
 48		))
 49	}
 50
 51	if caller != deposit.Owner() {
 52		panic(makeErrorWithDetails(
 53			errNoPermission,
 54			ufmt.Sprintf("caller(%s) is not depositor(%s)", caller.String(), deposit.Owner().String()),
 55		))
 56	}
 57}
 58
 59// assertIsNotStaked ensures the position is not already staked.
 60func assertIsNotStaked(s *stakerV1, positionId uint64) {
 61	if s.getDeposits().Has(positionId) {
 62		panic(makeErrorWithDetails(
 63			errAlreadyStaked,
 64			ufmt.Sprintf("positionId(%d) already staked", positionId),
 65		))
 66	}
 67}
 68
 69// assertIsPositionOwner ensures the caller owns the position NFT.
 70func assertIsPositionOwner(s *stakerV1, positionId uint64, caller address) {
 71	owner := s.nftAccessor.MustOwnerOf(positionIdFrom(positionId))
 72	if owner != caller {
 73		panic(makeErrorWithDetails(
 74			errNoPermission,
 75			ufmt.Sprintf("caller(%s) is not owner(%s)", caller.String(), owner.String()),
 76		))
 77	}
 78}
 79
 80// assertIsPoolExists ensures the pool exists.
 81func assertIsPoolExists(s *stakerV1, poolPath string) {
 82	if !s.poolAccessor.ExistsPoolPath(poolPath) {
 83		panic(makeErrorWithDetails(
 84			errInvalidPoolPath,
 85			ufmt.Sprintf("pool(%s) does not exist", poolPath),
 86		))
 87	}
 88}
 89
 90// assertIsValidPoolTier ensures the tier is within valid range.
 91func assertIsValidPoolTier(tier uint64) {
 92	if tier >= AllTierCount {
 93		panic(makeErrorWithDetails(
 94			errInvalidPoolTier,
 95			ufmt.Sprintf("tier(%d) must be less than %d", tier, AllTierCount),
 96		))
 97	}
 98}
 99
100// assertIsGreaterThanMinimumRewardAmount ensures the reward amount meets minimum requirements.
101func assertIsGreaterThanMinimumRewardAmount(s *stakerV1, rewardToken string, rewardAmount int64) {
102	minReward := s.getMinimumRewardAmount()
103
104	if minRewardI, found := s.store.GetTokenSpecificMinimumRewards().Get(rewardToken); found {
105		minRewardInt64, ok := minRewardI.(int64)
106		if !ok {
107			panic(makeErrorWithDetails(
108				errInvalidInput,
109				ufmt.Sprintf("rewardToken(%s) is not int64", rewardToken),
110			))
111		}
112
113		minReward = minRewardInt64
114	}
115
116	if rewardAmount < minReward {
117		panic(makeErrorWithDetails(
118			errInvalidInput,
119			ufmt.Sprintf("rewardAmount(%d) is less than minimum required amount(%d)", rewardAmount, minReward),
120		))
121	}
122}
123
124// assertIsAllowedForExternalReward ensures the token is allowed for external rewards.
125func assertIsAllowedForExternalReward(s *stakerV1, poolPath, tokenPath string) {
126	token0, token1, _ := poolPathDivide(poolPath)
127
128	if tokenPath == token0 || tokenPath == token1 {
129		return
130	}
131
132	allowed := contains(s.store.GetAllowedTokens(), tokenPath)
133	if allowed {
134		return
135	}
136
137	panic(makeErrorWithDetails(
138		errNotAllowedForExternalReward,
139		ufmt.Sprintf("tokenPath(%s) is not allowed for external reward for poolPath(%s)", tokenPath, poolPath),
140	))
141}
142
143const maxUnstakingFee = int64(1000) // 10%
144
145// assertIsValidFeeRate ensures the fee rate is within valid range (0-1000 basis points).
146func assertIsValidFeeRate(fee int64) {
147	if fee < 0 || fee > maxUnstakingFee {
148		panic(makeErrorWithDetails(
149			errInvalidUnstakingFee,
150			ufmt.Sprintf("fee(%d) must be in range 0 ~ %d", fee, maxUnstakingFee),
151		))
152	}
153}
154
155// assertIsValidIncentiveStartTime ensures the incentive starts at midnight of a future date.
156func assertIsValidIncentiveStartTime(startTimestamp int64) {
157	// must be in seconds format, not milliseconds
158	// REF: https://stackoverflow.com/a/23982005
159	numStr := strconv.Itoa(int(startTimestamp))
160
161	if len(numStr) >= 13 {
162		panic(makeErrorWithDetails(
163			errInvalidIncentiveStartTime,
164			ufmt.Sprintf("startTimestamp(%d) must be in seconds format, not milliseconds", startTimestamp),
165		))
166	}
167
168	// must be at least +1 day midnight
169	tomorrowMidnight := time.Now().AddDate(0, 0, 1).Truncate(24 * time.Hour).Unix()
170	if startTimestamp < tomorrowMidnight {
171		panic(makeErrorWithDetails(
172			errInvalidIncentiveStartTime,
173			ufmt.Sprintf("startTimestamp(%d) must be at least +1 day midnight(%d)", startTimestamp, tomorrowMidnight),
174		))
175	}
176
177	// must be midnight of the day
178	startTime := time.Unix(startTimestamp, 0)
179	if !isMidnight(startTime) {
180		panic(makeErrorWithDetails(
181			errInvalidIncentiveStartTime,
182			ufmt.Sprintf("startTime(%d = %s) must be midnight of the day", startTimestamp, startTime.String()),
183		))
184	}
185}
186
187// assertIsValidIncentiveEndTime ensures the end timestamp is within valid epoch range.
188func assertIsValidIncentiveEndTime(endTimestamp int64) {
189	if endTimestamp >= MAX_UNIX_EPOCH_TIME {
190		panic(makeErrorWithDetails(
191			errInvalidInput,
192			ufmt.Sprintf("endTimestamp(%d) cannot be later than 253402300799 (9999-12-31 23:59:59)", endTimestamp),
193		))
194	}
195}
196
197// assertIsValidIncentiveDuration ensures the duration is 90, 180, or 365 days.
198func assertIsValidIncentiveDuration(externalDuration int64) {
199	switch externalDuration {
200	case TIMESTAMP_90DAYS, TIMESTAMP_180DAYS, TIMESTAMP_365DAYS:
201		return
202	}
203
204	panic(makeErrorWithDetails(
205		errInvalidIncentiveDuration,
206		ufmt.Sprintf("externalDuration(%d) must be 90, 180, 365 days", externalDuration),
207	))
208}
209
210// isMidnight checks if a time represents midnight (00:00:00).
211func isMidnight(startTime time.Time) bool {
212	hour := startTime.Hour()
213	minute := startTime.Minute()
214	second := startTime.Second()
215
216	return hour == 0 && minute == 0 && second == 0
217}
218
219func assertIsValidUserCoinSend(tokenPath string, amount int64) {
220	if common.IsGNOTNativePath(tokenPath) {
221		common.AssertIsUserSendGNOTAmount(amount)
222	} else {
223		common.AssertIsNotHandleNativeCoin()
224	}
225}