code.vegaprotocol.io/vega@v0.79.0/datanode/entities/reward.go (about) 1 // Copyright (C) 2023 Gobalsky Labs Limited 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <http://www.gnu.org/licenses/>. 15 16 package entities 17 18 import ( 19 "encoding/json" 20 "fmt" 21 "strconv" 22 "time" 23 24 "code.vegaprotocol.io/vega/libs/ptr" 25 v2 "code.vegaprotocol.io/vega/protos/data-node/api/v2" 26 "code.vegaprotocol.io/vega/protos/vega" 27 eventspb "code.vegaprotocol.io/vega/protos/vega/events/v1" 28 29 "github.com/shopspring/decimal" 30 ) 31 32 type Reward struct { 33 PartyID PartyID 34 AssetID AssetID 35 MarketID MarketID 36 EpochID int64 37 Amount decimal.Decimal 38 QuantumAmount decimal.Decimal 39 PercentOfTotal float64 40 RewardType string 41 Timestamp time.Time 42 TxHash TxHash 43 VegaTime time.Time 44 SeqNum uint64 45 LockedUntilEpochID int64 46 GameID *GameID 47 TeamID *TeamID 48 } 49 50 type RewardTotals struct { 51 GameID GameID 52 PartyID PartyID 53 AssetID AssetID 54 MarketID MarketID 55 EpochID int64 56 TeamID TeamID 57 TotalRewards decimal.Decimal 58 TotalRewardsQuantum decimal.Decimal 59 } 60 61 func (r Reward) String() string { 62 return fmt.Sprintf("{Epoch: %v, Party: %s, Asset: %s, Amount: %v}", 63 r.EpochID, r.PartyID, r.AssetID, r.Amount) 64 } 65 66 func (r Reward) ToProto() *vega.Reward { 67 var gameID, teamID *string 68 if r.GameID != nil && *r.GameID != "" { 69 gameID = ptr.From(r.GameID.String()) 70 } 71 72 if r.TeamID != nil && *r.TeamID != "" { 73 teamID = ptr.From(r.TeamID.String()) 74 } 75 76 protoReward := vega.Reward{ 77 PartyId: r.PartyID.String(), 78 AssetId: r.AssetID.String(), 79 Epoch: uint64(r.EpochID), 80 Amount: r.Amount.String(), 81 QuantumAmount: r.QuantumAmount.String(), 82 PercentageOfTotal: fmt.Sprintf("%v", r.PercentOfTotal), 83 ReceivedAt: r.Timestamp.UnixNano(), 84 RewardType: r.RewardType, 85 LockedUntilEpoch: uint64(r.LockedUntilEpochID), 86 GameId: gameID, 87 TeamId: teamID, 88 } 89 return &protoReward 90 } 91 92 func (r Reward) Cursor() *Cursor { 93 cursor := RewardCursor{ 94 PartyID: r.PartyID.String(), 95 AssetID: r.AssetID.String(), 96 EpochID: r.EpochID, 97 } 98 return NewCursor(cursor.String()) 99 } 100 101 func (r Reward) ToProtoEdge(_ ...any) (*v2.RewardEdge, error) { 102 return &v2.RewardEdge{ 103 Node: r.ToProto(), 104 Cursor: r.Cursor().Encode(), 105 }, nil 106 } 107 108 func RewardFromProto(pr eventspb.RewardPayoutEvent, txHash TxHash, vegaTime time.Time, seqNum uint64) (Reward, error) { 109 epochID, err := strconv.ParseInt(pr.EpochSeq, 10, 64) 110 if err != nil { 111 return Reward{}, fmt.Errorf("could not parse epoch %q: %w", pr.EpochSeq, err) 112 } 113 114 percentOfTotal, err := strconv.ParseFloat(pr.PercentOfTotalReward, 64) 115 if err != nil { 116 return Reward{}, fmt.Errorf("could not parse percent of total reward %q: %w", pr.PercentOfTotalReward, err) 117 } 118 119 amount, err := decimal.NewFromString(pr.Amount) 120 if err != nil { 121 return Reward{}, fmt.Errorf("could not parse amount of reward %q: %w", pr.Amount, err) 122 } 123 124 quantumAmount, err := decimal.NewFromString(pr.QuantumAmount) 125 if err != nil { 126 return Reward{}, fmt.Errorf("could not parse the amount of reward %q: %w", pr.QuantumAmount, err) 127 } 128 129 lockedUntilEpochID := epochID 130 if len(pr.LockedUntilEpoch) > 0 { 131 lockedUntilEpochID, err = strconv.ParseInt(pr.LockedUntilEpoch, 10, 64) 132 if err != nil { 133 return Reward{}, fmt.Errorf("parsing locked until epoch %q: %w", pr.LockedUntilEpoch, err) 134 } 135 } 136 137 var gameID *GameID 138 if pr.GameId != nil { 139 gameID = ptr.From(GameID(*pr.GameId)) 140 } 141 142 reward := Reward{ 143 PartyID: PartyID(pr.Party), 144 AssetID: AssetID(pr.Asset), 145 EpochID: epochID, 146 Amount: amount, 147 QuantumAmount: quantumAmount, 148 PercentOfTotal: percentOfTotal, 149 Timestamp: NanosToPostgresTimestamp(pr.Timestamp), 150 RewardType: pr.RewardType, 151 TxHash: txHash, 152 VegaTime: vegaTime, 153 SeqNum: seqNum, 154 LockedUntilEpochID: lockedUntilEpochID, 155 GameID: gameID, 156 // We are not expecting TeamID to be set in the proto from core, but the API will populate it 157 // if the reward is for a team game. 158 } 159 160 return reward, nil 161 } 162 163 type RewardCursor struct { 164 PartyID string `json:"party_id"` 165 AssetID string `json:"asset_id"` 166 EpochID int64 `json:"epoch_id"` 167 } 168 169 func (rc RewardCursor) String() string { 170 bs, err := json.Marshal(rc) 171 if err != nil { 172 // This should never happen. 173 panic(fmt.Errorf("marshalling reward cursor: %w", err)) 174 } 175 return string(bs) 176 } 177 178 func (rc *RewardCursor) Parse(cursorString string) error { 179 if cursorString == "" { 180 return nil 181 } 182 return json.Unmarshal([]byte(cursorString), rc) 183 }