forked from crescent-network/crescent
-
Notifications
You must be signed in to change notification settings - Fork 0
/
plan.go
142 lines (130 loc) · 4.28 KB
/
plan.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package types
import (
"fmt"
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
utils "github.com/crescent-network/crescent/v5/types"
)
const day = 24 * time.Hour
// NewPlan creates a new Plan.
func NewPlan(
id uint64, description string, farmingPoolAddr, termAddr sdk.AccAddress,
rewardAllocs []RewardAllocation, startTime, endTime time.Time,
isPrivate bool) Plan {
return Plan{
Id: id,
Description: description,
FarmingPoolAddress: farmingPoolAddr.String(),
TerminationAddress: termAddr.String(),
RewardAllocations: rewardAllocs,
StartTime: startTime,
EndTime: endTime,
IsPrivate: isPrivate,
IsTerminated: false,
}
}
// IsActiveAt returns whether the plan is active(being able to distribute rewards)
// at given time t.
func (plan Plan) IsActiveAt(t time.Time) bool {
return !plan.StartTime.After(t) && plan.EndTime.After(t)
}
func (plan Plan) Validate() error {
if plan.Id == 0 {
return fmt.Errorf("plan id must be positive")
}
if len(plan.Description) > MaxPlanDescriptionLen {
return fmt.Errorf("too long plan description, maximum %d", MaxPlanDescriptionLen)
}
if _, err := sdk.AccAddressFromBech32(plan.FarmingPoolAddress); err != nil {
return fmt.Errorf("invalid farming pool address: %w", err)
}
if _, err := sdk.AccAddressFromBech32(plan.TerminationAddress); err != nil {
return fmt.Errorf("invalid termination address: %w", err)
}
if !plan.IsPrivate {
if plan.FarmingPoolAddress != plan.TerminationAddress {
return fmt.Errorf(
"farming pool address and termination address of a public plan must be same: %s != %s",
plan.FarmingPoolAddress, plan.TerminationAddress)
}
}
if err := ValidateRewardAllocations(plan.RewardAllocations); err != nil {
return fmt.Errorf("invalid reward allocations: %w", err)
}
if !plan.StartTime.Before(plan.EndTime) {
return fmt.Errorf("end time must be after start time")
}
return nil
}
func (plan Plan) GetFarmingPoolAddress() sdk.AccAddress {
addr, err := sdk.AccAddressFromBech32(plan.FarmingPoolAddress)
if err != nil {
panic(err)
}
return addr
}
func (plan Plan) GetTerminationAddress() sdk.AccAddress {
addr, err := sdk.AccAddressFromBech32(plan.TerminationAddress)
if err != nil {
panic(err)
}
return addr
}
// NewPairRewardAllocation creates a new RewardAllocation for a pair.
func NewPairRewardAllocation(pairId uint64, rewardsPerDay sdk.Coins) RewardAllocation {
return RewardAllocation{
PairId: pairId,
RewardsPerDay: rewardsPerDay,
}
}
// NewDenomRewardAllocation creates a new RewardAllocation for a target denom.
func NewDenomRewardAllocation(denom string, rewardsPerDay sdk.Coins) RewardAllocation {
return RewardAllocation{
Denom: denom,
RewardsPerDay: rewardsPerDay,
}
}
// ValidateRewardAllocations validates a slice of RewardAllocation.
// It also checks whether there's any duplication of pair id among the
// reward allocations.
func ValidateRewardAllocations(rewardAllocs []RewardAllocation) error {
if len(rewardAllocs) == 0 {
return fmt.Errorf("empty reward allocations")
}
denomSet := map[string]struct{}{}
pairIdSet := map[uint64]struct{}{}
for _, rewardAlloc := range rewardAllocs {
if rewardAlloc.Denom == "" && rewardAlloc.PairId == 0 {
return fmt.Errorf("target denom or pair id must be specified")
} else if rewardAlloc.Denom != "" && rewardAlloc.PairId != 0 {
return fmt.Errorf("target denom and pair id cannot be specified together")
}
if rewardAlloc.Denom != "" {
if err := sdk.ValidateDenom(rewardAlloc.Denom); err != nil {
return fmt.Errorf("invalid target denom: %w", err)
}
if _, ok := denomSet[rewardAlloc.Denom]; ok {
return fmt.Errorf("duplicate target denom: %s", rewardAlloc.Denom)
}
denomSet[rewardAlloc.Denom] = struct{}{}
} else if rewardAlloc.PairId > 0 {
if _, ok := pairIdSet[rewardAlloc.PairId]; ok {
return fmt.Errorf("duplicate pair id: %d", rewardAlloc.PairId)
}
pairIdSet[rewardAlloc.PairId] = struct{}{}
}
if err := rewardAlloc.RewardsPerDay.Validate(); err != nil {
return fmt.Errorf("invalid rewards per day: %w", err)
}
overflow := false
utils.SafeMath(func() {
RewardsForBlock(rewardAlloc.RewardsPerDay, day)
}, func() {
overflow = true
})
if overflow {
return fmt.Errorf("too much rewards per day")
}
}
return nil
}