code.vegaprotocol.io/vega@v0.79.0/datanode/entities/proposal.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  	"time"
    22  
    23  	"code.vegaprotocol.io/vega/libs/num"
    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  
    28  	"google.golang.org/protobuf/encoding/protojson"
    29  )
    30  
    31  type ProposalType v2.ListGovernanceDataRequest_Type
    32  
    33  var (
    34  	ProposalTypeAll                         = ProposalType(v2.ListGovernanceDataRequest_TYPE_ALL)
    35  	ProposalTypeNewMarket                   = ProposalType(v2.ListGovernanceDataRequest_TYPE_NEW_MARKET)
    36  	ProposalTypeNewAsset                    = ProposalType(v2.ListGovernanceDataRequest_TYPE_NEW_ASSET)
    37  	ProposalTypeUpdateAsset                 = ProposalType(v2.ListGovernanceDataRequest_TYPE_UPDATE_ASSET)
    38  	ProposalTypeUpdateMarket                = ProposalType(v2.ListGovernanceDataRequest_TYPE_UPDATE_MARKET)
    39  	ProposalTypeUpdateNetworkParameter      = ProposalType(v2.ListGovernanceDataRequest_TYPE_NETWORK_PARAMETERS)
    40  	ProposalTypeNewFreeform                 = ProposalType(v2.ListGovernanceDataRequest_TYPE_NEW_FREE_FORM)
    41  	ProposalTypeNewSpotMarket               = ProposalType(v2.ListGovernanceDataRequest_TYPE_NEW_SPOT_MARKET)
    42  	ProposalTypeUpdateSpotMarket            = ProposalType(v2.ListGovernanceDataRequest_TYPE_UPDATE_SPOT_MARKET)
    43  	ProposalTypeNewTransfer                 = ProposalType(v2.ListGovernanceDataRequest_TYPE_NEW_TRANSFER)
    44  	ProposalTypeCancelTransfer              = ProposalType(v2.ListGovernanceDataRequest_TYPE_CANCEL_TRANSFER)
    45  	ProposalTypeUpdateMarketState           = ProposalType(v2.ListGovernanceDataRequest_TYPE_UPDATE_MARKET_STATE)
    46  	ProposalTypeUpdateReferralProgram       = ProposalType(v2.ListGovernanceDataRequest_TYPE_UPDATE_REFERRAL_PROGRAM)
    47  	ProposalTypeUpdateVolumeDiscountProgram = ProposalType(v2.ListGovernanceDataRequest_TYPE_UPDATE_VOLUME_DISCOUNT_PROGRAM)
    48  	ProposalTypeAutomatedPurchase           = ProposalType(v2.ListGovernanceDataRequest_TYPE_NEW_AUTOMATED_PURCHASE)
    49  )
    50  
    51  func (p *ProposalType) String() string {
    52  	if p == nil {
    53  		return ""
    54  	}
    55  	switch *p {
    56  	case ProposalTypeAll:
    57  		return "all"
    58  	case ProposalTypeNewMarket:
    59  		return "newMarket"
    60  	case ProposalTypeNewAsset:
    61  		return "newAsset"
    62  	case ProposalTypeUpdateAsset:
    63  		return "updateAsset"
    64  	case ProposalTypeUpdateMarket:
    65  		return "updateMarket"
    66  	case ProposalTypeUpdateNetworkParameter:
    67  		return "updateNetworkParameter"
    68  	case ProposalTypeNewFreeform:
    69  		return "newFreeform"
    70  	case ProposalTypeNewSpotMarket:
    71  		return "newSpotMarket"
    72  	case ProposalTypeUpdateSpotMarket:
    73  		return "updateSpotMarket"
    74  	case ProposalTypeNewTransfer:
    75  		return "newTransfer"
    76  	case ProposalTypeCancelTransfer:
    77  		return "cancelTransfer"
    78  	case ProposalTypeUpdateMarketState:
    79  		return "updateMarketState"
    80  	case ProposalTypeUpdateReferralProgram:
    81  		return "updateReferralProgram"
    82  	case ProposalTypeUpdateVolumeDiscountProgram:
    83  		return "updateVolumeDiscountProgram"
    84  	case ProposalTypeAutomatedPurchase:
    85  		return "NewProtocolAutomatedPurchase"
    86  	default:
    87  		return "unknown"
    88  	}
    89  }
    90  
    91  type _Proposal struct{}
    92  
    93  type ProposalID = ID[_Proposal]
    94  
    95  type Proposal struct {
    96  	ID                      ProposalID
    97  	BatchID                 ProposalID
    98  	Reference               string
    99  	PartyID                 PartyID
   100  	State                   ProposalState
   101  	Rationale               ProposalRationale
   102  	Terms                   ProposalTerms
   103  	BatchTerms              BatchProposalTerms
   104  	Reason                  ProposalError
   105  	ErrorDetails            string
   106  	ProposalTime            time.Time
   107  	VegaTime                time.Time
   108  	RequiredMajority        num.Decimal
   109  	RequiredParticipation   num.Decimal
   110  	RequiredLPMajority      *num.Decimal
   111  	RequiredLPParticipation *num.Decimal
   112  	TxHash                  TxHash
   113  	Proposals               []Proposal
   114  }
   115  
   116  func (p Proposal) IsBatch() bool {
   117  	return p.BatchTerms.BatchProposalTerms != nil
   118  }
   119  
   120  func (p Proposal) BelongsToBatch() bool {
   121  	return len(p.BatchID) > 0
   122  }
   123  
   124  func (p Proposal) ToProto() *vega.Proposal {
   125  	var lpMajority *string
   126  	if !p.RequiredLPMajority.IsZero() {
   127  		lpMajority = toPtr(p.RequiredLPMajority.String())
   128  	}
   129  	var lpParticipation *string
   130  	if !p.RequiredLPParticipation.IsZero() {
   131  		lpParticipation = toPtr(p.RequiredLPParticipation.String())
   132  	}
   133  
   134  	var reason *vega.ProposalError
   135  	if p.Reason != ProposalErrorUnspecified {
   136  		reason = ptr.From(vega.ProposalError(p.Reason))
   137  	}
   138  
   139  	var errDetails *string
   140  	if len(p.ErrorDetails) > 0 {
   141  		errDetails = ptr.From(p.ErrorDetails)
   142  	}
   143  
   144  	var batchID *string
   145  	if len(p.BatchID) > 0 {
   146  		batchID = ptr.From(p.BatchID.String())
   147  	}
   148  
   149  	pp := vega.Proposal{
   150  		Id:                                     p.ID.String(),
   151  		BatchId:                                batchID,
   152  		Reference:                              p.Reference,
   153  		PartyId:                                p.PartyID.String(),
   154  		State:                                  vega.Proposal_State(p.State),
   155  		Rationale:                              p.Rationale.ProposalRationale,
   156  		Timestamp:                              p.ProposalTime.UnixNano(),
   157  		Terms:                                  p.Terms.ProposalTerms,
   158  		BatchTerms:                             p.BatchTerms.BatchProposalTerms,
   159  		Reason:                                 reason,
   160  		ErrorDetails:                           errDetails,
   161  		RequiredMajority:                       p.RequiredMajority.String(),
   162  		RequiredParticipation:                  p.RequiredParticipation.String(),
   163  		RequiredLiquidityProviderMajority:      lpMajority,
   164  		RequiredLiquidityProviderParticipation: lpParticipation,
   165  	}
   166  	return &pp
   167  }
   168  
   169  func (p Proposal) Cursor() *Cursor {
   170  	pc := ProposalCursor{
   171  		State:    p.State,
   172  		VegaTime: p.VegaTime,
   173  		ID:       p.ID,
   174  	}
   175  	return NewCursor(pc.String())
   176  }
   177  
   178  func (p Proposal) ToProtoEdge(_ ...any) (*v2.GovernanceDataEdge, error) {
   179  	proposalsProto := make([]*vega.Proposal, len(p.Proposals))
   180  
   181  	for i, proposal := range p.Proposals {
   182  		proposalsProto[i] = proposal.ToProto()
   183  	}
   184  
   185  	return &v2.GovernanceDataEdge{
   186  		Node: &vega.GovernanceData{
   187  			Proposal:  p.ToProto(),
   188  			Proposals: proposalsProto,
   189  		},
   190  		Cursor: p.Cursor().Encode(),
   191  	}, nil
   192  }
   193  
   194  func ProposalFromProto(pp *vega.Proposal, txHash TxHash) (Proposal, error) {
   195  	var err error
   196  	var majority num.Decimal
   197  	if len(pp.RequiredMajority) <= 0 {
   198  		majority = num.DecimalZero()
   199  	} else if majority, err = num.DecimalFromString(pp.RequiredMajority); err != nil {
   200  		return Proposal{}, err
   201  	}
   202  
   203  	var participation num.Decimal
   204  	if len(pp.RequiredParticipation) <= 0 {
   205  		participation = num.DecimalZero()
   206  	} else if participation, err = num.DecimalFromString(pp.RequiredParticipation); err != nil {
   207  		return Proposal{}, err
   208  	}
   209  
   210  	lpMajority := num.DecimalZero()
   211  	if pp.RequiredLiquidityProviderMajority != nil && len(*pp.RequiredLiquidityProviderMajority) > 0 {
   212  		if lpMajority, err = num.DecimalFromString(*pp.RequiredLiquidityProviderMajority); err != nil {
   213  			return Proposal{}, err
   214  		}
   215  	}
   216  	lpParticipation := num.DecimalZero()
   217  	if pp.RequiredLiquidityProviderParticipation != nil && len(*pp.RequiredLiquidityProviderParticipation) > 0 {
   218  		if lpParticipation, err = num.DecimalFromString(*pp.RequiredLiquidityProviderParticipation); err != nil {
   219  			return Proposal{}, err
   220  		}
   221  	}
   222  
   223  	reason := ProposalErrorUnspecified
   224  	if pp.Reason != nil {
   225  		reason = ProposalError(*pp.Reason)
   226  	}
   227  
   228  	var errDetails string
   229  	if pp.ErrorDetails != nil {
   230  		errDetails = *pp.ErrorDetails
   231  	}
   232  
   233  	var batchID ProposalID
   234  	if pp.BatchId != nil {
   235  		batchID = ProposalID(*pp.BatchId)
   236  	}
   237  
   238  	p := Proposal{
   239  		ID:                      ProposalID(pp.Id),
   240  		BatchID:                 batchID,
   241  		Reference:               pp.Reference,
   242  		PartyID:                 PartyID(pp.PartyId),
   243  		State:                   ProposalState(pp.State),
   244  		Rationale:               ProposalRationale{pp.Rationale},
   245  		Terms:                   ProposalTerms{pp.GetTerms()},
   246  		BatchTerms:              BatchProposalTerms{pp.GetBatchTerms()},
   247  		Reason:                  reason,
   248  		ErrorDetails:            errDetails,
   249  		ProposalTime:            time.Unix(0, pp.Timestamp),
   250  		RequiredMajority:        majority,
   251  		RequiredParticipation:   participation,
   252  		RequiredLPMajority:      &lpMajority,
   253  		RequiredLPParticipation: &lpParticipation,
   254  		TxHash:                  txHash,
   255  	}
   256  	return p, nil
   257  }
   258  
   259  type ProposalRationale struct {
   260  	*vega.ProposalRationale
   261  }
   262  
   263  func (pt ProposalRationale) MarshalJSON() ([]byte, error) {
   264  	return protojson.Marshal(pt)
   265  }
   266  
   267  func (pt *ProposalRationale) UnmarshalJSON(b []byte) error {
   268  	pt.ProposalRationale = &vega.ProposalRationale{}
   269  	return protojson.Unmarshal(b, pt)
   270  }
   271  
   272  type ProposalTerms struct {
   273  	*vega.ProposalTerms
   274  }
   275  
   276  func (pt ProposalTerms) MarshalJSON() ([]byte, error) {
   277  	return protojson.Marshal(pt)
   278  }
   279  
   280  func (pt *ProposalTerms) UnmarshalJSON(b []byte) error {
   281  	pt.ProposalTerms = &vega.ProposalTerms{}
   282  	if err := protojson.Unmarshal(b, pt); err != nil {
   283  		return err
   284  	}
   285  
   286  	if pt.ProposalTerms.Change == nil {
   287  		pt.ProposalTerms = nil
   288  	}
   289  
   290  	return nil
   291  }
   292  
   293  type BatchProposalTerms struct {
   294  	*vega.BatchProposalTerms
   295  }
   296  
   297  func (pt BatchProposalTerms) MarshalJSON() ([]byte, error) {
   298  	return protojson.Marshal(pt)
   299  }
   300  
   301  func (pt *BatchProposalTerms) UnmarshalJSON(b []byte) error {
   302  	pt.BatchProposalTerms = &vega.BatchProposalTerms{}
   303  	if err := protojson.Unmarshal(b, pt); err != nil {
   304  		return err
   305  	}
   306  
   307  	if pt.BatchProposalTerms.Changes == nil || len(pt.BatchProposalTerms.Changes) == 0 {
   308  		pt.BatchProposalTerms = nil
   309  	}
   310  
   311  	return nil
   312  }
   313  
   314  type ProposalCursor struct {
   315  	State    ProposalState `json:"state"`
   316  	VegaTime time.Time     `json:"vega_time"`
   317  	ID       ProposalID    `json:"id"`
   318  }
   319  
   320  func (pc ProposalCursor) String() string {
   321  	bs, err := json.Marshal(pc)
   322  	if err != nil {
   323  		panic(fmt.Errorf("failed to marshal proposal cursor: %w", err))
   324  	}
   325  	return string(bs)
   326  }
   327  
   328  func (pc *ProposalCursor) Parse(cursorString string) error {
   329  	if cursorString == "" {
   330  		return nil
   331  	}
   332  	return json.Unmarshal([]byte(cursorString), pc)
   333  }
   334  
   335  func toPtr[T any](t T) *T {
   336  	return &t
   337  }