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

     1  /*
     2   * Copyright (C) 2019 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 endpoints
    19  
    20  import (
    21  	"encoding/json"
    22  	"fmt"
    23  	"math/big"
    24  	"net/http"
    25  
    26  	"github.com/mysteriumnetwork/go-rest/apierror"
    27  	"github.com/shopspring/decimal"
    28  
    29  	"github.com/asdine/storm/v3"
    30  	"github.com/spf13/cast"
    31  
    32  	"github.com/gin-gonic/gin"
    33  
    34  	"github.com/ethereum/go-ethereum/common"
    35  	"github.com/pkg/errors"
    36  	"github.com/rs/zerolog/log"
    37  	"github.com/vcraescu/go-paginator/adapter"
    38  
    39  	"github.com/mysteriumnetwork/node/config"
    40  	"github.com/mysteriumnetwork/node/core/beneficiary"
    41  	"github.com/mysteriumnetwork/node/identity"
    42  	"github.com/mysteriumnetwork/node/identity/registry"
    43  	"github.com/mysteriumnetwork/node/pilvytis"
    44  	"github.com/mysteriumnetwork/node/session/pingpong"
    45  	"github.com/mysteriumnetwork/node/tequilapi/contract"
    46  	"github.com/mysteriumnetwork/node/tequilapi/utils"
    47  )
    48  
    49  // Transactor represents interface to Transactor service
    50  type Transactor interface {
    51  	FetchCombinedFees(chainID int64) (registry.CombinedFeesResponse, error)
    52  	FetchRegistrationFees(chainID int64) (registry.FeesResponse, error)
    53  	FetchSettleFees(chainID int64) (registry.FeesResponse, error)
    54  	FetchStakeDecreaseFee(chainID int64) (registry.FeesResponse, error)
    55  	RegisterIdentity(id string, stake, fee *big.Int, beneficiary string, chainID int64, referralToken *string) error
    56  	DecreaseStake(id string, chainID int64, amount, transactorFee *big.Int) error
    57  	GetFreeRegistrationEligibility(identity identity.Identity) (bool, error)
    58  	GetFreeProviderRegistrationEligibility() (bool, error)
    59  	OpenChannel(chainID int64, id, hermesID, registryAddress string) error
    60  	ChannelStatus(chainID int64, id, hermesID, registryAddress string) (registry.ChannelStatusResponse, error)
    61  }
    62  
    63  // promiseSettler settles the given promises
    64  type promiseSettler interface {
    65  	ForceSettle(chainID int64, providerID identity.Identity, hermesID ...common.Address) error
    66  	ForceSettleAsync(chainID int64, providerID identity.Identity, hermesID ...common.Address) error
    67  	SettleIntoStake(chainID int64, providerID identity.Identity, hermesID ...common.Address) error
    68  	GetHermesFee(chainID int64, id common.Address) (uint16, error)
    69  	Withdraw(fromChainID int64, toChainID int64, providerID identity.Identity, hermesID, beneficiary common.Address, amount *big.Int) error
    70  }
    71  
    72  type addressProvider interface {
    73  	GetActiveHermes(chainID int64) (common.Address, error)
    74  	GetKnownHermeses(chainID int64) ([]common.Address, error)
    75  	GetHermesChannelAddress(chainID int64, id, hermesAddr common.Address) (common.Address, error)
    76  }
    77  
    78  type beneficiarySaver interface {
    79  	SettleAndSaveBeneficiary(id identity.Identity, hermeses []common.Address, beneficiary common.Address) error
    80  	CleanupAndGetChangeStatus(id identity.Identity, currentBeneficiary string) (*beneficiary.ChangeStatus, error)
    81  }
    82  
    83  type settlementHistoryProvider interface {
    84  	List(pingpong.SettlementHistoryFilter) ([]pingpong.SettlementHistoryEntry, error)
    85  }
    86  
    87  type pilvytisApi interface {
    88  	GetRegistrationPaymentStatus(id identity.Identity) (*pilvytis.RegistrationPaymentResponse, error)
    89  }
    90  
    91  type transactorEndpoint struct {
    92  	transactor                Transactor
    93  	affiliator                Affiliator
    94  	identityRegistry          identityRegistry
    95  	promiseSettler            promiseSettler
    96  	settlementHistoryProvider settlementHistoryProvider
    97  	addressProvider           addressProvider
    98  	bprovider                 beneficiaryProvider
    99  	bhandler                  beneficiarySaver
   100  	pilvytis                  pilvytisApi
   101  }
   102  
   103  // NewTransactorEndpoint creates and returns transactor endpoint
   104  func NewTransactorEndpoint(
   105  	transactor Transactor,
   106  	identityRegistry identityRegistry,
   107  	promiseSettler promiseSettler,
   108  	settlementHistoryProvider settlementHistoryProvider,
   109  	addressProvider addressProvider,
   110  	bprovider beneficiaryProvider,
   111  	bhandler beneficiarySaver,
   112  	pilvytis pilvytisApi,
   113  ) *transactorEndpoint {
   114  	return &transactorEndpoint{
   115  		transactor:                transactor,
   116  		identityRegistry:          identityRegistry,
   117  		promiseSettler:            promiseSettler,
   118  		settlementHistoryProvider: settlementHistoryProvider,
   119  		addressProvider:           addressProvider,
   120  		bprovider:                 bprovider,
   121  		bhandler:                  bhandler,
   122  		pilvytis:                  pilvytis,
   123  	}
   124  }
   125  
   126  // swagger:operation GET /v2/transactor/fees CombinedFeesResponse
   127  //
   128  //	---
   129  //	summary: Returns fees
   130  //	description: Returns fees applied by Transactor
   131  //	responses:
   132  //	  200:
   133  //	    description: Fees applied by Transactor
   134  //	    schema:
   135  //	      "$ref": "#/definitions/CombinedFeesResponse"
   136  //	  500:
   137  //	    description: Internal server error
   138  //	    schema:
   139  //	      "$ref": "#/definitions/APIError"
   140  func (te *transactorEndpoint) TransactorFeesV2(c *gin.Context) {
   141  	chainID := config.GetInt64(config.FlagChainID)
   142  	if qcid, err := cast.ToInt64E(c.Query("chain_id")); err == nil {
   143  		chainID = qcid
   144  	}
   145  
   146  	fees, err := te.transactor.FetchCombinedFees(chainID)
   147  	if err != nil {
   148  		utils.ForwardError(c, err, apierror.Internal("Failed to fetch fees", contract.ErrCodeTransactorFetchFees))
   149  		return
   150  	}
   151  
   152  	hermes, err := te.addressProvider.GetActiveHermes(chainID)
   153  	if err != nil {
   154  		c.Error(apierror.Internal("Failed to get active hermes", contract.ErrCodeActiveHermes))
   155  		return
   156  	}
   157  
   158  	hermesFeePerMyriad, err := te.promiseSettler.GetHermesFee(chainID, hermes)
   159  	if err != nil {
   160  		c.Error(apierror.Internal("Could not get hermes fee: "+err.Error(), contract.ErrCodeHermesFee))
   161  		return
   162  	}
   163  
   164  	hermesPercent := decimal.NewFromInt(int64(hermesFeePerMyriad)).Div(decimal.NewFromInt(10000))
   165  	f := contract.CombinedFeesResponse{
   166  		Current:    contract.NewTransactorFees(&fees.Current),
   167  		Last:       contract.NewTransactorFees(&fees.Last),
   168  		ServerTime: fees.ServerTime,
   169  
   170  		HermesPercent: hermesPercent.StringFixed(4),
   171  	}
   172  
   173  	utils.WriteAsJSON(f, c.Writer)
   174  }
   175  
   176  // swagger:operation GET /transactor/fees FeesDTO
   177  //
   178  //	---
   179  //	summary: Returns fees
   180  //	deprecated: true
   181  //	description: Returns fees applied by Transactor
   182  //	responses:
   183  //	  200:
   184  //	    description: Fees applied by Transactor
   185  //	    schema:
   186  //	      "$ref": "#/definitions/FeesDTO"
   187  //	  500:
   188  //	    description: Internal server error
   189  //	    schema:
   190  //	      "$ref": "#/definitions/APIError"
   191  func (te *transactorEndpoint) TransactorFees(c *gin.Context) {
   192  	chainID := config.GetInt64(config.FlagChainID)
   193  	if qcid, err := cast.ToInt64E(c.Query("chain_id")); err == nil {
   194  		chainID = qcid
   195  	}
   196  
   197  	registrationFees, err := te.transactor.FetchRegistrationFees(chainID)
   198  	if err != nil {
   199  		utils.ForwardError(c, err, apierror.Internal("Failed to fetch fees", contract.ErrCodeTransactorFetchFees))
   200  		return
   201  	}
   202  	settlementFees, err := te.transactor.FetchSettleFees(chainID)
   203  	if err != nil {
   204  		utils.ForwardError(c, err, apierror.Internal("Failed to fetch fees", contract.ErrCodeTransactorFetchFees))
   205  		return
   206  	}
   207  	decreaseStakeFees, err := te.transactor.FetchStakeDecreaseFee(chainID)
   208  	if err != nil {
   209  		utils.ForwardError(c, err, apierror.Internal("Failed to fetch fees", contract.ErrCodeTransactorFetchFees))
   210  		return
   211  	}
   212  
   213  	hermes, err := te.addressProvider.GetActiveHermes(chainID)
   214  	if err != nil {
   215  		c.Error(apierror.Internal("Failed to get active hermes", contract.ErrCodeActiveHermes))
   216  		return
   217  	}
   218  
   219  	hermesFeePerMyriad, err := te.promiseSettler.GetHermesFee(chainID, hermes)
   220  	if err != nil {
   221  		c.Error(apierror.Internal("Could not get hermes fee: "+err.Error(), contract.ErrCodeHermesFee))
   222  		return
   223  	}
   224  
   225  	hermesPercent := decimal.NewFromInt(int64(hermesFeePerMyriad)).Div(decimal.NewFromInt(10000))
   226  	f := contract.FeesDTO{
   227  		Registration:        registrationFees.Fee,
   228  		RegistrationTokens:  contract.NewTokens(registrationFees.Fee),
   229  		Settlement:          settlementFees.Fee,
   230  		SettlementTokens:    contract.NewTokens(settlementFees.Fee),
   231  		HermesPercent:       hermesPercent.StringFixed(4),
   232  		Hermes:              hermesFeePerMyriad,
   233  		DecreaseStake:       decreaseStakeFees.Fee,
   234  		DecreaseStakeTokens: contract.NewTokens(decreaseStakeFees.Fee),
   235  	}
   236  
   237  	utils.WriteAsJSON(f, c.Writer)
   238  }
   239  
   240  // swagger:operation POST /transactor/settle/sync SettleSync
   241  //
   242  //	---
   243  //	summary: Forces the settlement of promises for the given provider and hermes
   244  //	description: Forces a settlement for the hermes promises and blocks until the settlement is complete.
   245  //	parameters:
   246  //	- in: body
   247  //	  name: body
   248  //	  description: Settle request
   249  //	  schema:
   250  //	    $ref: "#/definitions/SettleRequestDTO"
   251  //	responses:
   252  //	  202:
   253  //	    description: Settle request accepted
   254  //	  500:
   255  //	    description: Internal server error
   256  //	    schema:
   257  //	      "$ref": "#/definitions/APIError"
   258  func (te *transactorEndpoint) SettleSync(c *gin.Context) {
   259  	err := te.settle(c.Request, te.promiseSettler.ForceSettle)
   260  	if err != nil {
   261  		log.Err(err).Msg("Settle failed")
   262  		utils.ForwardError(c, err, apierror.Internal("Could not force settle", contract.ErrCodeHermesSettle))
   263  		return
   264  	}
   265  	c.Status(http.StatusOK)
   266  }
   267  
   268  // swagger:operation POST /transactor/settle/async SettleAsync
   269  //
   270  //	---
   271  //	summary: forces the settlement of promises for the given provider and hermes
   272  //	description: Forces a settlement for the hermes promises. Does not wait for completion.
   273  //	parameters:
   274  //	- in: body
   275  //	  name: body
   276  //	  description: Settle request
   277  //	  schema:
   278  //	    $ref: "#/definitions/SettleRequestDTO"
   279  //	responses:
   280  //	  202:
   281  //	    description: Settle request accepted
   282  //	  500:
   283  //	    description: Internal server error
   284  //	    schema:
   285  //	      "$ref": "#/definitions/APIError"
   286  func (te *transactorEndpoint) SettleAsync(c *gin.Context) {
   287  	err := te.settle(c.Request, te.promiseSettler.ForceSettleAsync)
   288  	if err != nil {
   289  		log.Err(err).Msg("Settle async failed")
   290  		utils.ForwardError(c, err, apierror.Internal("Failed to force settle async", contract.ErrCodeHermesSettleAsync))
   291  		return
   292  	}
   293  	c.Status(http.StatusAccepted)
   294  }
   295  
   296  func (te *transactorEndpoint) settle(request *http.Request, settler func(int64, identity.Identity, ...common.Address) error) error {
   297  	req := contract.SettleRequest{}
   298  
   299  	err := json.NewDecoder(request.Body).Decode(&req)
   300  	if err != nil {
   301  		return errors.Wrap(err, "failed to unmarshal settle request")
   302  	}
   303  
   304  	hermesIDs := []common.Address{
   305  		// TODO: Remove this ant just use req.HermesIDs when UI upgrades.
   306  		common.HexToAddress(req.HermesID),
   307  	}
   308  
   309  	if len(req.HermesIDs) > 0 {
   310  		hermesIDs = req.HermesIDs
   311  	}
   312  
   313  	if len(hermesIDs) == 0 {
   314  		return errors.New("must specify a hermes to settle with")
   315  	}
   316  
   317  	chainID := config.GetInt64(config.FlagChainID)
   318  	return settler(chainID, identity.FromAddress(req.ProviderID), hermesIDs...)
   319  }
   320  
   321  // swagger:operation POST /identities/{id}/register Identity RegisterIdentity
   322  //
   323  //	---
   324  //	summary: Registers identity
   325  //	description: Registers identity on Mysterium Network smart contracts using Transactor
   326  //	parameters:
   327  //	- name: id
   328  //	  in: path
   329  //	  description: Identity address to register
   330  //	  type: string
   331  //	  required: true
   332  //	- in: body
   333  //	  name: body
   334  //	  description: all body parameters a optional
   335  //	  schema:
   336  //	    $ref: "#/definitions/IdentityRegisterRequestDTO"
   337  //	responses:
   338  //	  200:
   339  //	    description: Identity registered.
   340  //	  202:
   341  //	    description: Identity registration accepted and will be processed.
   342  //	  400:
   343  //	    description: Failed to parse or request validation failed
   344  //	    schema:
   345  //	      "$ref": "#/definitions/APIError"
   346  //	  422:
   347  //	    description: Unable to process the request at this point
   348  //	    schema:
   349  //	      "$ref": "#/definitions/APIError"
   350  //	  500:
   351  //	    description: Internal server error
   352  //	    schema:
   353  //	      "$ref": "#/definitions/APIError"
   354  func (te *transactorEndpoint) RegisterIdentity(c *gin.Context) {
   355  	id := identity.FromAddress(c.Param("id"))
   356  	chainID := config.GetInt64(config.FlagChainID)
   357  
   358  	req := &contract.IdentityRegisterRequest{}
   359  
   360  	err := json.NewDecoder(c.Request.Body).Decode(&req)
   361  	if err != nil {
   362  		c.Error(apierror.ParseFailed())
   363  		return
   364  	}
   365  
   366  	registrationStatus, err := te.identityRegistry.GetRegistrationStatus(chainID, id)
   367  	if err != nil {
   368  		log.Err(err).Stack().Msgf("Could not check registration status for ID: %s, %+v", id.Address, req)
   369  		c.Error(apierror.Internal(fmt.Sprintf("could not check registration status for ID: %s", id.Address), contract.ErrCodeIDBlockchainRegistrationCheck))
   370  		return
   371  	}
   372  	switch registrationStatus {
   373  	case registry.InProgress:
   374  		log.Info().Msgf("Identity %q registration is in status %s, aborting...", id.Address, registrationStatus)
   375  		c.Error(apierror.Unprocessable("Identity registration in progress", contract.ErrCodeIDRegistrationInProgress))
   376  		return
   377  	case registry.Registered:
   378  		c.Status(http.StatusOK)
   379  		return
   380  	}
   381  
   382  	regFee := big.NewInt(0)
   383  	if !te.canRegisterForFree(req, id) {
   384  		if req.Fee == nil || req.Fee.Cmp(big.NewInt(0)) == 0 {
   385  			rf, err := te.transactor.FetchRegistrationFees(chainID)
   386  			if err != nil {
   387  				utils.ForwardError(c, err, apierror.Internal("Failed to fetch fees", contract.ErrCodeTransactorFetchFees))
   388  				return
   389  			}
   390  
   391  			regFee = rf.Fee
   392  		} else {
   393  			regFee = req.Fee
   394  		}
   395  	}
   396  
   397  	err = te.transactor.RegisterIdentity(id.Address, big.NewInt(0), regFee, req.Beneficiary, chainID, req.ReferralToken)
   398  	if err != nil {
   399  		log.Err(err).Msgf("Failed identity registration request for ID: %s, %+v", id.Address, req)
   400  		utils.ForwardError(c, err, apierror.Internal("Failed to register identity", contract.ErrCodeTransactorRegistration))
   401  		return
   402  	}
   403  
   404  	c.Status(http.StatusAccepted)
   405  }
   406  
   407  // swagger:operation GET /settle/history settlementList
   408  //
   409  //	---
   410  //	summary: Returns settlement history
   411  //	description: Returns settlement history
   412  //	responses:
   413  //	  200:
   414  //	    description: Returns settlement history
   415  //	    schema:
   416  //	      "$ref": "#/definitions/SettlementListResponse"
   417  //	  400:
   418  //	    description: Failed to parse or request validation failed
   419  //	    schema:
   420  //	      "$ref": "#/definitions/APIError"
   421  //	  500:
   422  //	    description: Internal server error
   423  //	    schema:
   424  //	      "$ref": "#/definitions/APIError"
   425  func (te *transactorEndpoint) SettlementHistory(c *gin.Context) {
   426  	query := contract.NewSettlementListQuery()
   427  	if err := query.Bind(c.Request); err != nil {
   428  		c.Error(err)
   429  		return
   430  	}
   431  
   432  	settlementsAll, err := te.settlementHistoryProvider.List(query.ToFilter())
   433  	if err != nil {
   434  		c.Error(apierror.Internal("Could not list settlement history: "+err.Error(), contract.ErrCodeTransactorSettleHistory))
   435  		return
   436  	}
   437  
   438  	var pagedSettlements []pingpong.SettlementHistoryEntry
   439  	p := utils.NewPaginator(adapter.NewSliceAdapter(settlementsAll), query.PageSize, query.Page)
   440  	if err := p.Results(&pagedSettlements); err != nil {
   441  		c.Error(apierror.Internal("Could not paginate settlement history: "+err.Error(), contract.ErrCodeTransactorSettleHistoryPaginate))
   442  		return
   443  	}
   444  
   445  	withdrawalTotal := big.NewInt(0)
   446  	for _, s := range settlementsAll {
   447  		if s.IsWithdrawal {
   448  			withdrawalTotal.Add(withdrawalTotal, s.Amount)
   449  		}
   450  	}
   451  
   452  	response := contract.NewSettlementListResponse(withdrawalTotal, pagedSettlements, p)
   453  	utils.WriteAsJSON(response, c.Writer)
   454  }
   455  
   456  // swagger:operation POST /transactor/stake/decrease Decrease Stake
   457  //
   458  //	---
   459  //	summary: Decreases stake
   460  //	description: Decreases stake on eth blockchain via the mysterium transactor.
   461  //	parameters:
   462  //	- in: body
   463  //	  name: body
   464  //	  description: decrease stake request
   465  //	  schema:
   466  //	    $ref: "#/definitions/DecreaseStakeRequest"
   467  //	responses:
   468  //	  200:
   469  //	    description: Payout info registered
   470  //	  400:
   471  //	    description: Failed to parse or request validation failed
   472  //	    schema:
   473  //	      "$ref": "#/definitions/APIError"
   474  //	  500:
   475  //	    description: Internal server error
   476  //	    schema:
   477  //	      "$ref": "#/definitions/APIError"
   478  func (te *transactorEndpoint) DecreaseStake(c *gin.Context) {
   479  	var req contract.DecreaseStakeRequest
   480  	err := json.NewDecoder(c.Request.Body).Decode(&req)
   481  	if err != nil {
   482  		c.Error(apierror.ParseFailed())
   483  		return
   484  	}
   485  
   486  	chainID := config.GetInt64(config.FlagChainID)
   487  	fees, err := te.transactor.FetchStakeDecreaseFee(chainID)
   488  	if err != nil {
   489  		c.Error(err)
   490  		return
   491  	}
   492  
   493  	err = te.transactor.DecreaseStake(req.ID, chainID, req.Amount, fees.Fee)
   494  	if err != nil {
   495  		log.Err(err).Msgf("Failed decreases stake request for ID: %s, %+v", req.ID, req)
   496  		utils.ForwardError(c, err, apierror.Internal("Failed to decrease stake: "+err.Error(), contract.ErrCodeTransactorDecreaseStake))
   497  		return
   498  	}
   499  
   500  	c.Status(http.StatusAccepted)
   501  }
   502  
   503  // swagger:operation POST /transactor/settle/withdraw Withdraw
   504  //
   505  //	---
   506  //	summary: Asks to perform withdrawal to l1.
   507  //	deprecated: true
   508  //	description: Asks to perform withdrawal to l1.
   509  //	parameters:
   510  //	- in: body
   511  //	  name: body
   512  //	  description: withdraw request body
   513  //	  schema:
   514  //	    $ref: "#/definitions/WithdrawRequestDTO"
   515  //	responses:
   516  //	  202:
   517  //	    description: Withdraw request accepted
   518  //	  400:
   519  //	    description: Failed to parse or request validation failed
   520  //	    schema:
   521  //	      "$ref": "#/definitions/APIError"
   522  //	  500:
   523  //	    description: Internal server error
   524  //	    schema:
   525  //	      "$ref": "#/definitions/APIError"
   526  func (te *transactorEndpoint) Withdraw(c *gin.Context) {
   527  	var req contract.WithdrawRequest
   528  	err := json.NewDecoder(c.Request.Body).Decode(&req)
   529  	if err != nil {
   530  		c.Error(apierror.ParseFailed())
   531  		return
   532  	}
   533  
   534  	if err := req.Validate(); err != nil {
   535  		c.Error(err)
   536  		return
   537  	}
   538  
   539  	amount, err := req.AmountInMYST()
   540  	if err != nil {
   541  		c.Error(apierror.BadRequestField("'amount' is invalid", apierror.ValidateErrInvalidVal, "amount"))
   542  		return
   543  	}
   544  
   545  	fromChainID := config.GetInt64(config.FlagChainID)
   546  	if req.FromChainID != 0 {
   547  		if _, ok := registry.Chains()[req.FromChainID]; !ok {
   548  			c.Error(apierror.BadRequestField("Unsupported from_chain_id", apierror.ValidateErrInvalidVal, "from_chain_id"))
   549  			return
   550  		}
   551  
   552  		fromChainID = req.FromChainID
   553  	}
   554  
   555  	var toChainID int64
   556  	if req.ToChainID != 0 {
   557  		if _, ok := registry.Chains()[req.ToChainID]; !ok {
   558  			c.Error(apierror.BadRequestField("Unsupported to_chain_id", apierror.ValidateErrInvalidVal, "to_chain_id"))
   559  			return
   560  		}
   561  
   562  		toChainID = req.ToChainID
   563  	}
   564  
   565  	err = te.promiseSettler.Withdraw(fromChainID, toChainID, identity.FromAddress(req.ProviderID), common.HexToAddress(req.HermesID), common.HexToAddress(req.Beneficiary), amount)
   566  	if err != nil {
   567  		log.Err(err).Fields(map[string]interface{}{
   568  			"from_chain_id": fromChainID,
   569  			"to_chain_id":   toChainID,
   570  			"provider_id":   req.ProviderID,
   571  			"hermes_id":     req.HermesID,
   572  			"beneficiary":   req.Beneficiary,
   573  			"amount":        amount.String(),
   574  		}).Msg("Withdrawal failed")
   575  		utils.ForwardError(c, err, apierror.Internal("Could not withdraw", contract.ErrCodeTransactorWithdraw))
   576  		return
   577  	}
   578  
   579  	c.Status(http.StatusOK)
   580  }
   581  
   582  // swagger:operation POST /transactor/stake/increase/sync StakeIncreaseSync
   583  //
   584  //	---
   585  //	summary: Forces the settlement with stake increase of promises for the given provider and hermes.
   586  //	description: Forces a settlement with stake increase for the hermes promises and blocks until the settlement is complete.
   587  //	parameters:
   588  //	- in: body
   589  //	  name: body
   590  //	  description: Settle request
   591  //	  schema:
   592  //	    $ref: "#/definitions/SettleRequestDTO"
   593  //	responses:
   594  //	  202:
   595  //	    description: Settle request accepted
   596  //	  500:
   597  //	    description: Internal server error
   598  //	    schema:
   599  //	      "$ref": "#/definitions/APIError"
   600  func (te *transactorEndpoint) SettleIntoStakeSync(c *gin.Context) {
   601  	err := te.settle(c.Request, te.promiseSettler.SettleIntoStake)
   602  	if err != nil {
   603  		log.Err(err).Msg("Settle into stake failed")
   604  		utils.ForwardError(c, err, apierror.Internal("Could not settle into stake", contract.ErrCodeTransactorSettle))
   605  		return
   606  	}
   607  
   608  	c.Status(http.StatusOK)
   609  }
   610  
   611  // swagger:operation POST /transactor/stake/increase/async StakeIncreaseAsync
   612  //
   613  //	---
   614  //	summary: forces the settlement with stake increase of promises for the given provider and hermes.
   615  //	description: Forces a settlement with stake increase for the hermes promises and does not block.
   616  //	parameters:
   617  //	- in: body
   618  //	  name: body
   619  //	  description: settle request body
   620  //	  schema:
   621  //	    $ref: "#/definitions/SettleRequestDTO"
   622  //	responses:
   623  //	  202:
   624  //	    description: Settle request accepted
   625  //	  500:
   626  //	    description: Internal server error
   627  //	    schema:
   628  //	      "$ref": "#/definitions/APIError"
   629  func (te *transactorEndpoint) SettleIntoStakeAsync(c *gin.Context) {
   630  	err := te.settle(c.Request, func(chainID int64, provider identity.Identity, hermes ...common.Address) error {
   631  		go func() {
   632  			err := te.promiseSettler.SettleIntoStake(chainID, provider, hermes...)
   633  			if err != nil {
   634  				log.Error().Err(err).Msgf("could not settle into stake provider(%q) promises", provider.Address)
   635  			}
   636  		}()
   637  		return nil
   638  	})
   639  	if err != nil {
   640  		utils.ForwardError(c, err, apierror.Internal("Could not settle into stake async", contract.ErrCodeTransactorSettle))
   641  		return
   642  	}
   643  
   644  	c.Status(http.StatusOK)
   645  }
   646  
   647  // swagger:operation POST /transactor/token/{token}/reward Reward
   648  //
   649  //	---
   650  //	summary: Returns the amount of reward for a token
   651  //	deprecated: true
   652  //	parameters:
   653  //	- in: path
   654  //	  name: token
   655  //	  description: Token for which to lookup the reward
   656  //	  type: string
   657  //	  required: true
   658  //	responses:
   659  //	  200:
   660  //	    description: Token Reward
   661  //	    schema:
   662  //	      "$ref": "#/definitions/TokenRewardAmount"
   663  //	  500:
   664  //	    description: Internal server error
   665  //	    schema:
   666  //	      "$ref": "#/definitions/APIError"
   667  func (te *transactorEndpoint) TokenRewardAmount(c *gin.Context) {
   668  	token := c.Param("token")
   669  	reward, err := te.affiliator.RegistrationTokenReward(token)
   670  	if err != nil {
   671  		c.Error(err)
   672  		return
   673  	}
   674  	if reward == nil {
   675  		c.Error(apierror.Internal("No reward for token", contract.ErrCodeTransactorNoReward))
   676  		return
   677  	}
   678  
   679  	utils.WriteAsJSON(contract.TokenRewardAmount{
   680  		Amount: reward,
   681  	}, c.Writer)
   682  }
   683  
   684  // swagger:operation GET /transactor/chains-summary Chains
   685  //
   686  //	---
   687  //	summary: Returns available chain map
   688  //	responses:
   689  //	  200:
   690  //	    description: Chain Summary
   691  //	    schema:
   692  //	      "$ref": "#/definitions/ChainSummary"
   693  func (te *transactorEndpoint) ChainSummary(c *gin.Context) {
   694  	chains := registry.Chains()
   695  	result := map[int64]string{}
   696  
   697  	for _, id := range []int64{
   698  		config.FlagChain1ChainID.Value,
   699  		config.FlagChain2ChainID.Value,
   700  	} {
   701  		if name, ok := chains[id]; ok {
   702  			result[id] = name
   703  		}
   704  	}
   705  
   706  	c.JSON(http.StatusOK, &contract.ChainSummary{
   707  		Chains:       result,
   708  		CurrentChain: config.GetInt64(config.FlagChainID),
   709  	})
   710  }
   711  
   712  // EligibilityResponse represents the eligibility response
   713  // swagger:model EligibilityResponse
   714  type EligibilityResponse struct {
   715  	Eligible bool `json:"eligible"`
   716  }
   717  
   718  // swagger:operation GET /transactor/identities/{id}/eligibility Eligibility
   719  //
   720  //	---
   721  //	summary: Checks if given id is eligible for free registration
   722  //	parameters:
   723  //	- name: id
   724  //	  in: path
   725  //	  description: Identity address to register
   726  //	  type: string
   727  //	  required: true
   728  //	responses:
   729  //	  200:
   730  //	    description: Eligibility response
   731  //	    schema:
   732  //	      "$ref": "#/definitions/EligibilityResponse"
   733  //	  500:
   734  //	    description: Internal server error
   735  //	    schema:
   736  //	      "$ref": "#/definitions/APIError"
   737  func (te *transactorEndpoint) FreeRegistrationEligibility(c *gin.Context) {
   738  	id := identity.FromAddress(c.Param("id"))
   739  
   740  	res, err := te.transactor.GetFreeRegistrationEligibility(id)
   741  	if err != nil {
   742  		utils.ForwardError(c, err, apierror.Internal("Failed to check if free registration is possible", contract.ErrCodeTransactorRegistration))
   743  		return
   744  	}
   745  
   746  	c.JSON(http.StatusOK, EligibilityResponse{Eligible: res})
   747  }
   748  
   749  // swagger:operation GET /identities/provider/eligibility ProviderEligibility
   750  //
   751  //	---
   752  //	summary: Checks if provider is eligible for free registration
   753  //	responses:
   754  //	  200:
   755  //	    description: Eligibility response
   756  //	    schema:
   757  //	      "$ref": "#/definitions/EligibilityResponse"
   758  //	  500:
   759  //	    description: Internal server error
   760  //	    schema:
   761  //	      "$ref": "#/definitions/APIError"
   762  func (te *transactorEndpoint) FreeProviderRegistrationEligibility(c *gin.Context) {
   763  	res, err := te.transactor.GetFreeProviderRegistrationEligibility()
   764  	if err != nil {
   765  		utils.ForwardError(c, err, apierror.Internal("Failed to check if free registration is possible", contract.ErrCodeTransactorRegistration))
   766  		return
   767  	}
   768  
   769  	c.JSON(http.StatusOK, EligibilityResponse{Eligible: res})
   770  }
   771  
   772  // swagger:operation GET /identities/{id}/beneficiary-status
   773  //
   774  //	---
   775  //	summary: Returns beneficiary transaction status
   776  //	description: Returns the last beneficiary transaction status for given identity
   777  //	parameters:
   778  //	- name: id
   779  //	  in: path
   780  //	  description: Identity address to register
   781  //	  type: string
   782  //	  required: true
   783  //	responses:
   784  //	  200:
   785  //	    description: Returns beneficiary transaction status
   786  //	    schema:
   787  //	      "$ref": "#/definitions/BeneficiaryTxStatus"
   788  //	  404:
   789  //	    description: Beneficiary change never recorded.
   790  //	  500:
   791  //	    description: Internal server error
   792  //	    schema:
   793  //	      "$ref": "#/definitions/APIError"
   794  func (te *transactorEndpoint) BeneficiaryTxStatus(c *gin.Context) {
   795  	id := identity.FromAddress(c.Param("id"))
   796  	current, err := te.bprovider.GetBeneficiary(id.ToCommonAddress())
   797  	if err != nil {
   798  		c.Error(apierror.Internal("Failed to get current beneficiary: "+err.Error(), contract.ErrCodeTransactorBeneficiary))
   799  		return
   800  	}
   801  	status, err := te.bhandler.CleanupAndGetChangeStatus(id, current.Hex())
   802  	if err != nil {
   803  		if errors.Is(err, storm.ErrNotFound) {
   804  			utils.WriteAsJSON(
   805  				&contract.BeneficiaryTxStatus{
   806  					State:    beneficiary.NotFound,
   807  					Error:    "",
   808  					ChangeTo: "",
   809  				},
   810  				c.Writer,
   811  			)
   812  			return
   813  		}
   814  		c.Error(apierror.Internal("Failed to get current transaction status: "+err.Error(), contract.ErrCodeTransactorBeneficiaryTxStatus))
   815  		return
   816  	}
   817  
   818  	utils.WriteAsJSON(
   819  		&contract.BeneficiaryTxStatus{
   820  			State:    status.State,
   821  			Error:    status.Error,
   822  			ChangeTo: status.ChangeTo,
   823  		},
   824  		c.Writer,
   825  	)
   826  }
   827  
   828  // swagger:operation POST /identities/{id}/beneficiary
   829  //
   830  //	---
   831  //	summary: Settle with Beneficiary
   832  //	description: Change beneficiary and settle earnings to it. This is async method.
   833  //	parameters:
   834  //	- name: id
   835  //	  in: path
   836  //	  description: Identity address to register
   837  //	  type: string
   838  //	  required: true
   839  //	responses:
   840  //	  202:
   841  //	    description: Settle request accepted
   842  //	  400:
   843  //	    description: Failed to parse or request validation failed
   844  //	    schema:
   845  //	      "$ref": "#/definitions/APIError"
   846  func (te *transactorEndpoint) SettleWithBeneficiaryAsync(c *gin.Context) {
   847  	id := c.Param("id")
   848  
   849  	req := &contract.SettleWithBeneficiaryRequest{}
   850  	err := json.NewDecoder(c.Request.Body).Decode(&req)
   851  	if err != nil {
   852  		c.Error(apierror.ParseFailed())
   853  		return
   854  	}
   855  
   856  	chainID := config.GetInt64(config.FlagChainID)
   857  
   858  	hermesID := common.HexToAddress(req.HermesID)
   859  	hermeses := []common.Address{hermesID}
   860  	if hermesID == common.HexToAddress("") {
   861  		hermeses, err = te.addressProvider.GetKnownHermeses(chainID)
   862  		if err != nil {
   863  			c.Error(err)
   864  			return
   865  		}
   866  	}
   867  
   868  	go func() {
   869  		err = te.bhandler.SettleAndSaveBeneficiary(identity.FromAddress(id), hermeses, common.HexToAddress(req.Beneficiary))
   870  		if err != nil {
   871  			log.Err(err).Msgf("Failed set beneficiary request for ID: %s, %+v", id, req)
   872  		}
   873  	}()
   874  
   875  	c.Status(http.StatusAccepted)
   876  }
   877  
   878  // AddRoutesForTransactor attaches Transactor endpoints to router
   879  func AddRoutesForTransactor(
   880  	identityRegistry identityRegistry,
   881  	transactor Transactor,
   882  	affiliator Affiliator,
   883  	promiseSettler promiseSettler,
   884  	settlementHistoryProvider settlementHistoryProvider,
   885  	addressProvider addressProvider,
   886  	bprovider beneficiaryProvider,
   887  	bhandler beneficiarySaver,
   888  	pilvytis pilvytisApi,
   889  ) func(*gin.Engine) error {
   890  	te := NewTransactorEndpoint(transactor, identityRegistry, promiseSettler, settlementHistoryProvider, addressProvider, bprovider, bhandler, pilvytis)
   891  	a := NewAffiliatorEndpoint(affiliator)
   892  
   893  	return func(e *gin.Engine) error {
   894  		idGroup := e.Group("/identities")
   895  		{
   896  			idGroup.POST("/:id/register", te.RegisterIdentity)
   897  			idGroup.GET("/provider/eligibility", te.FreeProviderRegistrationEligibility)
   898  			idGroup.GET("/:id/eligibility", te.FreeRegistrationEligibility)
   899  			idGroup.GET("/:id/beneficiary-status", te.BeneficiaryTxStatus)
   900  			idGroup.POST("/:id/beneficiary", te.SettleWithBeneficiaryAsync)
   901  		}
   902  
   903  		transGroup := e.Group("/transactor")
   904  		{
   905  			transGroup.GET("/fees", te.TransactorFees)
   906  			transGroup.POST("/settle/sync", te.SettleSync)
   907  			transGroup.POST("/settle/async", te.SettleAsync)
   908  			transGroup.GET("/settle/history", te.SettlementHistory)
   909  			transGroup.POST("/stake/increase/sync", te.SettleIntoStakeSync)
   910  			transGroup.POST("/stake/increase/async", te.SettleIntoStakeAsync)
   911  			transGroup.POST("/stake/decrease", te.DecreaseStake)
   912  			transGroup.POST("/settle/withdraw", te.Withdraw)
   913  			transGroup.GET("/token/:token/reward", a.TokenRewardAmount)
   914  			transGroup.GET("/chain-summary", te.ChainSummary)
   915  		}
   916  		transGroupV2 := e.Group("/v2/transactor")
   917  		{
   918  			transGroupV2.GET("/fees", te.TransactorFeesV2)
   919  		}
   920  		return nil
   921  	}
   922  }
   923  
   924  func (te *transactorEndpoint) canRegisterForFree(req *contract.IdentityRegisterRequest, id identity.Identity) bool {
   925  	if req.ReferralToken != nil {
   926  		return true
   927  	}
   928  	eligible, err := te.transactor.GetFreeRegistrationEligibility(id)
   929  
   930  	if err != nil {
   931  		log.Warn().AnErr("err", err).Msg("Failed to get registration payment status from pilvytis")
   932  		return false
   933  	}
   934  
   935  	return eligible
   936  }