github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/tequilapi/contract/transactor.go (about)

     1  /*
     2   * Copyright (C) 2020 The "MysteriumNetwork/node" Authors.
     3   *
     4   * This program is free software: you can redistribute it and/or modify
     5   * it under the terms of the GNU General Public License as published by
     6   * the Free Software Foundation, either version 3 of the License, or
     7   * (at your option) any later version.
     8   *
     9   * This program is distributed in the hope that it will be useful,
    10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12   * GNU General Public License for more details.
    13   *
    14   * You should have received a copy of the GNU General Public License
    15   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    16   */
    17  
    18  package contract
    19  
    20  import (
    21  	"fmt"
    22  	"math/big"
    23  	"net/http"
    24  	"time"
    25  
    26  	"github.com/mysteriumnetwork/go-rest/apierror"
    27  	"github.com/mysteriumnetwork/node/core/beneficiary"
    28  	"github.com/mysteriumnetwork/node/identity/registry"
    29  	"github.com/mysteriumnetwork/payments/crypto"
    30  
    31  	"github.com/ethereum/go-ethereum/common"
    32  	"github.com/go-openapi/strfmt"
    33  	"github.com/mysteriumnetwork/node/identity"
    34  	"github.com/mysteriumnetwork/node/session/pingpong"
    35  	"github.com/mysteriumnetwork/node/tequilapi/utils"
    36  )
    37  
    38  // FeesDTO represents the transactor fees
    39  // swagger:model FeesDTO
    40  type FeesDTO struct {
    41  	Registration       *big.Int `json:"registration"`
    42  	RegistrationTokens Tokens   `json:"registration_tokens"`
    43  	Settlement         *big.Int `json:"settlement"`
    44  	SettlementTokens   Tokens   `json:"settlement_tokens"`
    45  	// deprecated - confusing name
    46  	Hermes              uint16   `json:"hermes"`
    47  	HermesPercent       string   `json:"hermes_percent"`
    48  	DecreaseStake       *big.Int `json:"decreaseStake"`
    49  	DecreaseStakeTokens Tokens   `json:"decrease_stake_tokens"`
    50  }
    51  
    52  // CombinedFeesResponse represents transactor fees.
    53  // swagger:model CombinedFeesResponse
    54  type CombinedFeesResponse struct {
    55  	Current TransactorFees `json:"current"`
    56  	Last    TransactorFees `json:"last"`
    57  
    58  	ServerTime    time.Time `json:"server_time"`
    59  	HermesPercent string    `json:"hermes_percent"`
    60  }
    61  
    62  // TransactorFees represents transactor fees.
    63  // swagger:model TransactorFees
    64  type TransactorFees struct {
    65  	Registration  Tokens `json:"registration"`
    66  	Settlement    Tokens `json:"settlement"`
    67  	DecreaseStake Tokens `json:"decrease_stake"`
    68  
    69  	ValidUntil time.Time `json:"valid_until"`
    70  }
    71  
    72  // NewTransactorFees converts registry fees to public api.
    73  func NewTransactorFees(r *registry.Fees) TransactorFees {
    74  	return TransactorFees{
    75  		Registration:  NewTokens(r.Register),
    76  		Settlement:    NewTokens(r.Settle),
    77  		DecreaseStake: NewTokens(r.DecreaseStake),
    78  		ValidUntil:    r.ValidUntil,
    79  	}
    80  }
    81  
    82  // NewSettlementListQuery creates settlement list query with default values.
    83  func NewSettlementListQuery() SettlementListQuery {
    84  	return SettlementListQuery{
    85  		PaginationQuery: NewPaginationQuery(),
    86  	}
    87  }
    88  
    89  // SettlementListQuery allows to filter requested settlements.
    90  // swagger:parameters settlementList
    91  type SettlementListQuery struct {
    92  	PaginationQuery
    93  
    94  	// Filter the settlements from this date. Formatted in RFC3339 e.g. 2020-07-01.
    95  	// in: query
    96  	DateFrom *strfmt.Date `json:"date_from"`
    97  
    98  	// Filter the settlements until this date Formatted in RFC3339 e.g. 2020-07-30.
    99  	// in: query
   100  	DateTo *strfmt.Date `json:"date_to"`
   101  
   102  	// Provider identity to filter the sessions by.
   103  	// in: query
   104  	ProviderID *string `json:"provider_id"`
   105  
   106  	// Hermes ID to filter the sessions by.
   107  	// in: query
   108  	HermesID *string `json:"hermes_id"`
   109  
   110  	// Settlement type to filter the sessions by. "settlement" or "withdrawal"
   111  	// in: query
   112  	Types []string `json:"types"`
   113  }
   114  
   115  // Bind creates and validates query from API request.
   116  func (q *SettlementListQuery) Bind(request *http.Request) *apierror.APIError {
   117  	v := apierror.NewValidator()
   118  	if err := q.PaginationQuery.Bind(request); err != nil {
   119  		for field, fieldErr := range err.Err.Fields {
   120  			v.Fail(field, fieldErr.Code, fieldErr.Message)
   121  		}
   122  	}
   123  
   124  	qs := request.URL.Query()
   125  	if qStr := qs.Get("date_from"); qStr != "" {
   126  		if qVal, err := parseDate(qStr); err != nil {
   127  			v.Invalid("date_from", "Could not parse 'date_from'")
   128  		} else {
   129  			q.DateFrom = qVal
   130  		}
   131  	}
   132  	if qStr := qs.Get("date_to"); qStr != "" {
   133  		if qVal, err := parseDate(qStr); err != nil {
   134  			v.Invalid("date_to", "Could not parse 'date_to'")
   135  		} else {
   136  			q.DateTo = qVal
   137  		}
   138  	}
   139  	if qStr := qs.Get("provider_id"); qStr != "" {
   140  		q.ProviderID = &qStr
   141  	}
   142  	if qStr := qs.Get("hermes_id"); qStr != "" {
   143  		q.HermesID = &qStr
   144  	}
   145  
   146  	if types, ok := qs["types"]; ok {
   147  		q.Types = append(q.Types, types...)
   148  	}
   149  
   150  	return v.Err()
   151  }
   152  
   153  // ToFilter converts API query to storage filter.
   154  func (q *SettlementListQuery) ToFilter() pingpong.SettlementHistoryFilter {
   155  	filter := pingpong.SettlementHistoryFilter{}
   156  	if q.DateFrom != nil {
   157  		timeFrom := time.Time(*q.DateFrom).Truncate(24 * time.Hour)
   158  		filter.TimeFrom = &timeFrom
   159  	}
   160  	if q.DateTo != nil {
   161  		timeTo := time.Time(*q.DateTo).Truncate(24 * time.Hour).Add(23 * time.Hour).Add(59 * time.Minute).Add(59 * time.Second)
   162  		filter.TimeTo = &timeTo
   163  	}
   164  	if q.ProviderID != nil {
   165  		providerID := identity.FromAddress(*q.ProviderID)
   166  		filter.ProviderID = &providerID
   167  	}
   168  	if q.HermesID != nil {
   169  		hermesID := common.HexToAddress(*q.HermesID)
   170  		filter.HermesID = &hermesID
   171  	}
   172  	if q.Types != nil {
   173  		for _, qt := range q.Types {
   174  			filter.Types = append(filter.Types, pingpong.HistoryType(qt))
   175  		}
   176  	}
   177  	return filter
   178  }
   179  
   180  // NewSettlementListResponse maps to API settlement list.
   181  func NewSettlementListResponse(
   182  	WithdrawalTotal *big.Int,
   183  	settlements []pingpong.SettlementHistoryEntry,
   184  	paginator *utils.Paginator,
   185  ) SettlementListResponse {
   186  	dtoArray := make([]SettlementDTO, len(settlements))
   187  	for i, settlement := range settlements {
   188  		dtoArray[i] = NewSettlementDTO(settlement)
   189  	}
   190  
   191  	return SettlementListResponse{
   192  		Items:           dtoArray,
   193  		PageableDTO:     NewPageableDTO(paginator),
   194  		WithdrawalTotal: WithdrawalTotal.String(),
   195  	}
   196  }
   197  
   198  // SettlementListResponse defines settlement list representable as json.
   199  // swagger:model SettlementListResponse
   200  type SettlementListResponse struct {
   201  	Items           []SettlementDTO `json:"items"`
   202  	WithdrawalTotal string          `json:"withdrawal_total"`
   203  	PageableDTO
   204  }
   205  
   206  // NewSettlementDTO maps to API settlement.
   207  func NewSettlementDTO(settlement pingpong.SettlementHistoryEntry) SettlementDTO {
   208  	return SettlementDTO{
   209  		TxHash:           settlement.TxHash.Hex(),
   210  		ProviderID:       settlement.ProviderID.Address,
   211  		HermesID:         settlement.HermesID.Hex(),
   212  		ChannelAddress:   settlement.ChannelAddress.Hex(),
   213  		Beneficiary:      settlement.Beneficiary.Hex(),
   214  		Amount:           settlement.Amount,
   215  		SettledAt:        settlement.Time.Format(time.RFC3339),
   216  		Fees:             settlement.Fees,
   217  		Error:            settlement.Error,
   218  		IsWithdrawal:     settlement.IsWithdrawal,
   219  		BlockExplorerURL: settlement.BlockExplorerURL,
   220  	}
   221  }
   222  
   223  // SettlementDTO represents the settlement object.
   224  // swagger:model SettlementDTO
   225  type SettlementDTO struct {
   226  	// example: 0x20c070a9be65355adbd2ba479e095e2e8ed7e692596548734984eab75d3fdfa5
   227  	TxHash string `json:"tx_hash"`
   228  
   229  	// example: 0x0000000000000000000000000000000000000001
   230  	ProviderID string `json:"provider_id"`
   231  
   232  	// example: 0x0000000000000000000000000000000000000001
   233  	HermesID string `json:"hermes_id"`
   234  
   235  	// example: 0x0000000000000000000000000000000000000001
   236  	ChannelAddress string `json:"channel_address"`
   237  
   238  	// example: 0x0000000000000000000000000000000000000001
   239  	Beneficiary string `json:"beneficiary"`
   240  
   241  	// example: 500000
   242  	Amount *big.Int `json:"amount"`
   243  
   244  	// example: 2019-06-06T11:04:43.910035Z
   245  	SettledAt string `json:"settled_at"`
   246  
   247  	// example: 500000
   248  	Fees *big.Int `json:"fees"`
   249  
   250  	// example: false
   251  	IsWithdrawal bool `json:"is_withdrawal"`
   252  
   253  	// example: https://example.com
   254  	BlockExplorerURL string `json:"block_explorer_url"`
   255  
   256  	// example: internal server error
   257  	Error string `json:"error"`
   258  }
   259  
   260  // SettleRequest represents the request to settle hermes promises
   261  // swagger:model SettleRequestDTO
   262  type SettleRequest struct {
   263  	HermesIDs  []common.Address `json:"hermes_ids"`
   264  	ProviderID string           `json:"provider_id"`
   265  
   266  	// Deprecated
   267  	HermesID string `json:"hermes_id"`
   268  }
   269  
   270  // WithdrawRequest represents the request to withdraw earnings to l1.
   271  // swagger:model WithdrawRequestDTO
   272  type WithdrawRequest struct {
   273  	HermesID    string `json:"hermes_id"`
   274  	ProviderID  string `json:"provider_id"`
   275  	Beneficiary string `json:"beneficiary"`
   276  	FromChainID int64  `json:"from_chain_id"`
   277  	ToChainID   int64  `json:"to_chain_id"`
   278  	Amount      string `json:"amount,omitempty"`
   279  }
   280  
   281  // Validate will validate a given request
   282  func (w *WithdrawRequest) Validate() *apierror.APIError {
   283  	v := apierror.NewValidator()
   284  	zeroAddr := common.HexToAddress("").Hex()
   285  	if !common.IsHexAddress(w.HermesID) || w.HermesID == zeroAddr {
   286  		v.Invalid("hermes_id", "'hermes_id' should be a valid hex address")
   287  	}
   288  	if !common.IsHexAddress(w.ProviderID) || w.ProviderID == zeroAddr {
   289  		v.Invalid("provider_id", "'provider_id' should be a valid hex address")
   290  	}
   291  	if !common.IsHexAddress(w.Beneficiary) || w.Beneficiary == zeroAddr {
   292  		v.Invalid("beneficiary", "'beneficiary' should be a valid hex address")
   293  	}
   294  
   295  	amount, err := w.AmountInMYST()
   296  	if err != nil {
   297  		v.Invalid("amount", err.Error())
   298  	} else if amount != nil && amount.Cmp(crypto.FloatToBigMyst(99)) > 0 {
   299  		v.Invalid("amount", "withdrawal amount cannot be more than 99 MYST")
   300  	}
   301  
   302  	return v.Err()
   303  }
   304  
   305  // AmountInMYST will return the amount value converted to big.Int MYST.
   306  //
   307  // Amount can be `nil`
   308  func (w *WithdrawRequest) AmountInMYST() (*big.Int, error) {
   309  	if w.Amount == "" {
   310  		return nil, nil
   311  	}
   312  
   313  	res, ok := big.NewInt(0).SetString(w.Amount, 10)
   314  	if !ok {
   315  		return nil, fmt.Errorf("%v is not a valid integer", w.Amount)
   316  	}
   317  
   318  	return res, nil
   319  }
   320  
   321  // SettleWithBeneficiaryRequest represent the request to settle with new beneficiary address.
   322  type SettleWithBeneficiaryRequest struct {
   323  	ProviderID  string `json:"provider_id"`
   324  	HermesID    string `json:"hermes_id"`
   325  	Beneficiary string `json:"beneficiary"`
   326  }
   327  
   328  // DecreaseStakeRequest represents the decrease stake request
   329  // swagger:model DecreaseStakeRequest
   330  type DecreaseStakeRequest struct {
   331  	ID     string   `json:"id,omitempty"`
   332  	Amount *big.Int `json:"amount,omitempty"`
   333  }
   334  
   335  // ReferralTokenResponse represents a response for referral token.
   336  // swagger:model ReferralTokenResponse
   337  type ReferralTokenResponse struct {
   338  	Token string `json:"token"`
   339  }
   340  
   341  // BeneficiaryTxStatus settle with beneficiary transaction status.
   342  // swagger:model BeneficiaryTxStatus
   343  type BeneficiaryTxStatus struct {
   344  	State beneficiary.SettleState `json:"state"`
   345  	Error string                  `json:"error"`
   346  	// example: 0x0000000000000000000000000000000000000001
   347  	ChangeTo string `json:"change_to"`
   348  }
   349  
   350  // TokenRewardAmount represents a response for token rewards.
   351  // swagger:model TokenRewardAmount
   352  type TokenRewardAmount struct {
   353  	Amount *big.Int `json:"amount"`
   354  }
   355  
   356  // ChainSummary represents a response for token rewards.
   357  // swagger:model ChainSummary
   358  type ChainSummary struct {
   359  	Chains       map[int64]string `json:"chains"`
   360  	CurrentChain int64            `json:"current_chain"`
   361  }