diff --git a/pkg/api/router.go b/pkg/api/router.go index 3d61fc98c00..987f4b995dd 100644 --- a/pkg/api/router.go +++ b/pkg/api/router.go @@ -563,16 +563,14 @@ func (s *Service) mountBusinessDebug() { )) handle("/wallet", web.ChainHandlers( - s.checkChequebookAvailability, - s.checkSwapAvailability, + s.checkChainAvailability, web.FinalHandler(jsonhttp.MethodHandler{ "GET": http.HandlerFunc(s.walletHandler), }), )) handle("/wallet/withdraw/{coin}", web.ChainHandlers( - s.checkChequebookAvailability, - s.checkSwapAvailability, + s.checkChainAvailability, web.FinalHandler(jsonhttp.MethodHandler{ "POST": web.ChainHandlers( s.gasConfigMiddleware("wallet withdraw"), diff --git a/pkg/api/router_test.go b/pkg/api/router_test.go index 8c13f459375..4e972f9f1a6 100644 --- a/pkg/api/router_test.go +++ b/pkg/api/router_test.go @@ -290,8 +290,8 @@ func TestEndpointOptions(t *testing.T) { {"/chequebook/address", []string{"GET"}, http.StatusNoContent}, {"/chequebook/deposit", []string{"POST"}, http.StatusNoContent}, {"/chequebook/withdraw", []string{"POST"}, http.StatusNoContent}, - {"/wallet", nil, http.StatusForbidden}, - {"/wallet/withdraw/{coin}", nil, http.StatusForbidden}, + {"/wallet", []string{"GET"}, http.StatusNoContent}, + {"/wallet/withdraw/{coin}", []string{"POST"}, http.StatusNoContent}, {"/stamps", []string{"GET"}, http.StatusNoContent}, {"/stamps/{batch_id}", []string{"GET"}, http.StatusNoContent}, {"/stamps/{batch_id}/buckets", []string{"GET"}, http.StatusNoContent}, @@ -385,8 +385,8 @@ func TestEndpointOptions(t *testing.T) { {"/chequebook/address", nil, http.StatusForbidden}, {"/chequebook/deposit", nil, http.StatusForbidden}, {"/chequebook/withdraw", nil, http.StatusForbidden}, - {"/wallet", nil, http.StatusForbidden}, - {"/wallet/withdraw/{coin}", nil, http.StatusForbidden}, + {"/wallet", []string{"GET"}, http.StatusNoContent}, + {"/wallet/withdraw/{coin}", []string{"POST"}, http.StatusNoContent}, {"/stamps", []string{"GET"}, http.StatusNoContent}, {"/stamps/{batch_id}", []string{"GET"}, http.StatusNoContent}, {"/stamps/{batch_id}/buckets", []string{"GET"}, http.StatusNoContent}, diff --git a/pkg/api/wallet.go b/pkg/api/wallet.go index 75674e1f48b..57f3c2de8e3 100644 --- a/pkg/api/wallet.go +++ b/pkg/api/wallet.go @@ -37,12 +37,15 @@ func (s *Service) walletHandler(w http.ResponseWriter, r *http.Request) { return } - bzz, err := s.erc20Service.BalanceOf(r.Context(), s.ethereumAddress) - if err != nil { - logger.Debug("unable to acquire erc20 balance", "error", err) - logger.Error(nil, "unable to acquire erc20 balance") - jsonhttp.InternalServerError(w, "unable to acquire erc20 balance") - return + bzz := new(big.Int) + if s.erc20Service != nil { + bzz, err = s.erc20Service.BalanceOf(r.Context(), s.ethereumAddress) + if err != nil { + logger.Debug("unable to acquire erc20 balance", "error", err) + logger.Error(nil, "unable to acquire erc20 balance") + jsonhttp.InternalServerError(w, "unable to acquire erc20 balance") + return + } } jsonhttp.OK(w, walletResponse{ @@ -95,6 +98,10 @@ func (s *Service) walletWithdrawHandler(w http.ResponseWriter, r *http.Request) } if bzz { + if s.erc20Service == nil { + jsonhttp.ServiceUnavailable(w, "BZZ token address unavailable") + return + } currentBalance, err := s.erc20Service.BalanceOf(r.Context(), s.ethereumAddress) if err != nil { logger.Error(err, "unable to get balance") diff --git a/pkg/api/wallet_test.go b/pkg/api/wallet_test.go index d5c09efaeeb..1633b8f4249 100644 --- a/pkg/api/wallet_test.go +++ b/pkg/api/wallet_test.go @@ -84,6 +84,58 @@ func TestWallet(t *testing.T) { Code: 500, })) }) + + t.Run("swap disabled", func(t *testing.T) { + t.Parallel() + + srv, _, _, _ := newTestServer(t, testServerOptions{ + SwapDisabled: true, + Erc20Opts: []erc20mock.Option{ + erc20mock.WithBalanceOfFunc(func(ctx context.Context, address common.Address) (*big.Int, error) { + return big.NewInt(10000000000000000), nil + }), + }, + BackendOpts: []backendmock.Option{ + backendmock.WithBalanceAt(func(ctx context.Context, address common.Address, block *big.Int) (*big.Int, error) { + return big.NewInt(2000000000000000000), nil + }), + }, + }) + + jsonhttptest.Request(t, srv, http.MethodGet, "/wallet", http.StatusOK, + jsonhttptest.WithExpectedJSONResponse(api.WalletResponse{ + BZZ: bigint.Wrap(big.NewInt(10000000000000000)), + NativeToken: bigint.Wrap(big.NewInt(2000000000000000000)), + ChainID: 1, + }), + ) + }) + + t.Run("chequebook disabled", func(t *testing.T) { + t.Parallel() + + srv, _, _, _ := newTestServer(t, testServerOptions{ + ChequebookDisabled: true, + Erc20Opts: []erc20mock.Option{ + erc20mock.WithBalanceOfFunc(func(ctx context.Context, address common.Address) (*big.Int, error) { + return big.NewInt(10000000000000000), nil + }), + }, + BackendOpts: []backendmock.Option{ + backendmock.WithBalanceAt(func(ctx context.Context, address common.Address, block *big.Int) (*big.Int, error) { + return big.NewInt(2000000000000000000), nil + }), + }, + }) + + jsonhttptest.Request(t, srv, http.MethodGet, "/wallet", http.StatusOK, + jsonhttptest.WithExpectedJSONResponse(api.WalletResponse{ + BZZ: bigint.Wrap(big.NewInt(10000000000000000)), + NativeToken: bigint.Wrap(big.NewInt(2000000000000000000)), + ChainID: 1, + }), + ) + }) } func TestWalletWithdraw(t *testing.T) { diff --git a/pkg/node/node.go b/pkg/node/node.go index 53d96ec5419..a01938891f7 100644 --- a/pkg/node/node.go +++ b/pkg/node/node.go @@ -393,6 +393,10 @@ func NewBee( chainEnabled := isChainEnabled(o, o.BlockchainRpcEndpoint, logger) + if o.SwapEnable && !chainEnabled { + return nil, errors.New("swap is enabled but the chain backend is not; provide --blockchain-rpc-endpoint or disable swap") + } + var batchStore postage.Storer = new(postage.NoOpBatchStore) var evictFn func([]byte) error @@ -535,46 +539,56 @@ func NewBee( } } - if o.SwapEnable { - chequebookFactory, err := InitChequebookFactory(logger, chainBackend, chainID, transactionService, o.SwapFactoryAddress) - if err != nil { - return nil, fmt.Errorf("init chequebook factory: %w", err) + if chainEnabled { + chequebookFactory, ferr := InitChequebookFactory(logger, chainBackend, chainID, transactionService, o.SwapFactoryAddress) + if o.SwapEnable && ferr != nil { + return nil, fmt.Errorf("init chequebook factory: %w", ferr) } - erc20Address, err := chequebookFactory.ERC20Address(ctx) - if err != nil { - return nil, fmt.Errorf("factory fail: %w", err) + if ferr == nil { + erc20Address, err := chequebookFactory.ERC20Address(ctx) + if err != nil { + if o.SwapEnable { + return nil, fmt.Errorf("factory fail: %w", err) + } + logger.Warning("unable to resolve ERC20 token address; BZZ balance will be unavailable via /wallet", "error", err) + } else { + erc20Service = erc20.New(transactionService, erc20Address) + } + } else { + logger.Warning("unable to init chequebook factory; BZZ balance will be unavailable via /wallet", "error", ferr) } - erc20Service = erc20.New(transactionService, erc20Address) + if o.SwapEnable { + if o.ChequebookEnable { + var err error + chequebookService, err = InitChequebookService( + ctx, + logger, + stateStore, + signer, + chainID, + chainBackend, + overlayEthAddress, + transactionService, + chequebookFactory, + o.SwapInitialDeposit, + erc20Service, + ) + if err != nil { + return nil, fmt.Errorf("init chequebook service: %w", err) + } + } - if o.ChequebookEnable && chainEnabled { - chequebookService, err = InitChequebookService( - ctx, - logger, + chequeStore, cashoutService = initChequeStoreCashout( stateStore, - signer, - chainID, chainBackend, + chequebookFactory, + chainID, overlayEthAddress, transactionService, - chequebookFactory, - o.SwapInitialDeposit, - erc20Service, ) - if err != nil { - return nil, fmt.Errorf("init chequebook service: %w", err) - } } - - chequeStore, cashoutService = initChequeStoreCashout( - stateStore, - chainBackend, - chequebookFactory, - chainID, - overlayEthAddress, - transactionService, - ) } lightNodes := lightnode.NewContainer(swarmAddress)