github.com/ethersphere/bee/v2@v2.2.0/pkg/api/staking.go (about) 1 // Copyright 2022 The Swarm Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package api 6 7 import ( 8 "errors" 9 "math/big" 10 "net/http" 11 12 "github.com/ethersphere/bee/v2/pkg/bigint" 13 14 "github.com/ethersphere/bee/v2/pkg/jsonhttp" 15 "github.com/ethersphere/bee/v2/pkg/storageincentives/staking" 16 "github.com/gorilla/mux" 17 ) 18 19 func (s *Service) stakingAccessHandler(h http.Handler) http.Handler { 20 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 21 if !s.stakingSem.TryAcquire(1) { 22 s.logger.Debug("staking access: simultaneous on-chain operations not supported") 23 s.logger.Error(nil, "staking access: simultaneous on-chain operations not supported") 24 jsonhttp.TooManyRequests(w, "simultaneous on-chain operations not supported") 25 return 26 } 27 defer s.stakingSem.Release(1) 28 29 h.ServeHTTP(w, r) 30 }) 31 } 32 33 type getStakeResponse struct { 34 StakedAmount *bigint.BigInt `json:"stakedAmount"` 35 } 36 37 type getWithdrawableResponse struct { 38 WithdrawableAmount *bigint.BigInt `json:"withdrawableAmount"` 39 } 40 type stakeTransactionReponse struct { 41 TxHash string `json:"txHash"` 42 } 43 44 func (s *Service) stakingDepositHandler(w http.ResponseWriter, r *http.Request) { 45 logger := s.logger.WithName("post_stake_deposit").Build() 46 47 paths := struct { 48 Amount *big.Int `map:"amount" validate:"required"` 49 }{} 50 if response := s.mapStructure(mux.Vars(r), &paths); response != nil { 51 response("invalid path params", logger, w) 52 return 53 } 54 55 txHash, err := s.stakingContract.DepositStake(r.Context(), paths.Amount) 56 if err != nil { 57 if errors.Is(err, staking.ErrInsufficientStakeAmount) { 58 logger.Debug("insufficient stake amount", "minimum_stake", staking.MinimumStakeAmount, "error", err) 59 logger.Error(nil, "insufficient stake amount") 60 jsonhttp.BadRequest(w, "insufficient stake amount") 61 return 62 } 63 if errors.Is(err, staking.ErrNotImplemented) { 64 logger.Debug("not implemented", "error", err) 65 logger.Error(nil, "not implemented") 66 jsonhttp.NotImplemented(w, "not implemented") 67 return 68 } 69 if errors.Is(err, staking.ErrInsufficientFunds) { 70 logger.Debug("out of funds", "error", err) 71 logger.Error(nil, "out of funds") 72 jsonhttp.BadRequest(w, "out of funds") 73 return 74 } 75 logger.Debug("deposit failed", "error", err) 76 logger.Error(nil, "deposit failed") 77 jsonhttp.InternalServerError(w, "cannot stake") 78 return 79 } 80 jsonhttp.OK(w, stakeTransactionReponse{ 81 TxHash: txHash.String(), 82 }) 83 } 84 85 func (s *Service) getPotentialStake(w http.ResponseWriter, r *http.Request) { 86 logger := s.logger.WithName("get_stake").Build() 87 88 stakedAmount, err := s.stakingContract.GetPotentialStake(r.Context()) 89 if err != nil { 90 logger.Debug("get staked amount failed", "overlayAddr", s.overlay, "error", err) 91 logger.Error(nil, "get staked amount failed") 92 jsonhttp.InternalServerError(w, "get staked amount failed") 93 return 94 } 95 96 jsonhttp.OK(w, getStakeResponse{StakedAmount: bigint.Wrap(stakedAmount)}) 97 } 98 99 func (s *Service) getWithdrawableStakeHandler(w http.ResponseWriter, r *http.Request) { 100 logger := s.logger.WithName("get_stake").Build() 101 102 withdrawableAmount, err := s.stakingContract.GetWithdrawableStake(r.Context()) 103 if err != nil { 104 logger.Debug("get staked amount failed", "overlayAddr", s.overlay, "error", err) 105 logger.Error(nil, "get staked amount failed") 106 jsonhttp.InternalServerError(w, "get staked amount failed") 107 return 108 } 109 110 jsonhttp.OK(w, getWithdrawableResponse{WithdrawableAmount: bigint.Wrap(withdrawableAmount)}) 111 } 112 113 func (s *Service) withdrawStakeHandler(w http.ResponseWriter, r *http.Request) { 114 logger := s.logger.WithName("withdraw_stake").Build() 115 116 txHash, err := s.stakingContract.WithdrawStake(r.Context()) 117 if err != nil { 118 if errors.Is(err, staking.ErrInsufficientStake) { 119 logger.Debug("insufficient stake", "overlayAddr", s.overlay, "error", err) 120 logger.Error(nil, "insufficient stake") 121 jsonhttp.BadRequest(w, "insufficient stake to withdraw") 122 return 123 } 124 logger.Debug("withdraw stake failed", "error", err) 125 logger.Error(nil, "withdraw stake failed") 126 jsonhttp.InternalServerError(w, "cannot withdraw stake") 127 return 128 } 129 130 jsonhttp.OK(w, stakeTransactionReponse{TxHash: txHash.String()}) 131 } 132 133 func (s *Service) migrateStakeHandler(w http.ResponseWriter, r *http.Request) { 134 logger := s.logger.WithName("migrate_stake").Build() 135 136 txHash, err := s.stakingContract.MigrateStake(r.Context()) 137 if err != nil { 138 if errors.Is(err, staking.ErrInsufficientStake) { 139 logger.Debug("insufficient stake", "overlayAddr", s.overlay, "error", err) 140 logger.Error(nil, "insufficient stake") 141 jsonhttp.BadRequest(w, "insufficient stake to migrate") 142 return 143 } 144 if errors.Is(err, staking.ErrNotPaused) { 145 logger.Debug("contract is not paused", "error", err) 146 logger.Error(nil, "contract is not paused") 147 jsonhttp.BadRequest(w, "contract is not paused") 148 return 149 } 150 logger.Debug("migrate stake failed", "error", err) 151 logger.Error(nil, "migrate stake failed") 152 jsonhttp.InternalServerError(w, "cannot migrate stake") 153 return 154 } 155 156 jsonhttp.OK(w, stakeTransactionReponse{TxHash: txHash.String()}) 157 }