github.com/ethersphere/bee/v2@v2.2.0/pkg/api/wallet_test.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_test
     6  
     7  import (
     8  	"context"
     9  	"math/big"
    10  	"net/http"
    11  	"testing"
    12  
    13  	"github.com/ethereum/go-ethereum/common"
    14  	"github.com/ethersphere/bee/v2/pkg/api"
    15  	"github.com/ethersphere/bee/v2/pkg/bigint"
    16  	"github.com/ethersphere/bee/v2/pkg/jsonhttp"
    17  	"github.com/ethersphere/bee/v2/pkg/jsonhttp/jsonhttptest"
    18  	erc20mock "github.com/ethersphere/bee/v2/pkg/settlement/swap/erc20/mock"
    19  	"github.com/ethersphere/bee/v2/pkg/transaction"
    20  	"github.com/ethersphere/bee/v2/pkg/transaction/backendmock"
    21  	transactionmock "github.com/ethersphere/bee/v2/pkg/transaction/mock"
    22  )
    23  
    24  func TestWallet(t *testing.T) {
    25  	t.Parallel()
    26  
    27  	t.Run("Okay", func(t *testing.T) {
    28  		t.Parallel()
    29  
    30  		srv, _, _, _ := newTestServer(t, testServerOptions{
    31  			Erc20Opts: []erc20mock.Option{
    32  				erc20mock.WithBalanceOfFunc(func(ctx context.Context, address common.Address) (*big.Int, error) {
    33  					return big.NewInt(10000000000000000), nil
    34  				}),
    35  			},
    36  			BackendOpts: []backendmock.Option{
    37  				backendmock.WithBalanceAt(func(ctx context.Context, address common.Address, block *big.Int) (*big.Int, error) {
    38  					return big.NewInt(2000000000000000000), nil
    39  				}),
    40  			},
    41  		})
    42  
    43  		jsonhttptest.Request(t, srv, http.MethodGet, "/wallet", http.StatusOK,
    44  			jsonhttptest.WithExpectedJSONResponse(api.WalletResponse{
    45  				BZZ:         bigint.Wrap(big.NewInt(10000000000000000)),
    46  				NativeToken: bigint.Wrap(big.NewInt(2000000000000000000)),
    47  				ChainID:     1,
    48  			}),
    49  		)
    50  	})
    51  
    52  	t.Run("500 - erc20 error", func(t *testing.T) {
    53  		t.Parallel()
    54  
    55  		srv, _, _, _ := newTestServer(t, testServerOptions{
    56  			BackendOpts: []backendmock.Option{
    57  				backendmock.WithBalanceAt(func(ctx context.Context, address common.Address, block *big.Int) (*big.Int, error) {
    58  					return new(big.Int), nil
    59  				}),
    60  			},
    61  		})
    62  
    63  		jsonhttptest.Request(t, srv, http.MethodGet, "/wallet", http.StatusInternalServerError,
    64  			jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{
    65  				Message: "unable to acquire erc20 balance",
    66  				Code:    500,
    67  			}))
    68  	})
    69  
    70  	t.Run("500 - chain backend error", func(t *testing.T) {
    71  		t.Parallel()
    72  
    73  		srv, _, _, _ := newTestServer(t, testServerOptions{
    74  			Erc20Opts: []erc20mock.Option{
    75  				erc20mock.WithBalanceOfFunc(func(ctx context.Context, address common.Address) (*big.Int, error) {
    76  					return new(big.Int), nil
    77  				}),
    78  			},
    79  		})
    80  
    81  		jsonhttptest.Request(t, srv, http.MethodGet, "/wallet", http.StatusInternalServerError,
    82  			jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{
    83  				Message: "unable to acquire balance from the chain backend",
    84  				Code:    500,
    85  			}))
    86  	})
    87  }
    88  
    89  func TestWalletWithdraw(t *testing.T) {
    90  	t.Parallel()
    91  
    92  	t.Run("address not whitelisted", func(t *testing.T) {
    93  		t.Parallel()
    94  
    95  		srv, _, _, _ := newTestServer(t, testServerOptions{})
    96  
    97  		jsonhttptest.Request(t, srv, http.MethodPost, "/wallet/withdraw/BZZ?address=0xaf&amount=99999999", http.StatusBadRequest,
    98  			jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{
    99  				Message: "provided address not whitelisted",
   100  				Code:    400,
   101  			}))
   102  	})
   103  
   104  	t.Run("invalid coin type", func(t *testing.T) {
   105  		t.Parallel()
   106  
   107  		srv, _, _, _ := newTestServer(t, testServerOptions{})
   108  
   109  		jsonhttptest.Request(t, srv, http.MethodPost, "/wallet/withdraw/BTC?address=0xaf&amount=99999999", http.StatusBadRequest,
   110  			jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{
   111  				Message: "only BZZ or NativeToken options are accepted",
   112  				Code:    400,
   113  			}))
   114  	})
   115  
   116  	t.Run("BZZ erc20 balance error", func(t *testing.T) {
   117  		t.Parallel()
   118  
   119  		srv, _, _, _ := newTestServer(t, testServerOptions{
   120  			WhitelistedAddr: "0xaf",
   121  		})
   122  
   123  		jsonhttptest.Request(t, srv, http.MethodPost, "/wallet/withdraw/BZZ?address=0xaf&amount=99999999", http.StatusInternalServerError,
   124  			jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{
   125  				Message: "unable to get balance",
   126  				Code:    500,
   127  			}))
   128  	})
   129  
   130  	t.Run("BZZ erc20 balance insufficient", func(t *testing.T) {
   131  		t.Parallel()
   132  
   133  		srv, _, _, _ := newTestServer(t, testServerOptions{
   134  			WhitelistedAddr: "0xaf",
   135  			Erc20Opts: []erc20mock.Option{
   136  				erc20mock.WithBalanceOfFunc(func(ctx context.Context, address common.Address) (*big.Int, error) {
   137  					return big.NewInt(88888888), nil
   138  				}),
   139  			},
   140  		})
   141  
   142  		jsonhttptest.Request(t, srv, http.MethodPost, "/wallet/withdraw/BZZ?address=0xaf&amount=99999999", http.StatusBadRequest,
   143  			jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{
   144  				Message: "not enough balance",
   145  				Code:    400,
   146  			}))
   147  	})
   148  
   149  	t.Run("BZZ erc20 transfer error", func(t *testing.T) {
   150  		t.Parallel()
   151  
   152  		srv, _, _, _ := newTestServer(t, testServerOptions{
   153  			WhitelistedAddr: "0xaf",
   154  			Erc20Opts: []erc20mock.Option{
   155  				erc20mock.WithBalanceOfFunc(func(ctx context.Context, address common.Address) (*big.Int, error) {
   156  					return big.NewInt(100000000), nil
   157  				}),
   158  			},
   159  		})
   160  
   161  		jsonhttptest.Request(t, srv, http.MethodPost, "/wallet/withdraw/BZZ?address=0xaf&amount=99999999", http.StatusInternalServerError,
   162  			jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{
   163  				Message: "unable to transfer amount",
   164  				Code:    500,
   165  			}))
   166  	})
   167  
   168  	t.Run("BZZ erc20 transfer ok", func(t *testing.T) {
   169  		t.Parallel()
   170  
   171  		txHash := common.HexToHash("0x00f")
   172  
   173  		srv, _, _, _ := newTestServer(t, testServerOptions{
   174  			WhitelistedAddr: "0xaf",
   175  			Erc20Opts: []erc20mock.Option{
   176  				erc20mock.WithBalanceOfFunc(func(ctx context.Context, address common.Address) (*big.Int, error) {
   177  					return big.NewInt(100000000), nil
   178  				}),
   179  				erc20mock.WithTransferFunc(func(ctx context.Context, address common.Address, value *big.Int) (common.Hash, error) {
   180  					if address != common.HexToAddress("0xaf") {
   181  						t.Fatalf("want addr 0xaf, got %s", address)
   182  					}
   183  					if value.Cmp(big.NewInt(99999999)) != 0 {
   184  						t.Fatalf("want value 99999999, got %s", value)
   185  					}
   186  					return txHash, nil
   187  				}),
   188  			},
   189  		})
   190  
   191  		jsonhttptest.Request(t, srv, http.MethodPost, "/wallet/withdraw/BZZ?address=0xaf&amount=99999999", http.StatusOK,
   192  			jsonhttptest.WithExpectedJSONResponse(api.WalletTxResponse{
   193  				TransactionHash: txHash,
   194  			}))
   195  	})
   196  
   197  	t.Run("native balance error", func(t *testing.T) {
   198  		t.Parallel()
   199  
   200  		srv, _, _, _ := newTestServer(t, testServerOptions{
   201  			WhitelistedAddr: "0xaf",
   202  		})
   203  
   204  		jsonhttptest.Request(t, srv, http.MethodPost, "/wallet/withdraw/NativeToken?address=0xaf&amount=99999999", http.StatusInternalServerError,
   205  			jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{
   206  				Message: "unable to acquire balance from the chain backend",
   207  				Code:    500,
   208  			}))
   209  	})
   210  
   211  	t.Run("native insufficient balance", func(t *testing.T) {
   212  		t.Parallel()
   213  
   214  		srv, _, _, _ := newTestServer(t, testServerOptions{
   215  			WhitelistedAddr: "0xaf",
   216  			BackendOpts: []backendmock.Option{
   217  				backendmock.WithBalanceAt(func(ctx context.Context, address common.Address, block *big.Int) (*big.Int, error) {
   218  					return big.NewInt(99999990), nil
   219  				}),
   220  			},
   221  		})
   222  
   223  		jsonhttptest.Request(t, srv, http.MethodPost, "/wallet/withdraw/NativeToken?address=0xaf&amount=99999999", http.StatusBadRequest,
   224  			jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{
   225  				Message: "not enough balance",
   226  				Code:    400,
   227  			}))
   228  	})
   229  
   230  	t.Run("native backend send error", func(t *testing.T) {
   231  		t.Parallel()
   232  
   233  		srv, _, _, _ := newTestServer(t, testServerOptions{
   234  			WhitelistedAddr: "0xaf",
   235  			BackendOpts: []backendmock.Option{
   236  				backendmock.WithBalanceAt(func(ctx context.Context, address common.Address, block *big.Int) (*big.Int, error) {
   237  					return big.NewInt(100000000), nil
   238  				}),
   239  			},
   240  		})
   241  
   242  		jsonhttptest.Request(t, srv, http.MethodPost, "/wallet/withdraw/NativeToken?address=0xaf&amount=99999999", http.StatusInternalServerError,
   243  			jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{
   244  				Message: "unable to transfer",
   245  				Code:    500,
   246  			}))
   247  	})
   248  
   249  	t.Run("native ok", func(t *testing.T) {
   250  		t.Parallel()
   251  
   252  		txHash := common.HexToHash("0x00f")
   253  
   254  		srv, _, _, _ := newTestServer(t, testServerOptions{
   255  			WhitelistedAddr: "0xaf",
   256  			BackendOpts: []backendmock.Option{
   257  				backendmock.WithBalanceAt(func(ctx context.Context, address common.Address, block *big.Int) (*big.Int, error) {
   258  					return big.NewInt(100000000), nil
   259  				}),
   260  			},
   261  			TransactionOpts: []transactionmock.Option{
   262  				transactionmock.WithSendFunc(func(ctx context.Context, tx *transaction.TxRequest, i int) (common.Hash, error) {
   263  					if tx.Value.Cmp(big.NewInt(99999999)) != 0 {
   264  						t.Fatalf("bad value, want 99999999, got %s", tx.Value)
   265  					}
   266  					if tx.To.Cmp(common.HexToAddress("0xaf")) != 0 {
   267  						t.Fatalf("bad address, want 0xaf, got %s", tx.To)
   268  					}
   269  					return txHash, nil
   270  				}),
   271  			},
   272  		})
   273  
   274  		jsonhttptest.Request(t, srv, http.MethodPost, "/wallet/withdraw/NativeToken?address=0xaf&amount=99999999", http.StatusOK,
   275  			jsonhttptest.WithExpectedJSONResponse(api.WalletTxResponse{
   276  				TransactionHash: txHash,
   277  			}))
   278  	})
   279  }