github.com/ethersphere/bee/v2@v2.2.0/pkg/settlement/swap/chequebook/cashout_test.go (about) 1 // Copyright 2020 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 chequebook_test 6 7 import ( 8 "context" 9 "math/big" 10 "testing" 11 12 "github.com/ethereum/go-ethereum/common" 13 "github.com/ethereum/go-ethereum/core/types" 14 "github.com/ethersphere/bee/v2/pkg/settlement/swap/chequebook" 15 chequestoremock "github.com/ethersphere/bee/v2/pkg/settlement/swap/chequestore/mock" 16 storemock "github.com/ethersphere/bee/v2/pkg/statestore/mock" 17 "github.com/ethersphere/bee/v2/pkg/transaction/backendmock" 18 transactionmock "github.com/ethersphere/bee/v2/pkg/transaction/mock" 19 "github.com/ethersphere/bee/v2/pkg/util/abiutil" 20 "github.com/ethersphere/go-sw3-abi/sw3abi" 21 ) 22 23 var ( 24 chequebookABI = abiutil.MustParseABI(sw3abi.ERC20SimpleSwapABIv0_6_5) 25 chequeCashedEventType = chequebookABI.Events["ChequeCashed"] 26 chequeBouncedEventType = chequebookABI.Events["ChequeBounced"] 27 ) 28 29 func TestCashout(t *testing.T) { 30 t.Parallel() 31 32 chequebookAddress := common.HexToAddress("abcd") 33 recipientAddress := common.HexToAddress("efff") 34 txHash := common.HexToHash("dddd") 35 totalPayout := big.NewInt(100) 36 cumulativePayout := big.NewInt(500) 37 38 cheque := &chequebook.SignedCheque{ 39 Cheque: chequebook.Cheque{ 40 Beneficiary: common.HexToAddress("aaaa"), 41 CumulativePayout: cumulativePayout, 42 Chequebook: chequebookAddress, 43 }, 44 Signature: []byte{}, 45 } 46 47 store := storemock.NewStateStore() 48 cashoutService := chequebook.NewCashoutService( 49 store, 50 backendmock.New( 51 backendmock.WithTransactionByHashFunc(func(ctx context.Context, hash common.Hash) (tx *types.Transaction, isPending bool, err error) { 52 if hash != txHash { 53 t.Fatalf("fetching wrong transaction. wanted %v, got %v", txHash, hash) 54 } 55 return nil, false, nil 56 }), 57 backendmock.WithTransactionReceiptFunc(func(ctx context.Context, hash common.Hash) (*types.Receipt, error) { 58 if hash != txHash { 59 t.Fatalf("fetching receipt for transaction. wanted %v, got %v", txHash, hash) 60 } 61 62 logData, err := chequeCashedEventType.Inputs.NonIndexed().Pack(totalPayout, cumulativePayout, big.NewInt(0)) 63 if err != nil { 64 t.Fatal(err) 65 } 66 67 return &types.Receipt{ 68 Status: types.ReceiptStatusSuccessful, 69 Logs: []*types.Log{ 70 { 71 Address: chequebookAddress, 72 Topics: []common.Hash{ 73 chequeCashedEventType.ID, 74 common.BytesToHash(cheque.Beneficiary.Bytes()), 75 common.BytesToHash(recipientAddress.Bytes()), 76 common.BytesToHash(cheque.Beneficiary.Bytes()), 77 }, 78 Data: logData, 79 }, 80 }, 81 }, nil 82 }), 83 ), 84 transactionmock.New( 85 transactionmock.WithABISend(&chequebookABI, txHash, chequebookAddress, big.NewInt(0), "cashChequeBeneficiary", recipientAddress, cheque.CumulativePayout, cheque.Signature), 86 ), 87 chequestoremock.NewChequeStore( 88 chequestoremock.WithLastChequeFunc(func(c common.Address) (*chequebook.SignedCheque, error) { 89 if c != chequebookAddress { 90 t.Fatalf("using wrong chequebook. wanted %v, got %v", chequebookAddress, c) 91 } 92 return cheque, nil 93 }), 94 ), 95 ) 96 97 returnedTxHash, err := cashoutService.CashCheque(context.Background(), chequebookAddress, recipientAddress) 98 if err != nil { 99 t.Fatal(err) 100 } 101 102 if returnedTxHash != txHash { 103 t.Fatalf("returned wrong transaction hash. wanted %v, got %v", txHash, returnedTxHash) 104 } 105 106 status, err := cashoutService.CashoutStatus(context.Background(), chequebookAddress) 107 if err != nil { 108 t.Fatal(err) 109 } 110 111 verifyStatus(t, status, chequebook.CashoutStatus{ 112 Last: &chequebook.LastCashout{ 113 TxHash: txHash, 114 Cheque: *cheque, 115 Result: &chequebook.CashChequeResult{ 116 Beneficiary: cheque.Beneficiary, 117 Recipient: recipientAddress, 118 Caller: cheque.Beneficiary, 119 TotalPayout: totalPayout, 120 CumulativePayout: cumulativePayout, 121 CallerPayout: big.NewInt(0), 122 Bounced: false, 123 }, 124 Reverted: false, 125 }, 126 UncashedAmount: big.NewInt(0), 127 }) 128 } 129 130 func TestCashoutBounced(t *testing.T) { 131 t.Parallel() 132 133 chequebookAddress := common.HexToAddress("abcd") 134 recipientAddress := common.HexToAddress("efff") 135 txHash := common.HexToHash("dddd") 136 totalPayout := big.NewInt(100) 137 cumulativePayout := big.NewInt(500) 138 139 cheque := &chequebook.SignedCheque{ 140 Cheque: chequebook.Cheque{ 141 Beneficiary: common.HexToAddress("aaaa"), 142 CumulativePayout: cumulativePayout, 143 Chequebook: chequebookAddress, 144 }, 145 Signature: []byte{}, 146 } 147 148 store := storemock.NewStateStore() 149 cashoutService := chequebook.NewCashoutService( 150 store, 151 backendmock.New( 152 backendmock.WithTransactionByHashFunc(func(ctx context.Context, hash common.Hash) (*types.Transaction, bool, error) { 153 if hash != txHash { 154 t.Fatalf("fetching wrong transaction. wanted %v, got %v", txHash, hash) 155 } 156 return nil, false, nil 157 }), 158 backendmock.WithTransactionReceiptFunc(func(ctx context.Context, hash common.Hash) (*types.Receipt, error) { 159 if hash != txHash { 160 t.Fatalf("fetching receipt for transaction. wanted %v, got %v", txHash, hash) 161 } 162 163 chequeCashedLogData, err := chequeCashedEventType.Inputs.NonIndexed().Pack(totalPayout, cumulativePayout, big.NewInt(0)) 164 if err != nil { 165 t.Fatal(err) 166 } 167 168 return &types.Receipt{ 169 Status: types.ReceiptStatusSuccessful, 170 Logs: []*types.Log{ 171 { 172 Address: chequebookAddress, 173 Topics: []common.Hash{ 174 chequeCashedEventType.ID, 175 common.BytesToHash(cheque.Beneficiary.Bytes()), 176 common.BytesToHash(recipientAddress.Bytes()), 177 common.BytesToHash(cheque.Beneficiary.Bytes()), 178 }, 179 Data: chequeCashedLogData, 180 }, 181 { 182 Address: chequebookAddress, 183 Topics: []common.Hash{chequeBouncedEventType.ID}, 184 }, 185 }, 186 }, nil 187 }), 188 ), 189 transactionmock.New( 190 transactionmock.WithABISend(&chequebookABI, txHash, chequebookAddress, big.NewInt(0), "cashChequeBeneficiary", recipientAddress, cheque.CumulativePayout, cheque.Signature), 191 ), 192 chequestoremock.NewChequeStore( 193 chequestoremock.WithLastChequeFunc(func(c common.Address) (*chequebook.SignedCheque, error) { 194 if c != chequebookAddress { 195 t.Fatalf("using wrong chequebook. wanted %v, got %v", chequebookAddress, c) 196 } 197 return cheque, nil 198 }), 199 ), 200 ) 201 202 returnedTxHash, err := cashoutService.CashCheque(context.Background(), chequebookAddress, recipientAddress) 203 if err != nil { 204 t.Fatal(err) 205 } 206 207 if returnedTxHash != txHash { 208 t.Fatalf("returned wrong transaction hash. wanted %v, got %v", txHash, returnedTxHash) 209 } 210 211 status, err := cashoutService.CashoutStatus(context.Background(), chequebookAddress) 212 if err != nil { 213 t.Fatal(err) 214 } 215 216 verifyStatus(t, status, chequebook.CashoutStatus{ 217 Last: &chequebook.LastCashout{ 218 TxHash: txHash, 219 Cheque: *cheque, 220 Result: &chequebook.CashChequeResult{ 221 Beneficiary: cheque.Beneficiary, 222 Recipient: recipientAddress, 223 Caller: cheque.Beneficiary, 224 TotalPayout: totalPayout, 225 CumulativePayout: cumulativePayout, 226 CallerPayout: big.NewInt(0), 227 Bounced: true, 228 }, 229 Reverted: false, 230 }, 231 UncashedAmount: big.NewInt(0), 232 }) 233 } 234 235 func TestCashoutStatusReverted(t *testing.T) { 236 t.Parallel() 237 238 chequebookAddress := common.HexToAddress("abcd") 239 recipientAddress := common.HexToAddress("efff") 240 txHash := common.HexToHash("dddd") 241 cumulativePayout := big.NewInt(500) 242 onChainPaidOut := big.NewInt(100) 243 beneficiary := common.HexToAddress("aaaa") 244 245 cheque := &chequebook.SignedCheque{ 246 Cheque: chequebook.Cheque{ 247 Beneficiary: beneficiary, 248 CumulativePayout: cumulativePayout, 249 Chequebook: chequebookAddress, 250 }, 251 Signature: []byte{}, 252 } 253 254 store := storemock.NewStateStore() 255 cashoutService := chequebook.NewCashoutService( 256 store, 257 backendmock.New( 258 backendmock.WithTransactionByHashFunc(func(ctx context.Context, hash common.Hash) (tx *types.Transaction, isPending bool, err error) { 259 if hash != txHash { 260 t.Fatalf("fetching wrong transaction. wanted %v, got %v", txHash, hash) 261 } 262 return nil, false, nil 263 }), 264 backendmock.WithTransactionReceiptFunc(func(ctx context.Context, hash common.Hash) (*types.Receipt, error) { 265 if hash != txHash { 266 t.Fatalf("fetching receipt for transaction. wanted %v, got %v", txHash, hash) 267 } 268 return &types.Receipt{ 269 Status: types.ReceiptStatusFailed, 270 }, nil 271 }), 272 ), 273 transactionmock.New( 274 transactionmock.WithABISend(&chequebookABI, txHash, chequebookAddress, big.NewInt(0), "cashChequeBeneficiary", recipientAddress, cheque.CumulativePayout, cheque.Signature), 275 transactionmock.WithABICall(&chequebookABI, chequebookAddress, onChainPaidOut.FillBytes(make([]byte, 32)), "paidOut", beneficiary), 276 ), 277 chequestoremock.NewChequeStore( 278 chequestoremock.WithLastChequeFunc(func(c common.Address) (*chequebook.SignedCheque, error) { 279 if c != chequebookAddress { 280 t.Fatalf("using wrong chequebook. wanted %v, got %v", chequebookAddress, c) 281 } 282 return cheque, nil 283 }), 284 ), 285 ) 286 287 returnedTxHash, err := cashoutService.CashCheque(context.Background(), chequebookAddress, recipientAddress) 288 if err != nil { 289 t.Fatal(err) 290 } 291 292 if returnedTxHash != txHash { 293 t.Fatalf("returned wrong transaction hash. wanted %v, got %v", txHash, returnedTxHash) 294 } 295 296 status, err := cashoutService.CashoutStatus(context.Background(), chequebookAddress) 297 if err != nil { 298 t.Fatal(err) 299 } 300 301 verifyStatus(t, status, chequebook.CashoutStatus{ 302 Last: &chequebook.LastCashout{ 303 Reverted: true, 304 TxHash: txHash, 305 Cheque: *cheque, 306 }, 307 UncashedAmount: new(big.Int).Sub(cheque.CumulativePayout, onChainPaidOut), 308 }) 309 } 310 311 func TestCashoutStatusPending(t *testing.T) { 312 t.Parallel() 313 314 chequebookAddress := common.HexToAddress("abcd") 315 recipientAddress := common.HexToAddress("efff") 316 txHash := common.HexToHash("dddd") 317 cumulativePayout := big.NewInt(500) 318 319 cheque := &chequebook.SignedCheque{ 320 Cheque: chequebook.Cheque{ 321 Beneficiary: common.HexToAddress("aaaa"), 322 CumulativePayout: cumulativePayout, 323 Chequebook: chequebookAddress, 324 }, 325 Signature: []byte{}, 326 } 327 328 store := storemock.NewStateStore() 329 cashoutService := chequebook.NewCashoutService( 330 store, 331 backendmock.New( 332 backendmock.WithTransactionByHashFunc(func(ctx context.Context, hash common.Hash) (tx *types.Transaction, isPending bool, err error) { 333 if hash != txHash { 334 t.Fatalf("fetching wrong transaction. wanted %v, got %v", txHash, hash) 335 } 336 return nil, true, nil 337 }), 338 ), 339 transactionmock.New( 340 transactionmock.WithABISend(&chequebookABI, txHash, chequebookAddress, big.NewInt(0), "cashChequeBeneficiary", recipientAddress, cheque.CumulativePayout, cheque.Signature), 341 ), 342 chequestoremock.NewChequeStore( 343 chequestoremock.WithLastChequeFunc(func(c common.Address) (*chequebook.SignedCheque, error) { 344 if c != chequebookAddress { 345 t.Fatalf("using wrong chequebook. wanted %v, got %v", chequebookAddress, c) 346 } 347 return cheque, nil 348 }), 349 ), 350 ) 351 352 returnedTxHash, err := cashoutService.CashCheque(context.Background(), chequebookAddress, recipientAddress) 353 if err != nil { 354 t.Fatal(err) 355 } 356 357 if returnedTxHash != txHash { 358 t.Fatalf("returned wrong transaction hash. wanted %v, got %v", txHash, returnedTxHash) 359 } 360 361 status, err := cashoutService.CashoutStatus(context.Background(), chequebookAddress) 362 if err != nil { 363 t.Fatal(err) 364 } 365 366 verifyStatus(t, status, chequebook.CashoutStatus{ 367 Last: &chequebook.LastCashout{ 368 Reverted: false, 369 TxHash: txHash, 370 Cheque: *cheque, 371 Result: nil, 372 }, 373 UncashedAmount: big.NewInt(0), 374 }) 375 376 } 377 378 func verifyStatus(t *testing.T, status *chequebook.CashoutStatus, expected chequebook.CashoutStatus) { 379 t.Helper() 380 381 if expected.Last == nil { 382 if status.Last != nil { 383 t.Fatal("unexpected last cashout") 384 } 385 } else { 386 if status.Last == nil { 387 t.Fatal("no last cashout") 388 } 389 if status.Last.Reverted != expected.Last.Reverted { 390 t.Fatalf("wrong reverted value. wanted %v, got %v", expected.Last.Reverted, status.Last.Reverted) 391 } 392 if status.Last.TxHash != expected.Last.TxHash { 393 t.Fatalf("wrong transaction hash. wanted %v, got %v", expected.Last.TxHash, status.Last.TxHash) 394 } 395 if !status.Last.Cheque.Equal(&expected.Last.Cheque) { 396 t.Fatalf("wrong cheque in status. wanted %v, got %v", expected.Last.Cheque, status.Last.Cheque) 397 } 398 399 if expected.Last.Result != nil { 400 if !expected.Last.Result.Equal(status.Last.Result) { 401 t.Fatalf("wrong result. wanted %v, got %v", expected.Last.Result, status.Last.Result) 402 } 403 } 404 } 405 406 if status.UncashedAmount.Cmp(expected.UncashedAmount) != 0 { 407 t.Fatalf("wrong uncashed amount. wanted %d, got %d", expected.UncashedAmount, status.UncashedAmount) 408 } 409 }