func (r *CashChequeResult) Equal(o *CashChequeResult) bool {
	if r.Beneficiary != o.Beneficiary {
		return false
	}
	if r.Bounced != o.Bounced {
		return false
	}
	if r.Caller != o.Caller {
		return false
	}
	if r.CallerPayout.Cmp(o.CallerPayout) != 0 {
		return false
	}
	if r.CumulativePayout.Cmp(o.CumulativePayout) != 0 {
		return false
	}
	if r.Recipient != o.Recipient {
		return false
	}
	if r.TotalPayout.Cmp(o.TotalPayout) != 0 {
		return false
	}
	return true
}
Equal compares to CashChequeResults
package chequebook
import (
	"context"
	"errors"
	"fmt"
	"math/big"
	"github.com/ethereum/go-ethereum"
	"github.com/ethereum/go-ethereum/accounts/abi"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethsana/sana/pkg/sctx"
	"github.com/ethsana/sana/pkg/storage"
	"github.com/ethsana/sana/pkg/transaction"
)
var (
	ErrNoCashout = errors.New("no prior cashout")
)
type CashoutService interface {
	CashCheque(ctx context.Context, chequebook common.Address, recipient common.Address) (common.Hash, error)
	CashoutStatus(ctx context.Context, chequebookAddress common.Address) (*CashoutStatus, error)
}
type cashoutService struct {
	store              storage.StateStorer
	backend            transaction.Backend
	transactionService transaction.Service
	chequeStore        ChequeStore
}
type LastCashout struct {
	TxHash   common.Hash
	Cheque   SignedCheque  
	Result   *CashChequeResult
	Reverted bool
}
type CashoutStatus struct {
	Last           *LastCashout  
	UncashedAmount *big.Int      
}
type CashChequeResult struct {
	Beneficiary      common.Address  
	Recipient        common.Address  
	Caller           common.Address  
	TotalPayout      *big.Int        
	CumulativePayout *big.Int        
	CallerPayout     *big.Int        
	Bounced          bool            
}
type cashoutAction struct {
	TxHash common.Hash
	Cheque SignedCheque  
}
type chequeCashedEvent struct {
	Beneficiary      common.Address
	Recipient        common.Address
	Caller           common.Address
	TotalPayout      *big.Int
	CumulativePayout *big.Int
	CallerPayout     *big.Int
}
func NewCashoutService(
	store storage.StateStorer,
	backend transaction.Backend,
	transactionService transaction.Service,
	chequeStore ChequeStore,
) CashoutService {
	return &cashoutService{
		store:              store,
		backend:            backend,
		transactionService: transactionService,
		chequeStore:        chequeStore,
	}
}
func cashoutActionKey(chequebook common.Address) string {
	return fmt.Sprintf("swap_cashout_%x", chequebook)
}
func (s *cashoutService) paidOut(ctx context.Context, chequebook, beneficiary common.Address) (*big.Int, error) {
	callData, err := chequebookABI.Pack("paidOut", beneficiary)
	if err != nil {
		return nil, err
	}
	output, err := s.transactionService.Call(ctx, &transaction.TxRequest{
		To:   &chequebook,
		Data: callData,
	})
	if err != nil {
		return nil, err
	}
	results, err := chequebookABI.Unpack("paidOut", output)
	if err != nil {
		return nil, err
	}
	if len(results) != 1 {
		return nil, errDecodeABI
	}
	paidOut, ok := abi.ConvertType(results[0], new(big.Int)).(*big.Int)
	if !ok || paidOut == nil {
		return nil, errDecodeABI
	}
	return paidOut, nil
}
func (s *cashoutService) CashCheque(ctx context.Context, chequebook, recipient common.Address) (common.Hash, error) {
	cheque, err := s.chequeStore.LastCheque(chequebook)
	if err != nil {
		return common.Hash{}, err
	}
	callData, err := chequebookABI.Pack("cashChequeBeneficiary", recipient, cheque.CumulativePayout, cheque.Signature)
	if err != nil {
		return common.Hash{}, err
	}
	lim := sctx.GetGasLimit(ctx)
	if lim == 0 {
		lim = 300000
	}
	request := &transaction.TxRequest{
		To:          &chequebook,
		Data:        callData,
		GasPrice:    sctx.GetGasPrice(ctx),
		GasLimit:    lim,
		Value:       big.NewInt(0),
		Description: "cheque cashout",
	}
	txHash, err := s.transactionService.Send(ctx, request)
	if err != nil {
		return common.Hash{}, err
	}
	err = s.store.Put(cashoutActionKey(chequebook), &cashoutAction{
		TxHash: txHash,
		Cheque: *cheque,
	})
	if err != nil {
		return common.Hash{}, err
	}
	return txHash, nil
}
func (s *cashoutService) CashoutStatus(ctx context.Context, chequebookAddress common.Address) (*CashoutStatus, error) {
	cheque, err := s.chequeStore.LastCheque(chequebookAddress)
	if err != nil {
		return nil, err
	}
	var action cashoutAction
	err = s.store.Get(cashoutActionKey(chequebookAddress), &action)
	if err != nil {
		if errors.Is(err, storage.ErrNotFound) {
			return &CashoutStatus{
				Last:           nil,
				UncashedAmount: cheque.CumulativePayout,  
			}, nil
		}
		return nil, err
	}
	_, pending, err := s.backend.TransactionByHash(ctx, action.TxHash)
	if err != nil {
		if !errors.Is(err, ethereum.NotFound) {
			return nil, err
		}
		pending = true
	}
	if pending {
		return &CashoutStatus{
			Last: &LastCashout{
				TxHash:   action.TxHash,
				Cheque:   action.Cheque,
				Result:   nil,
				Reverted: false,
			},
			UncashedAmount: new(big.Int).Sub(cheque.CumulativePayout, action.Cheque.CumulativePayout),
		}, nil
	}
	receipt, err := s.backend.TransactionReceipt(ctx, action.TxHash)
	if err != nil {
		return nil, err
	}
	if receipt.Status == types.ReceiptStatusFailed {
		paidOut, err := s.paidOut(ctx, chequebookAddress, cheque.Beneficiary)
		if err != nil {
			return nil, err
		}
		return &CashoutStatus{
			Last: &LastCashout{
				TxHash:   action.TxHash,
				Cheque:   action.Cheque,
				Result:   nil,
				Reverted: true,
			},
			UncashedAmount: new(big.Int).Sub(cheque.CumulativePayout, paidOut),
		}, nil
	}
	result, err := s.parseCashChequeBeneficiaryReceipt(chequebookAddress, receipt)
	if err != nil {
		return nil, err
	}
	return &CashoutStatus{
		Last: &LastCashout{
			TxHash:   action.TxHash,
			Cheque:   action.Cheque,
			Result:   result,
			Reverted: false,
		},
		UncashedAmount: new(big.Int).Sub(cheque.CumulativePayout, result.CumulativePayout),
	}, nil
}
func (s *cashoutService) parseCashChequeBeneficiaryReceipt(chequebookAddress common.Address, receipt *types.Receipt) (*CashChequeResult, error) {
	result := &CashChequeResult{
		Bounced: false,
	}
	var cashedEvent chequeCashedEvent
	err := transaction.FindSingleEvent(&chequebookABI, receipt, chequebookAddress, chequeCashedEventType, &cashedEvent)
	if err != nil {
		return nil, err
	}
	result.Beneficiary = cashedEvent.Beneficiary
	result.Caller = cashedEvent.Caller
	result.CallerPayout = cashedEvent.CallerPayout
	result.TotalPayout = cashedEvent.TotalPayout
	result.CumulativePayout = cashedEvent.CumulativePayout
	result.Recipient = cashedEvent.Recipient
	err = transaction.FindSingleEvent(&chequebookABI, receipt, chequebookAddress, chequeBouncedEventType, nil)
	if err == nil {
		result.Bounced = true
	} else if !errors.Is(err, transaction.ErrEventNotFound) {
		return nil, err
	}
	return result, nil
}