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

     1  /*
     2   * Copyright (C) 2017 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 client
    19  
    20  import (
    21  	"fmt"
    22  	"io"
    23  	"math/big"
    24  	"net/http"
    25  	"net/url"
    26  	"strconv"
    27  
    28  	"github.com/ethereum/go-ethereum/common"
    29  	"github.com/pkg/errors"
    30  
    31  	"github.com/mysteriumnetwork/go-rest/apierror"
    32  	"github.com/mysteriumnetwork/node/identity"
    33  	"github.com/mysteriumnetwork/node/tequilapi/contract"
    34  	"github.com/mysteriumnetwork/payments/exchange"
    35  )
    36  
    37  // NewClient returns a new instance of Client
    38  func NewClient(ip string, port int) *Client {
    39  	return &Client{
    40  		http: newHTTPClient(
    41  			fmt.Sprintf("http://%s:%d", ip, port),
    42  			"goclient-v0.1",
    43  		),
    44  	}
    45  }
    46  
    47  // Client is able perform remote requests to Tequilapi server
    48  type Client struct {
    49  	http httpClientInterface
    50  }
    51  
    52  // AuthAuthenticate authenticates user and issues auth token
    53  func (client *Client) AuthAuthenticate(request contract.AuthRequest) (res contract.AuthResponse, err error) {
    54  	response, err := client.http.Post("/auth/authenticate", request)
    55  	if err != nil {
    56  		return res, err
    57  	}
    58  	defer response.Body.Close()
    59  
    60  	err = parseResponseJSON(response, &res)
    61  	if err != nil {
    62  		return res, err
    63  	}
    64  
    65  	client.http.SetToken(res.Token)
    66  	return res, nil
    67  }
    68  
    69  // AuthLogin authenticates user and sets cookie with issued auth token
    70  func (client *Client) AuthLogin(request contract.AuthRequest) (res contract.AuthResponse, err error) {
    71  	response, err := client.http.Post("/auth/login", request)
    72  	if err != nil {
    73  		return res, err
    74  	}
    75  	defer response.Body.Close()
    76  
    77  	err = parseResponseJSON(response, &res)
    78  	if err != nil {
    79  		return res, err
    80  	}
    81  
    82  	client.http.SetToken(res.Token)
    83  	return res, nil
    84  }
    85  
    86  // AuthLogout Clears authentication cookie
    87  func (client *Client) AuthLogout() error {
    88  	response, err := client.http.Delete("/auth/logout", nil)
    89  	if err != nil {
    90  		return err
    91  	}
    92  	defer response.Body.Close()
    93  
    94  	return nil
    95  }
    96  
    97  // AuthChangePassword changes user password
    98  func (client *Client) AuthChangePassword(request contract.ChangePasswordRequest) error {
    99  	response, err := client.http.Put("/auth/password", request)
   100  	if err != nil {
   101  		return err
   102  	}
   103  	defer response.Body.Close()
   104  
   105  	return nil
   106  }
   107  
   108  // ImportIdentity sends a request to import a given identity.
   109  func (client *Client) ImportIdentity(blob []byte, passphrase string, setDefault bool) (id contract.IdentityRefDTO, err error) {
   110  	response, err := client.http.Post("identities-import", contract.IdentityImportRequest{
   111  		Data:              blob,
   112  		CurrentPassphrase: passphrase,
   113  		SetDefault:        setDefault,
   114  	})
   115  	if err != nil {
   116  		return
   117  	}
   118  	defer response.Body.Close()
   119  
   120  	err = parseResponseJSON(response, &id)
   121  	return id, err
   122  }
   123  
   124  // GetIdentities returns a list of client identities
   125  func (client *Client) GetIdentities() (ids []contract.IdentityRefDTO, err error) {
   126  	response, err := client.http.Get("identities", url.Values{})
   127  	if err != nil {
   128  		return
   129  	}
   130  	defer response.Body.Close()
   131  
   132  	var list contract.ListIdentitiesResponse
   133  	err = parseResponseJSON(response, &list)
   134  
   135  	return list.Identities, err
   136  }
   137  
   138  // NewIdentity creates a new client identity
   139  func (client *Client) NewIdentity(passphrase string) (id contract.IdentityRefDTO, err error) {
   140  	response, err := client.http.Post("identities", contract.IdentityCreateRequest{Passphrase: &passphrase})
   141  	if err != nil {
   142  		return
   143  	}
   144  	defer response.Body.Close()
   145  
   146  	err = parseResponseJSON(response, &id)
   147  	return id, err
   148  }
   149  
   150  // CurrentIdentity unlocks and returns the last used, new or first identity
   151  func (client *Client) CurrentIdentity(identity, passphrase string) (id contract.IdentityRefDTO, err error) {
   152  	response, err := client.http.Put("identities/current", contract.IdentityCurrentRequest{
   153  		Address:    &identity,
   154  		Passphrase: &passphrase,
   155  	})
   156  	if err != nil {
   157  		return
   158  	}
   159  	defer response.Body.Close()
   160  
   161  	err = parseResponseJSON(response, &id)
   162  	return id, err
   163  }
   164  
   165  // BalanceRefresh forces a balance refresh if possible and returns the latest balance.
   166  func (client *Client) BalanceRefresh(identityAddress string) (b contract.BalanceDTO, err error) {
   167  	path := fmt.Sprintf("identities/%s/balance/refresh", identityAddress)
   168  
   169  	response, err := client.http.Put(path, nil)
   170  	if err != nil {
   171  		return b, err
   172  	}
   173  	defer response.Body.Close()
   174  
   175  	err = parseResponseJSON(response, &b)
   176  	return b, err
   177  }
   178  
   179  // Identity returns identity status with cached balance
   180  func (client *Client) Identity(identityAddress string) (id contract.IdentityDTO, err error) {
   181  	path := fmt.Sprintf("identities/%s", identityAddress)
   182  
   183  	response, err := client.http.Get(path, nil)
   184  	if err != nil {
   185  		return id, err
   186  	}
   187  	defer response.Body.Close()
   188  
   189  	err = parseResponseJSON(response, &id)
   190  	return id, err
   191  }
   192  
   193  // IdentityRegistrationStatus returns information of identity needed to register it on blockchain
   194  func (client *Client) IdentityRegistrationStatus(address string) (contract.IdentityRegistrationResponse, error) {
   195  	response, err := client.http.Get("identities/"+address+"/registration", url.Values{})
   196  	if err != nil {
   197  		return contract.IdentityRegistrationResponse{}, err
   198  	}
   199  	defer response.Body.Close()
   200  
   201  	status := contract.IdentityRegistrationResponse{}
   202  	err = parseResponseJSON(response, &status)
   203  	return status, err
   204  }
   205  
   206  // GetTransactorFees returns the transactor fees
   207  func (client *Client) GetTransactorFees() (contract.FeesDTO, error) {
   208  	fees := contract.FeesDTO{}
   209  
   210  	res, err := client.http.Get("transactor/fees", nil)
   211  	if err != nil {
   212  		return fees, err
   213  	}
   214  	defer res.Body.Close()
   215  
   216  	err = parseResponseJSON(res, &fees)
   217  	return fees, err
   218  }
   219  
   220  // RegisterIdentity registers identity
   221  func (client *Client) RegisterIdentity(address, beneficiary string, token *string) error {
   222  	payload := contract.IdentityRegisterRequest{
   223  		ReferralToken: token,
   224  		Beneficiary:   beneficiary,
   225  	}
   226  
   227  	response, err := client.http.Post("identities/"+address+"/register", payload)
   228  	if err != nil {
   229  		return err
   230  	}
   231  	defer response.Body.Close()
   232  
   233  	switch response.StatusCode {
   234  	case http.StatusOK, http.StatusAccepted:
   235  	default:
   236  		return fmt.Errorf("expected 200 or 202 got %v", response.StatusCode)
   237  	}
   238  
   239  	return nil
   240  }
   241  
   242  // GetRegistrationPaymentStatus returns the registration payment status
   243  func (client *Client) GetRegistrationPaymentStatus(identity string) (contract.RegistrationPaymentResponse, error) {
   244  	resp := contract.RegistrationPaymentResponse{}
   245  
   246  	res, err := client.http.Get(fmt.Sprintf("v2/identities/%s/registration-payment", identity), nil)
   247  	if err != nil {
   248  		return resp, err
   249  	}
   250  	defer res.Body.Close()
   251  
   252  	err = parseResponseJSON(res, &resp)
   253  	return resp, err
   254  }
   255  
   256  // ConnectionCreate initiates a new connection to a host identified by providerID
   257  func (client *Client) ConnectionCreate(consumerID, providerID, hermesID, serviceType string, options contract.ConnectOptions) (status contract.ConnectionInfoDTO, err error) {
   258  	response, err := client.http.Put("connection", contract.ConnectionCreateRequest{
   259  		ConsumerID:     consumerID,
   260  		ProviderID:     providerID,
   261  		HermesID:       hermesID,
   262  		ServiceType:    serviceType,
   263  		ConnectOptions: options,
   264  		Filter: contract.ConnectionCreateFilter{
   265  			IncludeMonitoringFailed: true,
   266  		},
   267  	})
   268  	if err != nil {
   269  		return contract.ConnectionInfoDTO{}, err
   270  	}
   271  	defer response.Body.Close()
   272  
   273  	err = parseResponseJSON(response, &status)
   274  	return status, err
   275  }
   276  
   277  // SmartConnectionCreate initiates a new connection to a host identified by filter
   278  func (client *Client) SmartConnectionCreate(consumerID, hermesID, serviceType string, filter contract.ConnectionCreateFilter, options contract.ConnectOptions) (status contract.ConnectionInfoDTO, err error) {
   279  	response, err := client.http.Put("connection", contract.ConnectionCreateRequest{
   280  		ConsumerID:     consumerID,
   281  		Filter:         filter,
   282  		HermesID:       hermesID,
   283  		ServiceType:    serviceType,
   284  		ConnectOptions: options,
   285  	})
   286  	if err != nil {
   287  		return contract.ConnectionInfoDTO{}, err
   288  	}
   289  	defer response.Body.Close()
   290  
   291  	err = parseResponseJSON(response, &status)
   292  	return status, err
   293  }
   294  
   295  // ConnectionDestroy terminates current connection
   296  func (client *Client) ConnectionDestroy(port int) (err error) {
   297  	url := fmt.Sprintf("connection?%s", url.Values{"id": []string{strconv.Itoa(port)}}.Encode())
   298  	response, err := client.http.Delete(url, nil)
   299  	if err != nil {
   300  		return
   301  	}
   302  	defer response.Body.Close()
   303  
   304  	return nil
   305  }
   306  
   307  // ConnectionStatistics returns statistics about current connection
   308  func (client *Client) ConnectionStatistics(sessionID ...string) (statistics contract.ConnectionStatisticsDTO, err error) {
   309  	response, err := client.http.Get("connection/statistics", url.Values{
   310  		"id": sessionID,
   311  	})
   312  	if err != nil {
   313  		return statistics, err
   314  	}
   315  	defer response.Body.Close()
   316  
   317  	err = parseResponseJSON(response, &statistics)
   318  	return statistics, err
   319  }
   320  
   321  // ConnectionTraffic returns traffic information about current connection
   322  func (client *Client) ConnectionTraffic(sessionID ...string) (traffic contract.ConnectionTrafficDTO, err error) {
   323  	response, err := client.http.Get("connection/traffic", url.Values{
   324  		"id": sessionID,
   325  	})
   326  	if err != nil {
   327  		return traffic, err
   328  	}
   329  	defer response.Body.Close()
   330  
   331  	err = parseResponseJSON(response, &traffic)
   332  	return traffic, err
   333  }
   334  
   335  // ConnectionStatus returns connection status
   336  func (client *Client) ConnectionStatus(port int) (status contract.ConnectionInfoDTO, err error) {
   337  	response, err := client.http.Get("connection", url.Values{"id": []string{strconv.Itoa(port)}})
   338  	if err != nil {
   339  		return status, err
   340  	}
   341  	defer response.Body.Close()
   342  
   343  	err = parseResponseJSON(response, &status)
   344  	return status, err
   345  }
   346  
   347  // ConnectionIP returns public ip
   348  func (client *Client) ConnectionIP() (ip contract.IPDTO, err error) {
   349  	response, err := client.http.Get("connection/ip", url.Values{})
   350  	if err != nil {
   351  		return ip, err
   352  	}
   353  	defer response.Body.Close()
   354  
   355  	err = parseResponseJSON(response, &ip)
   356  	return ip, err
   357  }
   358  
   359  // ProxyIP returns public ip of the proxy.
   360  func (client *Client) ProxyIP(proxyPort int) (ip contract.IPDTO, err error) {
   361  	response, err := client.http.Get("connection/proxy/ip", url.Values{"port": []string{strconv.Itoa(proxyPort)}})
   362  	if err != nil {
   363  		return ip, err
   364  	}
   365  	defer response.Body.Close()
   366  
   367  	err = parseResponseJSON(response, &ip)
   368  	return ip, err
   369  }
   370  
   371  // ProxyLocation returns proxy location.
   372  func (client *Client) ProxyLocation(proxyPort int) (location contract.LocationDTO, err error) {
   373  	response, err := client.http.Get("connection/proxy/location", url.Values{"port": []string{strconv.Itoa(proxyPort)}})
   374  	if err != nil {
   375  		return location, err
   376  	}
   377  	defer response.Body.Close()
   378  
   379  	err = parseResponseJSON(response, &location)
   380  	return location, err
   381  }
   382  
   383  // ConnectionLocation returns current location
   384  func (client *Client) ConnectionLocation() (location contract.LocationDTO, err error) {
   385  	response, err := client.http.Get("connection/location", url.Values{})
   386  	if err != nil {
   387  		return location, err
   388  	}
   389  	defer response.Body.Close()
   390  
   391  	err = parseResponseJSON(response, &location)
   392  	return location, err
   393  }
   394  
   395  // Healthcheck returns a healthcheck info
   396  func (client *Client) Healthcheck() (healthcheck contract.HealthCheckDTO, err error) {
   397  	response, err := client.http.Get("healthcheck", url.Values{})
   398  	if err != nil {
   399  		return
   400  	}
   401  
   402  	defer response.Body.Close()
   403  	err = parseResponseJSON(response, &healthcheck)
   404  	return healthcheck, err
   405  }
   406  
   407  // OriginLocation returns original location
   408  func (client *Client) OriginLocation() (location contract.LocationDTO, err error) {
   409  	response, err := client.http.Get("location", url.Values{})
   410  	if err != nil {
   411  		return location, err
   412  	}
   413  	defer response.Body.Close()
   414  
   415  	err = parseResponseJSON(response, &location)
   416  	return location, err
   417  }
   418  
   419  // ProposalsByType fetches proposals by given type
   420  func (client *Client) ProposalsByType(serviceType string) ([]contract.ProposalDTO, error) {
   421  	queryParams := url.Values{}
   422  	queryParams.Add("service_type", serviceType)
   423  	return client.proposals(queryParams)
   424  }
   425  
   426  // ProposalsByTypeWithWhitelisting fetches proposals by given type with all whitelisting options.
   427  func (client *Client) ProposalsByTypeWithWhitelisting(serviceType string) ([]contract.ProposalDTO, error) {
   428  	queryParams := url.Values{}
   429  	queryParams.Add("service_type", serviceType)
   430  	queryParams.Add("access_policy", "all")
   431  	return client.proposals(queryParams)
   432  }
   433  
   434  // ProposalsByLocationAndService fetches proposals by given service and node location types.
   435  func (client *Client) ProposalsByLocationAndService(serviceType, locationType, locationCountry string) ([]contract.ProposalDTO, error) {
   436  	queryParams := url.Values{}
   437  	queryParams.Add("service_type", serviceType)
   438  	queryParams.Add("ip_type", locationType)
   439  	queryParams.Add("location_country", locationCountry)
   440  	return client.proposals(queryParams)
   441  }
   442  
   443  // Proposals returns all available proposals for services
   444  func (client *Client) Proposals() ([]contract.ProposalDTO, error) {
   445  	return client.proposals(url.Values{})
   446  }
   447  
   448  // ProposalsNATCompatible returns proposals for services which we can connect to
   449  func (client *Client) ProposalsNATCompatible() ([]contract.ProposalDTO, error) {
   450  	queryParams := url.Values{}
   451  	queryParams.Add("nat_compatibility", contract.AutoNATType)
   452  	return client.proposals(queryParams)
   453  }
   454  
   455  func (client *Client) proposals(query url.Values) ([]contract.ProposalDTO, error) {
   456  	response, err := client.http.Get("proposals", query)
   457  	if err != nil {
   458  		return []contract.ProposalDTO{}, err
   459  	}
   460  	defer response.Body.Close()
   461  
   462  	var proposals contract.ListProposalsResponse
   463  	err = parseResponseJSON(response, &proposals)
   464  	return proposals.Proposals, err
   465  }
   466  
   467  // Unlock allows using identity in following commands
   468  func (client *Client) Unlock(identity, passphrase string) error {
   469  	payload := contract.IdentityUnlockRequest{
   470  		Passphrase: &passphrase,
   471  	}
   472  
   473  	path := fmt.Sprintf("identities/%s/unlock", identity)
   474  	response, err := client.http.Put(path, payload)
   475  	if err != nil {
   476  		return err
   477  	}
   478  	defer response.Body.Close()
   479  
   480  	return nil
   481  }
   482  
   483  // SetBeneficiaryAsync store beneficiary address locally for identity.
   484  func (client *Client) SetBeneficiaryAsync(identity, ethAddress string) error {
   485  	path := fmt.Sprintf("identities/%s/beneficiary-async", identity)
   486  	payload := contract.BeneficiaryAddressRequest{
   487  		Address: ethAddress,
   488  	}
   489  
   490  	response, err := client.http.Post(path, payload)
   491  	if err != nil {
   492  		return err
   493  	}
   494  	defer response.Body.Close()
   495  
   496  	if response.StatusCode != http.StatusOK {
   497  		return fmt.Errorf("failed to save")
   498  	}
   499  
   500  	return nil
   501  }
   502  
   503  // GetBeneficiaryAsync gets locally saved beneficiary address.
   504  func (client *Client) GetBeneficiaryAsync(identity string) (contract.BeneficiaryAddressRequest, error) {
   505  	path := fmt.Sprintf("identities/%s/beneficiary-async", identity)
   506  	res := contract.BeneficiaryAddressRequest{}
   507  	response, err := client.http.Get(path, nil)
   508  	if err != nil {
   509  		return res, err
   510  	}
   511  	defer response.Body.Close()
   512  
   513  	err = parseResponseJSON(response, &res)
   514  	return res, err
   515  }
   516  
   517  // Stop kills mysterium client
   518  func (client *Client) Stop() error {
   519  	emptyPayload := struct{}{}
   520  	response, err := client.http.Post("stop", emptyPayload)
   521  	if err != nil {
   522  		return err
   523  	}
   524  	defer response.Body.Close()
   525  
   526  	return nil
   527  }
   528  
   529  // Sessions returns all sessions from history
   530  func (client *Client) Sessions() (sessions contract.SessionListResponse, err error) {
   531  	response, err := client.http.Get("sessions", url.Values{})
   532  	if err != nil {
   533  		return sessions, err
   534  	}
   535  	defer response.Body.Close()
   536  
   537  	err = parseResponseJSON(response, &sessions)
   538  	return sessions, err
   539  }
   540  
   541  // SessionsByServiceType returns sessions from history filtered by type
   542  func (client *Client) SessionsByServiceType(serviceType string) (contract.SessionListResponse, error) {
   543  	sessions, err := client.Sessions()
   544  	sessions = filterSessionsByType(serviceType, sessions)
   545  	return sessions, err
   546  }
   547  
   548  // SessionsByStatus returns sessions from history filtered by their status
   549  func (client *Client) SessionsByStatus(status string) (contract.SessionListResponse, error) {
   550  	sessions, err := client.Sessions()
   551  	sessions = filterSessionsByStatus(status, sessions)
   552  	return sessions, err
   553  }
   554  
   555  // Services returns all running services
   556  func (client *Client) Services() (services contract.ServiceListResponse, err error) {
   557  	response, err := client.http.Get("services", url.Values{})
   558  	if err != nil {
   559  		return services, err
   560  	}
   561  	defer response.Body.Close()
   562  
   563  	err = parseResponseJSON(response, &services)
   564  	return services, err
   565  }
   566  
   567  // Service returns a service information by the requested id
   568  func (client *Client) Service(id string) (service contract.ServiceInfoDTO, err error) {
   569  	response, err := client.http.Get("services/"+id, url.Values{})
   570  	if err != nil {
   571  		return service, err
   572  	}
   573  	defer response.Body.Close()
   574  
   575  	err = parseResponseJSON(response, &service)
   576  	return service, err
   577  }
   578  
   579  // ServiceStart starts an instance of the service.
   580  func (client *Client) ServiceStart(request contract.ServiceStartRequest) (service contract.ServiceInfoDTO, err error) {
   581  	response, err := client.http.Post("services", request)
   582  	if err != nil {
   583  		return service, err
   584  	}
   585  	defer response.Body.Close()
   586  
   587  	err = parseResponseJSON(response, &service)
   588  	return service, err
   589  }
   590  
   591  // ServiceStop stops the running service instance by the requested id.
   592  func (client *Client) ServiceStop(id string) error {
   593  	path := fmt.Sprintf("services/%s", id)
   594  	response, err := client.http.Delete(path, nil)
   595  	if err != nil {
   596  		return err
   597  	}
   598  	defer response.Body.Close()
   599  
   600  	return nil
   601  }
   602  
   603  // NATStatus returns status of NAT traversal
   604  func (client *Client) NATStatus() (status contract.NodeStatusResponse, err error) {
   605  	response, err := client.http.Get("node/monitoring-status", nil)
   606  	if err != nil {
   607  		return status, err
   608  	}
   609  	defer response.Body.Close()
   610  
   611  	err = parseResponseJSON(response, &status)
   612  	return status, err
   613  }
   614  
   615  // NATType returns type of NAT in sense of traversal capabilities
   616  func (client *Client) NATType() (status contract.NATTypeDTO, err error) {
   617  	response, err := client.http.Get("nat/type", nil)
   618  	if err != nil {
   619  		return status, err
   620  	}
   621  	defer response.Body.Close()
   622  
   623  	err = parseResponseJSON(response, &status)
   624  	return status, err
   625  }
   626  
   627  // filterSessionsByType removes all sessions of irrelevant types
   628  func filterSessionsByType(serviceType string, sessions contract.SessionListResponse) contract.SessionListResponse {
   629  	matches := 0
   630  	for _, s := range sessions.Items {
   631  		if s.ServiceType == serviceType {
   632  			sessions.Items[matches] = s
   633  			matches++
   634  		}
   635  	}
   636  	sessions.Items = sessions.Items[:matches]
   637  	return sessions
   638  }
   639  
   640  // filterSessionsByStatus removes all sessions with non matching status
   641  func filterSessionsByStatus(status string, sessions contract.SessionListResponse) contract.SessionListResponse {
   642  	matches := 0
   643  	for _, s := range sessions.Items {
   644  		if s.Status == status {
   645  			sessions.Items[matches] = s
   646  			matches++
   647  		}
   648  	}
   649  	sessions.Items = sessions.Items[:matches]
   650  	return sessions
   651  }
   652  
   653  // Withdraw requests the withdrawal of money from l2 to l1 of hermes promises
   654  func (client *Client) Withdraw(providerID identity.Identity, hermesID, beneficiary common.Address, amount *big.Int, fromChainID, toChainID int64) error {
   655  	withdrawRequest := contract.WithdrawRequest{
   656  		ProviderID:  providerID.Address,
   657  		HermesID:    hermesID.Hex(),
   658  		Beneficiary: beneficiary.Hex(),
   659  		FromChainID: fromChainID,
   660  		ToChainID:   toChainID,
   661  	}
   662  
   663  	if amount != nil {
   664  		withdrawRequest.Amount = amount.String()
   665  	}
   666  
   667  	path := "transactor/settle/withdraw"
   668  
   669  	response, err := client.http.Post(path, withdrawRequest)
   670  	if err != nil {
   671  		return err
   672  	}
   673  	defer response.Body.Close()
   674  
   675  	if response.StatusCode != http.StatusAccepted && response.StatusCode != http.StatusOK {
   676  		return errors.Wrap(err, "could not withdraw")
   677  	}
   678  	return nil
   679  }
   680  
   681  // Settle requests the settling of hermes promises
   682  func (client *Client) Settle(providerID identity.Identity, hermesIDs []common.Address, waitForBlockchain bool) error {
   683  	settleRequest := contract.SettleRequest{
   684  		ProviderID: providerID.Address,
   685  		HermesIDs:  hermesIDs,
   686  	}
   687  
   688  	path := "transactor/settle/"
   689  	if waitForBlockchain {
   690  		path += "sync"
   691  	} else {
   692  		path += "async"
   693  	}
   694  
   695  	response, err := client.http.Post(path, settleRequest)
   696  	if err != nil {
   697  		return err
   698  	}
   699  	defer response.Body.Close()
   700  
   701  	if response.StatusCode != http.StatusAccepted && response.StatusCode != http.StatusOK {
   702  		return fmt.Errorf("could not settle promise")
   703  	}
   704  	return nil
   705  }
   706  
   707  // SettleIntoStake requests the settling of accountant promises into a stake increase
   708  func (client *Client) SettleIntoStake(providerID, hermesID identity.Identity, waitForBlockchain bool) error {
   709  	settleRequest := contract.SettleRequest{
   710  		ProviderID: providerID.Address,
   711  		HermesID:   hermesID.Address,
   712  	}
   713  
   714  	path := "transactor/stake/increase/"
   715  	if waitForBlockchain {
   716  		path += "sync"
   717  	} else {
   718  		path += "async"
   719  	}
   720  
   721  	response, err := client.http.Post(path, settleRequest)
   722  	if err != nil {
   723  		return err
   724  	}
   725  	defer response.Body.Close()
   726  
   727  	if response.StatusCode != http.StatusAccepted && response.StatusCode != http.StatusOK {
   728  		return errors.Wrap(err, "could not settle promise")
   729  	}
   730  	return nil
   731  }
   732  
   733  // SettleWithBeneficiaryStatus set new beneficiary address for the provided identity.
   734  func (client *Client) SettleWithBeneficiaryStatus(address string) (res contract.BeneficiaryTxStatus, err error) {
   735  	response, err := client.http.Get("identities/"+address+"/beneficiary-status", nil)
   736  	if err != nil {
   737  		return contract.BeneficiaryTxStatus{}, err
   738  	}
   739  	defer response.Body.Close()
   740  
   741  	if response.StatusCode != http.StatusOK {
   742  		return contract.BeneficiaryTxStatus{}, fmt.Errorf("expected 200 got %v", response.StatusCode)
   743  	}
   744  
   745  	err = parseResponseJSON(response, &res)
   746  	return res, err
   747  }
   748  
   749  // SettleWithBeneficiary set new beneficiary address for the provided identity.
   750  func (client *Client) SettleWithBeneficiary(address, beneficiary, hermesID string) error {
   751  	payload := contract.SettleWithBeneficiaryRequest{
   752  		ProviderID:  address,
   753  		HermesID:    hermesID,
   754  		Beneficiary: beneficiary,
   755  	}
   756  	response, err := client.http.Post("identities/"+address+"/beneficiary", payload)
   757  	if err != nil {
   758  		return err
   759  	}
   760  	defer response.Body.Close()
   761  
   762  	if response.StatusCode != http.StatusAccepted {
   763  		return fmt.Errorf("expected 202 got %v", response.StatusCode)
   764  	}
   765  
   766  	return nil
   767  }
   768  
   769  // DecreaseStake requests the decrease of stake via the transactor.
   770  func (client *Client) DecreaseStake(ID identity.Identity, amount *big.Int) error {
   771  	decreaseRequest := contract.DecreaseStakeRequest{
   772  		ID:     ID.Address,
   773  		Amount: amount,
   774  	}
   775  
   776  	path := "transactor/stake/decrease"
   777  
   778  	response, err := client.http.Post(path, decreaseRequest)
   779  	if err != nil {
   780  		return err
   781  	}
   782  	defer response.Body.Close()
   783  
   784  	if response.StatusCode != http.StatusAccepted && response.StatusCode != http.StatusOK {
   785  		return errors.Wrap(err, "could not decrease stake")
   786  	}
   787  	return nil
   788  }
   789  
   790  // WithdrawalHistory returns latest withdrawals for identity
   791  func (client *Client) WithdrawalHistory(address string) (res contract.SettlementListResponse, err error) {
   792  	params := url.Values{
   793  		"types":       []string{"withdrawal"},
   794  		"provider_id": []string{address},
   795  	}
   796  
   797  	path := fmt.Sprintf("transactor/settle/history?%s", params.Encode())
   798  	response, err := client.http.Get(path, nil)
   799  	if err != nil {
   800  		return contract.SettlementListResponse{}, err
   801  	}
   802  	defer response.Body.Close()
   803  
   804  	err = parseResponseJSON(response, &res)
   805  	return res, err
   806  }
   807  
   808  // MigrateHermes migrate from old to active Hermes
   809  func (client *Client) MigrateHermes(address string) error {
   810  	response, err := client.http.Post(fmt.Sprintf("identities/%s/migrate-hermes", address), nil)
   811  	if err != nil {
   812  		return err
   813  	}
   814  	defer response.Body.Close()
   815  
   816  	if response.StatusCode != http.StatusOK {
   817  		body, err := io.ReadAll(response.Body)
   818  		if err != nil {
   819  			return err
   820  		}
   821  		return fmt.Errorf("migration error: %s", body)
   822  	}
   823  
   824  	return nil
   825  }
   826  
   827  // MigrateHermesStatus check status of the migration
   828  func (client *Client) MigrateHermesStatus(address string) (contract.MigrationStatusResponse, error) {
   829  	var res contract.MigrationStatusResponse
   830  
   831  	response, err := client.http.Get(fmt.Sprintf("identities/%s/migrate-hermes/status", address), nil)
   832  	if err != nil {
   833  		return res, err
   834  	}
   835  	defer response.Body.Close()
   836  
   837  	err = parseResponseJSON(response, &res)
   838  
   839  	return res, err
   840  }
   841  
   842  // Beneficiary gets beneficiary address for the provided identity.
   843  func (client *Client) Beneficiary(address string) (res contract.IdentityBeneficiaryResponse, err error) {
   844  	response, err := client.http.Get("identities/"+address+"/beneficiary", nil)
   845  	if err != nil {
   846  		return contract.IdentityBeneficiaryResponse{}, err
   847  	}
   848  	defer response.Body.Close()
   849  
   850  	err = parseResponseJSON(response, &res)
   851  	return res, err
   852  }
   853  
   854  // SetMMNApiKey sets MMN's API key in config and registers node to MMN
   855  func (client *Client) SetMMNApiKey(data contract.MMNApiKeyRequest) error {
   856  	response, err := client.http.Post("mmn/api-key", data)
   857  	// non 200 status codes return a generic error and we can't use it, instead
   858  	// the response contains validation JSON which we can use to extract the error
   859  	if err != nil {
   860  		return err
   861  	}
   862  	defer response.Body.Close()
   863  
   864  	if response.StatusCode != 200 {
   865  		return apierror.Parse(response)
   866  	}
   867  
   868  	return nil
   869  }
   870  
   871  // IdentityReferralCode returns a referral token for the given identity.
   872  func (client *Client) IdentityReferralCode(identity string) (contract.ReferralTokenResponse, error) {
   873  	response, err := client.http.Get(fmt.Sprintf("identities/%v/referral", identity), nil)
   874  	if err != nil {
   875  		return contract.ReferralTokenResponse{}, err
   876  	}
   877  	defer response.Body.Close()
   878  
   879  	res := contract.ReferralTokenResponse{}
   880  	err = parseResponseJSON(response, &res)
   881  	return res, err
   882  }
   883  
   884  // OrderCreate creates a new order for currency exchange in pilvytis
   885  func (client *Client) OrderCreate(id identity.Identity, gw string, order contract.PaymentOrderRequest) (contract.PaymentOrderResponse, error) {
   886  	resp, err := client.http.Post(fmt.Sprintf("v2/identities/%s/%s/payment-order", id.Address, gw), order)
   887  	if err != nil {
   888  		return contract.PaymentOrderResponse{}, err
   889  	}
   890  	defer resp.Body.Close()
   891  
   892  	var res contract.PaymentOrderResponse
   893  	return res, parseResponseJSON(resp, &res)
   894  }
   895  
   896  // OrderGet returns a single order istance given it's ID.
   897  func (client *Client) OrderGet(address identity.Identity, orderID string) (contract.PaymentOrderResponse, error) {
   898  	path := fmt.Sprintf("v2/identities/%s/payment-order/%s", address.Address, orderID)
   899  	resp, err := client.http.Get(path, nil)
   900  	if err != nil {
   901  		return contract.PaymentOrderResponse{}, err
   902  	}
   903  	defer resp.Body.Close()
   904  
   905  	var res contract.PaymentOrderResponse
   906  	return res, parseResponseJSON(resp, &res)
   907  }
   908  
   909  // OrderGetAll returns all order istances for a given identity
   910  func (client *Client) OrderGetAll(id identity.Identity) ([]contract.PaymentOrderResponse, error) {
   911  	path := fmt.Sprintf("v2/identities/%s/payment-order", id.Address)
   912  	resp, err := client.http.Get(path, nil)
   913  	if err != nil {
   914  		return nil, err
   915  	}
   916  	defer resp.Body.Close()
   917  
   918  	var res []contract.PaymentOrderResponse
   919  	return res, parseResponseJSON(resp, &res)
   920  }
   921  
   922  // OrderInvoice returns a single order istance given it's ID.
   923  func (client *Client) OrderInvoice(address identity.Identity, orderID string) ([]byte, error) {
   924  	path := fmt.Sprintf("v2/identities/%s/payment-order/%s/invoice", address.Address, orderID)
   925  	resp, err := client.http.Get(path, nil)
   926  	if err != nil {
   927  		return nil, err
   928  	}
   929  	defer resp.Body.Close()
   930  
   931  	return io.ReadAll(resp.Body)
   932  }
   933  
   934  // PaymentOrderGateways returns all possible gateways and their data.
   935  func (client *Client) PaymentOrderGateways(optionsCurrency exchange.Currency) ([]contract.GatewaysResponse, error) {
   936  	query := url.Values{}
   937  	query.Set("options_currency", string(optionsCurrency))
   938  	resp, err := client.http.Get("v2/payment-order-gateways", query)
   939  	if err != nil {
   940  		return nil, err
   941  	}
   942  	defer resp.Body.Close()
   943  
   944  	var res []contract.GatewaysResponse
   945  	return res, parseResponseJSON(resp, &res)
   946  }
   947  
   948  // UpdateTerms takes a TermsRequest and sends it as an update
   949  // for the terms of use.
   950  func (client *Client) UpdateTerms(obj contract.TermsRequest) error {
   951  	resp, err := client.http.Post("terms", obj)
   952  	if err != nil {
   953  		return err
   954  	}
   955  	defer resp.Body.Close()
   956  	return nil
   957  }
   958  
   959  // FetchConfig - fetches current config
   960  func (client *Client) FetchConfig() (map[string]interface{}, error) {
   961  	resp, err := client.http.Get("config", nil)
   962  	if err != nil {
   963  		return nil, err
   964  	}
   965  	defer resp.Body.Close()
   966  
   967  	if resp.StatusCode < 200 || resp.StatusCode > 299 {
   968  		return nil, fmt.Errorf("fetching config failed with status: %d", resp.StatusCode)
   969  	}
   970  
   971  	var res map[string]interface{}
   972  	err = parseResponseJSON(resp, &res)
   973  	if err != nil {
   974  		return nil, err
   975  	}
   976  
   977  	data, ok := res["data"]
   978  	if !ok {
   979  		return nil, errors.New("no field named 'data' found in config")
   980  	}
   981  
   982  	config := data.(map[string]interface{})
   983  	return config, err
   984  }
   985  
   986  // SetConfig - set user config.
   987  func (client *Client) SetConfig(data map[string]interface{}) error {
   988  	req := struct {
   989  		Data map[string]interface{} `json:"data"`
   990  	}{
   991  		Data: data,
   992  	}
   993  	resp, err := client.http.Post("config/user", req)
   994  	if err != nil {
   995  		return err
   996  	}
   997  
   998  	defer resp.Body.Close()
   999  
  1000  	if resp.StatusCode < 200 || resp.StatusCode > 299 {
  1001  		return fmt.Errorf("failed to set user config with status: %d", resp.StatusCode)
  1002  	}
  1003  
  1004  	return nil
  1005  }