github.com/ethersphere/bee/v2@v2.2.0/pkg/api/wallet.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 "math/big" 9 "net/http" 10 "strings" 11 12 "slices" 13 14 "github.com/ethereum/go-ethereum/common" 15 "github.com/ethersphere/bee/v2/pkg/bigint" 16 "github.com/ethersphere/bee/v2/pkg/jsonhttp" 17 "github.com/ethersphere/bee/v2/pkg/sctx" 18 "github.com/ethersphere/bee/v2/pkg/transaction" 19 "github.com/gorilla/mux" 20 ) 21 22 type walletResponse struct { 23 BZZ *bigint.BigInt `json:"bzzBalance"` // the BZZ balance of the wallet associated with the eth address of the node 24 NativeToken *bigint.BigInt `json:"nativeTokenBalance"` // the native token balance of the wallet associated with the eth address of the node 25 ChainID int64 `json:"chainID"` // the id of the blockchain 26 ChequebookContractAddress common.Address `json:"chequebookContractAddress"` // the address of the chequebook contract 27 WalletAddress common.Address `json:"walletAddress"` // the address of the bee wallet 28 } 29 30 func (s *Service) walletHandler(w http.ResponseWriter, r *http.Request) { 31 logger := s.logger.WithName("get_wallet").Build() 32 33 nativeToken, err := s.chainBackend.BalanceAt(r.Context(), s.ethereumAddress, nil) 34 if err != nil { 35 logger.Debug("unable to acquire balance from the chain backend", "error", err) 36 logger.Error(nil, "unable to acquire balance from the chain backend") 37 jsonhttp.InternalServerError(w, "unable to acquire balance from the chain backend") 38 return 39 } 40 41 bzz, err := s.erc20Service.BalanceOf(r.Context(), s.ethereumAddress) 42 if err != nil { 43 logger.Debug("unable to acquire erc20 balance", "error", err) 44 logger.Error(nil, "unable to acquire erc20 balance") 45 jsonhttp.InternalServerError(w, "unable to acquire erc20 balance") 46 return 47 } 48 49 jsonhttp.OK(w, walletResponse{ 50 BZZ: bigint.Wrap(bzz), 51 NativeToken: bigint.Wrap(nativeToken), 52 ChainID: s.chainID, 53 ChequebookContractAddress: s.chequebook.Address(), 54 WalletAddress: s.ethereumAddress, 55 }) 56 } 57 58 type walletTxResponse struct { 59 TransactionHash common.Hash `json:"transactionHash"` 60 } 61 62 func (s *Service) walletWithdrawHandler(w http.ResponseWriter, r *http.Request) { 63 logger := s.logger.WithName("post_wallet_withdraw").Build() 64 65 queries := struct { 66 Amount *big.Int `map:"amount" validate:"required"` 67 Address *common.Address `map:"address" validate:"required"` 68 }{} 69 70 if response := s.mapStructure(r.URL.Query(), &queries); response != nil { 71 response("invalid query params", logger, w) 72 return 73 } 74 75 path := struct { 76 Coin *string `map:"coin" validate:"required"` 77 }{} 78 79 if response := s.mapStructure(mux.Vars(r), &path); response != nil { 80 response("invalid query params", logger, w) 81 return 82 } 83 84 var bzz bool 85 86 if strings.EqualFold("BZZ", *path.Coin) { 87 bzz = true 88 } else if !strings.EqualFold("NativeToken", *path.Coin) { 89 jsonhttp.BadRequest(w, "only BZZ or NativeToken options are accepted") 90 return 91 } 92 93 if !slices.Contains(s.whitelistedWithdrawalAddress, *queries.Address) { 94 jsonhttp.BadRequest(w, "provided address not whitelisted") 95 return 96 } 97 98 if bzz { 99 currentBalance, err := s.erc20Service.BalanceOf(r.Context(), s.ethereumAddress) 100 if err != nil { 101 logger.Error(err, "unable to get balance") 102 jsonhttp.InternalServerError(w, "unable to get balance") 103 return 104 } 105 106 if queries.Amount.Cmp(currentBalance) > 0 { 107 logger.Error(err, "not enough balance") 108 jsonhttp.BadRequest(w, "not enough balance") 109 return 110 } 111 112 txHash, err := s.erc20Service.Transfer(r.Context(), *queries.Address, queries.Amount) 113 if err != nil { 114 logger.Error(err, "unable to transfer") 115 jsonhttp.InternalServerError(w, "unable to transfer amount") 116 return 117 } 118 jsonhttp.OK(w, walletTxResponse{TransactionHash: txHash}) 119 return 120 } 121 122 nativeToken, err := s.chainBackend.BalanceAt(r.Context(), s.ethereumAddress, nil) 123 if err != nil { 124 logger.Error(err, "unable to acquire balance from the chain backend") 125 jsonhttp.InternalServerError(w, "unable to acquire balance from the chain backend") 126 return 127 } 128 129 if queries.Amount.Cmp(nativeToken) > 0 { 130 jsonhttp.BadRequest(w, "not enough balance") 131 return 132 } 133 134 req := &transaction.TxRequest{ 135 To: queries.Address, 136 GasPrice: sctx.GetGasPrice(r.Context()), 137 GasLimit: sctx.GetGasLimitWithDefault(r.Context(), 300_000), 138 Value: queries.Amount, 139 Description: "native token withdraw", 140 } 141 142 txHash, err := s.transaction.Send(r.Context(), req, transaction.DefaultTipBoostPercent) 143 if err != nil { 144 logger.Error(err, "unable to transfer") 145 jsonhttp.InternalServerError(w, "unable to transfer") 146 return 147 } 148 149 jsonhttp.OK(w, walletTxResponse{TransactionHash: txHash}) 150 }