github.com/ethersphere/bee/v2@v2.2.0/pkg/settlement/swap/chequebook/factory.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
     6  
     7  import (
     8  	"errors"
     9  	"fmt"
    10  	"math/big"
    11  
    12  	"github.com/ethereum/go-ethereum/accounts/abi"
    13  	"github.com/ethereum/go-ethereum/common"
    14  	"github.com/ethersphere/bee/v2/pkg/sctx"
    15  	"github.com/ethersphere/bee/v2/pkg/transaction"
    16  	"github.com/ethersphere/bee/v2/pkg/util/abiutil"
    17  	"github.com/ethersphere/go-sw3-abi/sw3abi"
    18  	"golang.org/x/net/context"
    19  )
    20  
    21  var (
    22  	ErrInvalidFactory       = errors.New("not a valid factory contract")
    23  	ErrNotDeployedByFactory = errors.New("chequebook not deployed by factory")
    24  	errDecodeABI            = errors.New("could not decode abi data")
    25  
    26  	factoryABI                  = abiutil.MustParseABI(sw3abi.SimpleSwapFactoryABIv0_6_5)
    27  	simpleSwapDeployedEventType = factoryABI.Events["SimpleSwapDeployed"]
    28  )
    29  
    30  // Factory is the main interface for interacting with the chequebook factory.
    31  type Factory interface {
    32  	// ERC20Address returns the token for which this factory deploys chequebooks.
    33  	ERC20Address(ctx context.Context) (common.Address, error)
    34  	// Deploy deploys a new chequebook and returns once the transaction has been submitted.
    35  	Deploy(ctx context.Context, issuer common.Address, defaultHardDepositTimeoutDuration *big.Int, nonce common.Hash) (common.Hash, error)
    36  	// WaitDeployed waits for the deployment transaction to confirm and returns the chequebook address
    37  	WaitDeployed(ctx context.Context, txHash common.Hash) (common.Address, error)
    38  	// VerifyChequebook checks that the supplied chequebook has been deployed by this factory.
    39  	VerifyChequebook(ctx context.Context, chequebook common.Address) error
    40  }
    41  
    42  type factory struct {
    43  	backend            transaction.Backend
    44  	transactionService transaction.Service
    45  	address            common.Address // address of the factory to use for deployments
    46  }
    47  
    48  type simpleSwapDeployedEvent struct {
    49  	ContractAddress common.Address
    50  }
    51  
    52  // NewFactory creates a new factory service for the provided factory contract.
    53  func NewFactory(backend transaction.Backend, transactionService transaction.Service, address common.Address) Factory {
    54  	return &factory{
    55  		backend:            backend,
    56  		transactionService: transactionService,
    57  		address:            address,
    58  	}
    59  }
    60  
    61  // Deploy deploys a new chequebook and returns once the transaction has been submitted.
    62  func (c *factory) Deploy(ctx context.Context, issuer common.Address, defaultHardDepositTimeoutDuration *big.Int, nonce common.Hash) (common.Hash, error) {
    63  	callData, err := factoryABI.Pack("deploySimpleSwap", issuer, big.NewInt(0).Set(defaultHardDepositTimeoutDuration), nonce)
    64  	if err != nil {
    65  		return common.Hash{}, err
    66  	}
    67  
    68  	request := &transaction.TxRequest{
    69  		To:          &c.address,
    70  		Data:        callData,
    71  		GasPrice:    sctx.GetGasPrice(ctx),
    72  		GasLimit:    175000,
    73  		Value:       big.NewInt(0),
    74  		Description: "chequebook deployment",
    75  	}
    76  
    77  	txHash, err := c.transactionService.Send(ctx, request, transaction.DefaultTipBoostPercent)
    78  	if err != nil {
    79  		return common.Hash{}, err
    80  	}
    81  
    82  	return txHash, nil
    83  }
    84  
    85  // WaitDeployed waits for the deployment transaction to confirm and returns the chequebook address
    86  func (c *factory) WaitDeployed(ctx context.Context, txHash common.Hash) (common.Address, error) {
    87  	receipt, err := c.transactionService.WaitForReceipt(ctx, txHash)
    88  	if err != nil {
    89  		return common.Address{}, err
    90  	}
    91  
    92  	var event simpleSwapDeployedEvent
    93  	err = transaction.FindSingleEvent(&factoryABI, receipt, c.address, simpleSwapDeployedEventType, &event)
    94  	if err != nil {
    95  		return common.Address{}, fmt.Errorf("contract deployment failed: %w", err)
    96  	}
    97  
    98  	return event.ContractAddress, nil
    99  }
   100  
   101  func (c *factory) verifyChequebookAgainstFactory(ctx context.Context, factory, chequebook common.Address) (bool, error) {
   102  	callData, err := factoryABI.Pack("deployedContracts", chequebook)
   103  	if err != nil {
   104  		return false, err
   105  	}
   106  
   107  	output, err := c.transactionService.Call(ctx, &transaction.TxRequest{
   108  		To:   &factory,
   109  		Data: callData,
   110  	})
   111  	if err != nil {
   112  		return false, err
   113  	}
   114  
   115  	results, err := factoryABI.Unpack("deployedContracts", output)
   116  	if err != nil {
   117  		return false, err
   118  	}
   119  
   120  	if len(results) != 1 {
   121  		return false, errDecodeABI
   122  	}
   123  
   124  	deployed, ok := abi.ConvertType(results[0], new(bool)).(*bool)
   125  	if !ok || deployed == nil {
   126  		return false, errDecodeABI
   127  	}
   128  	if !*deployed {
   129  		return false, nil
   130  	}
   131  	return true, nil
   132  }
   133  
   134  // VerifyChequebook checks that the supplied chequebook has been deployed by a supported factory.
   135  func (c *factory) VerifyChequebook(ctx context.Context, chequebook common.Address) error {
   136  	deployed, err := c.verifyChequebookAgainstFactory(ctx, c.address, chequebook)
   137  	if err != nil {
   138  		return err
   139  	}
   140  	if deployed {
   141  		return nil
   142  	}
   143  	return ErrNotDeployedByFactory
   144  }
   145  
   146  // ERC20Address returns the token for which this factory deploys chequebooks.
   147  func (c *factory) ERC20Address(ctx context.Context) (common.Address, error) {
   148  	callData, err := factoryABI.Pack("ERC20Address")
   149  	if err != nil {
   150  		return common.Address{}, err
   151  	}
   152  
   153  	output, err := c.transactionService.Call(ctx, &transaction.TxRequest{
   154  		To:   &c.address,
   155  		Data: callData,
   156  	})
   157  	if err != nil {
   158  		return common.Address{}, err
   159  	}
   160  
   161  	results, err := factoryABI.Unpack("ERC20Address", output)
   162  	if err != nil {
   163  		return common.Address{}, err
   164  	}
   165  
   166  	if len(results) != 1 {
   167  		return common.Address{}, errDecodeABI
   168  	}
   169  
   170  	erc20Address, ok := abi.ConvertType(results[0], new(common.Address)).(*common.Address)
   171  	if !ok || erc20Address == nil {
   172  		return common.Address{}, errDecodeABI
   173  	}
   174  	return *erc20Address, nil
   175  }