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 }