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 }