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 }