diff --git a/go.mod b/go.mod index 7c1fd5f8d2..59de86e3b1 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/kylelemons/godebug v1.1.0 github.com/leanovate/gopter v0.2.11 github.com/lib/pq v1.10.9 + github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4 github.com/onsi/gomega v1.36.2 github.com/pelletier/go-toml/v2 v2.2.4 github.com/pkg/errors v0.9.1 @@ -125,7 +126,6 @@ require ( github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect - github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4 // indirect github.com/mitchellh/pointerstructure v1.2.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect diff --git a/pkg/config/toml/config.go b/pkg/config/toml/config.go index 9e6cbde164..d64f0ff737 100644 --- a/pkg/config/toml/config.go +++ b/pkg/config/toml/config.go @@ -509,9 +509,6 @@ func (c *Transactions) ValidateConfig() (err error) { if c.AutoPurge.Enabled != nil && !*c.AutoPurge.Enabled { err = multierr.Append(err, commonconfig.ErrInvalid{Name: "AutoPurge.Enabled", Value: false, Msg: "cannot be false if DualBroadcast is enabled"}) } - if c.AutoPurge.DetectionApiUrl == nil { - err = multierr.Append(err, commonconfig.ErrMissing{Name: "AutoPurge.DetectionApiUrl", Msg: "must be set if DualBroadcast is enabled"}) - } if c.AutoPurge.Threshold == nil { err = multierr.Append(err, commonconfig.ErrMissing{Name: "AutoPurge.Threshold", Msg: "needs to be set if auto-purge feature is enabled"}) } else if *c.AutoPurge.Threshold == 0 { diff --git a/pkg/txm/clientwrappers/dual_broadcast_client.go b/pkg/txm/clientwrappers/dualbroadcast/flashbots_client.go similarity index 79% rename from pkg/txm/clientwrappers/dual_broadcast_client.go rename to pkg/txm/clientwrappers/dualbroadcast/flashbots_client.go index aaafd974ac..9313490e2d 100644 --- a/pkg/txm/clientwrappers/dual_broadcast_client.go +++ b/pkg/txm/clientwrappers/dualbroadcast/flashbots_client.go @@ -1,4 +1,4 @@ -package clientwrappers +package dualbroadcast import ( "bytes" @@ -17,28 +17,31 @@ import ( "github.com/smartcontractkit/chainlink-evm/pkg/client" "github.com/smartcontractkit/chainlink-evm/pkg/keys" + "github.com/smartcontractkit/chainlink-evm/pkg/txm" "github.com/smartcontractkit/chainlink-evm/pkg/txm/types" ) -type DualBroadcastClient struct { +var _ txm.Client = &FlashbotsClient{} + +type FlashbotsClient struct { c client.Client keystore keys.MessageSigner customURL *url.URL } -func NewDualBroadcastClient(c client.Client, keystore keys.MessageSigner, customURL *url.URL) *DualBroadcastClient { - return &DualBroadcastClient{ +func NewFlashbotsClient(c client.Client, keystore keys.MessageSigner, customURL *url.URL) *FlashbotsClient { + return &FlashbotsClient{ c: c, keystore: keystore, customURL: customURL, } } -func (d *DualBroadcastClient) NonceAt(ctx context.Context, address common.Address, blockNumber *big.Int) (uint64, error) { +func (d *FlashbotsClient) NonceAt(ctx context.Context, address common.Address, blockNumber *big.Int) (uint64, error) { return d.c.NonceAt(ctx, address, blockNumber) } -func (d *DualBroadcastClient) PendingNonceAt(ctx context.Context, address common.Address) (uint64, error) { +func (d *FlashbotsClient) PendingNonceAt(ctx context.Context, address common.Address) (uint64, error) { body := []byte(fmt.Sprintf(`{"jsonrpc":"2.0","method":"eth_getTransactionCount","params":["%s","pending"], "id":1}`, address.String())) response, err := d.signAndPostMessage(ctx, address, body, "") if err != nil { @@ -52,7 +55,7 @@ func (d *DualBroadcastClient) PendingNonceAt(ctx context.Context, address common return nonce, nil } -func (d *DualBroadcastClient) SendTransaction(ctx context.Context, tx *types.Transaction, attempt *types.Attempt) error { +func (d *FlashbotsClient) SendTransaction(ctx context.Context, tx *types.Transaction, attempt *types.Attempt) error { meta, err := tx.GetMeta() if err != nil { return err @@ -75,7 +78,7 @@ func (d *DualBroadcastClient) SendTransaction(ctx context.Context, tx *types.Tra return d.c.SendTransaction(ctx, attempt.SignedTransaction) } -func (d *DualBroadcastClient) signAndPostMessage(ctx context.Context, address common.Address, body []byte, urlParams string) (result string, err error) { +func (d *FlashbotsClient) signAndPostMessage(ctx context.Context, address common.Address, body []byte, urlParams string) (result string, err error) { bodyReader := bytes.NewReader(body) postReq, err := http.NewRequestWithContext(ctx, http.MethodPost, d.customURL.String()+"?"+urlParams, bodyReader) if err != nil { diff --git a/pkg/txm/clientwrappers/dualbroadcast/meta_client.go b/pkg/txm/clientwrappers/dualbroadcast/meta_client.go new file mode 100644 index 0000000000..e1fac98822 --- /dev/null +++ b/pkg/txm/clientwrappers/dualbroadcast/meta_client.go @@ -0,0 +1,505 @@ +package dualbroadcast + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "math/big" + "net/http" + "net/url" + "strings" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + evmtypes "github.com/ethereum/go-ethereum/core/types" + + "github.com/mitchellh/mapstructure" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" + + "github.com/smartcontractkit/chainlink-evm/pkg/txm" + "github.com/smartcontractkit/chainlink-evm/pkg/txm/types" +) + +const ( + timeout = time.Second * 5 + metaABI = `[ + { + "type": "function", + "name": "metacall", + "stateMutability": "payable", + "inputs": [ + { + "name": "userOp", + "type": "tuple", + "components": [ + { "name": "from", "type": "address" }, + { "name": "to", "type": "address" }, + { "name": "value", "type": "uint256" }, + { "name": "gas", "type": "uint256" }, + { "name": "maxFeePerGas", "type": "uint256" }, + { "name": "nonce", "type": "uint256" }, + { "name": "deadline", "type": "uint256" }, + { "name": "dapp", "type": "address" }, + { "name": "control", "type": "address" }, + { "name": "callConfig", "type": "uint32" }, + { "name": "dappGasLimit", "type": "uint32" }, + { "name": "solverGasLimit", "type": "uint32" }, + { "name": "bundlerSurchargeRate", "type": "uint24" }, + { "name": "sessionKey", "type": "address" }, + { "name": "data", "type": "bytes" }, + { "name": "signature", "type": "bytes" } + ] + }, + { + "name": "solverOps", + "type": "tuple[]", + "components": [ + { "name": "from", "type": "address" }, + { "name": "to", "type": "address" }, + { "name": "value", "type": "uint256" }, + { "name": "gas", "type": "uint256" }, + { "name": "maxFeePerGas", "type": "uint256" }, + { "name": "deadline", "type": "uint256" }, + { "name": "solver", "type": "address" }, + { "name": "control", "type": "address" }, + { "name": "userOpHash", "type": "bytes32" }, + { "name": "bidToken", "type": "address" }, + { "name": "bidAmount", "type": "uint256" }, + { "name": "data", "type": "bytes" }, + { "name": "signature", "type": "bytes" } + ] + }, + { + "name": "dAppOp", + "type": "tuple", + "components": [ + { "name": "from", "type": "address" }, + { "name": "to", "type": "address" }, + { "name": "nonce", "type": "uint256" }, + { "name": "deadline", "type": "uint256" }, + { "name": "control", "type": "address" }, + { "name": "bundler", "type": "address" }, + { "name": "userOpHash", "type": "bytes32" }, + { "name": "callChainHash", "type": "bytes32" }, + { "name": "signature", "type": "bytes" } + ] + }, + { "name": "gasRefundBeneficiary", "type": "address" } + ], + "outputs": [ { "name": "auctionWon", "type": "bool" } ] + } +]` + ABI = `[ + { + "inputs": [ + { + "internalType": "address", + "type": "address" + }, + { + "internalType": "bytes", + "type": "bytes" + } + ], + "name": "update", + "type": "function" + } +]` +) + +var _ txm.Client = &MetaClient{} + +type MetaClientKeystore interface { + SignMessage(ctx context.Context, address common.Address, data []byte) ([]byte, error) + SignTx(ctx context.Context, fromAddress common.Address, tx *evmtypes.Transaction) (*evmtypes.Transaction, error) +} + +type MetaClientRPC interface { + NonceAt(context.Context, common.Address, *big.Int) (uint64, error) + PendingNonceAt(context.Context, common.Address) (uint64, error) + SendTransaction(context.Context, *evmtypes.Transaction) error +} + +type MetaClient struct { + lggr logger.SugaredLogger + c MetaClientRPC + ks MetaClientKeystore + customURL *url.URL + chainID *big.Int +} + +func NewMetaClient(lggr logger.Logger, c MetaClientRPC, ks MetaClientKeystore, customURL *url.URL, chainID *big.Int) *MetaClient { + return &MetaClient{ + lggr: logger.Sugared(logger.Named(lggr, "Txm.Txm.MetaClient")), + c: c, + ks: ks, + customURL: customURL, + chainID: chainID, + } +} + +func (a *MetaClient) NonceAt(ctx context.Context, address common.Address, blockNumber *big.Int) (uint64, error) { + return a.c.NonceAt(ctx, address, blockNumber) +} + +func (a *MetaClient) PendingNonceAt(ctx context.Context, address common.Address) (uint64, error) { + return a.c.PendingNonceAt(ctx, address) +} + +func (a *MetaClient) SendTransaction(ctx context.Context, tx *types.Transaction, attempt *types.Attempt) error { + meta, err := tx.GetMeta() + if err != nil { + return err + } + + if meta != nil && meta.DualBroadcast != nil && *meta.DualBroadcast && !tx.IsPurgeable && meta.DualBroadcastParams != nil && meta.FwdrDestAddress != nil { + meta, err := a.SendRequest(ctx, tx, attempt, *meta.DualBroadcastParams, tx.ToAddress) + if err != nil { + return fmt.Errorf("error sending request for transactionID(%d): %w", tx.ID, err) + } + if meta != nil { + if err := a.SendOperation(ctx, tx, attempt, *meta); err != nil { + return fmt.Errorf("failed to send operation for transactionID(%d): %w", tx.ID, err) + } + return nil + } + a.lggr.Infof("No bids for transactionID(%d): ", tx.ID) + return nil + } + a.lggr.Infow("Broadcasting attempt to public mempool", "tx", tx) + return a.c.SendTransaction(ctx, attempt.SignedTransaction) +} + +type Parameters struct { + ChainID *hexutil.Uint64 `json:"chainId"` + ToAddress common.Address `json:"adapter"` + Payload hexutil.Bytes `json:"updatePayload"` + ER bool `json:"earlyReturn"` + FromAddress common.Address `json:"bundlerEoa"` + MaxFeePerGas *hexutil.Big `json:"maxFeePerGas"` + Signature hexutil.Bytes `json:"signature"` +} + +type RequestResponse struct { + Result *ResponseResult `json:"result"` + Error struct { + ErrorMessage string `json:"message,omitempty"` + } +} + +type ResponseResult struct { + UO *UORaw `json:"userOperation,omitempty"` + SOS []*SO `json:"solverOperations,omitempty"` + DO *DO `json:"dAppOperation,omitempty"` + MetacalldataResponse +} + +type MetacalldataResponse struct { + ToAddress common.Address `json:"metacallDestination"` + GasLimit *hexutil.Big `json:"metacallGasLimit"` + MaxFeePerGas *hexutil.Big `json:"metacallMaxFeePerGas"` + CallData hexutil.Bytes `json:"metacallCallData"` +} + +type UO struct { + To common.Address + MaxFeePerGas *big.Int + Dapp common.Address + Control common.Address + Data []byte +} + +type UORaw struct { + From common.Address `json:"from"` + To common.Address `json:"to"` + Value *hexutil.Big `json:"value"` + Gas *hexutil.Big `json:"gas"` + MaxFeePerGas *hexutil.Big `json:"maxFeePerGas"` + Nonce *hexutil.Big `json:"nonce"` + Deadline *hexutil.Big `json:"deadline"` + Dapp common.Address `json:"dapp"` + Control common.Address `json:"control"` + CallConfig *hexutil.Big `json:"callConfig"` + DappGasLimit *hexutil.Big `json:"dappGasLimit,omitempty"` + SessionKey common.Address `json:"sessionKey"` + Data hexutil.Bytes `json:"data"` + Signature hexutil.Bytes `json:"signature"` +} + +type SO struct { + From common.Address `json:"from"` + To common.Address `json:"to"` + Value *hexutil.Big `json:"value"` + Gas *hexutil.Big `json:"gas"` + MaxFeePerGas *hexutil.Big `json:"maxFeePerGas"` + Deadline *hexutil.Big `json:"deadline"` + Solver common.Address `json:"solver"` + Control common.Address `json:"control"` + UserOpHash common.Hash `json:"userOpHash"` + BidToken common.Address `json:"bidToken"` + BidAmount *hexutil.Big `json:"bidAmount"` + Data hexutil.Bytes `json:"data"` + Signature hexutil.Bytes `json:"signature"` +} + +type DO struct { + From common.Address `json:"from"` + To common.Address `json:"to"` + Nonce *hexutil.Big `json:"nonce"` + Deadline *hexutil.Big `json:"deadline"` + Control common.Address `json:"control"` + Bundler common.Address `json:"bundler"` + UserOpHash common.Hash `json:"userOpHash"` + CallChainHash common.Hash `json:"callChainHash"` + Signature hexutil.Bytes `json:"signature"` +} + +type Metacalldata struct { + UOP UO + SOPs []SO + DOP DO + GasRefundBeneficiary common.Address +} + +func (a *MetaClient) SendRequest(parentCtx context.Context, tx *types.Transaction, attempt *types.Attempt, dualBroadcastParams string, fwdrDestAddress common.Address) (*MetacalldataResponse, error) { + ctx, cancel := context.WithTimeout(parentCtx, timeout) + defer cancel() + + m := []byte{97, 116, 108, 97, 115, 95, 111, 101, 118, 65, 117, 99, 116, 105, 111, 110} + + cid := hexutil.Uint64(a.chainID.Uint64()) + var fee hexutil.Big + if attempt.Fee.ValidDynamic() { + fee = hexutil.Big(*attempt.Fee.GasFeeCap.ToInt()) + } else { + fee = hexutil.Big(*attempt.Fee.GasPrice.ToInt()) + } + params := Parameters{ + ChainID: &cid, + ToAddress: tx.ToAddress, + Payload: tx.Data, + ER: true, + FromAddress: tx.FromAddress, + MaxFeePerGas: &fee, + } + + payload := fmt.Sprintf( + "%s:%s:%s:%t:%s:%s", + params.ChainID.String(), + params.ToAddress.Hex(), + params.Payload.String(), + params.ER, + params.FromAddress.Hex(), + params.MaxFeePerGas.String(), + ) + + signature, err := a.ks.SignMessage(parentCtx, tx.FromAddress, []byte(payload)) + if err != nil { + return nil, fmt.Errorf("failed to sign message: %w", err) + } + params.Signature = signature + marshalledParamsExtended, err := json.Marshal(params) + if err != nil { + return nil, fmt.Errorf("failed to marshal signed params: %w", err) + } + body := fmt.Appendf(nil, `{"jsonrpc":"2.0","method":"%s","params":[%s], "id":1}`, string(m), marshalledParamsExtended) + req, err := http.NewRequestWithContext(ctx, http.MethodPost, a.customURL.String(), bytes.NewBuffer(body)) + if err != nil { + return nil, fmt.Errorf("failed to create POST request: %w", err) + } + req.Header.Add("Content-Type", "application/json") + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return nil, fmt.Errorf("failed to send POST request: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("request %v failed with status: %d", req, resp.StatusCode) + } + + data, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + var response RequestResponse + err = json.Unmarshal(data, &response) + if err != nil { + return nil, err + } + + if response.Error.ErrorMessage != "" { + if strings.Contains(response.Error.ErrorMessage, "no solver operations received") { + return nil, nil + } + return nil, errors.New(response.Error.ErrorMessage) + } + + if response.Result == nil { + return nil, nil + } + + if r, err := json.MarshalIndent(response.Result, "", " "); err == nil { + a.lggr.Info("Response: ", string(r)) + } + + return VerifyResponse(response.Result.MetacalldataResponse, dualBroadcastParams, tx.Data, tx.FromAddress, fwdrDestAddress) +} + +func VerifyResponse(metacalldata MetacalldataResponse, dualBroadcastParams string, txData []byte, fromAddress common.Address, fwdrDestAddress common.Address) (*MetacalldataResponse, error) { + params, err := url.ParseQuery(dualBroadcastParams) + if err != nil { + return nil, err + } + + destination := params["destination"] + dapp := params["dapp"] + if len(destination) != 1 || len(dapp) != 1 { + return nil, fmt.Errorf("incorrect size for info params: %v - %v", destination, dapp) + } + to := common.HexToAddress(destination[0]) // metacall address + dApp := common.HexToAddress(dapp[0]) + + if metacalldata.ToAddress != to { + return nil, fmt.Errorf("incorrect metacall: metacall.ToAddress: %v, to: %v", + metacalldata.ToAddress, to) + } + + // Calldata verification + if len(metacalldata.CallData) < 4 { + return nil, errors.New("calldata too short") + } + + mABI, err := abi.JSON(strings.NewReader(metaABI)) + if err != nil { + return nil, fmt.Errorf("parse ABI: %w", err) + } + method, ok := mABI.Methods["metacall"] + if !ok { + return nil, errors.New("method not found in ABI") + } + + if !bytes.Equal(metacalldata.CallData[:4], method.ID) { + return nil, fmt.Errorf("selector mismatch: got %x, expected %x", metacalldata.CallData[:4], method.ID) + } + args, err := method.Inputs.Unpack(metacalldata.CallData[4:]) + if err != nil || len(args) != 4 { + return nil, fmt.Errorf("unpack error(argsLen:%d): %w", len(args), err) + } + + result := new(Metacalldata) + err = mapstructure.Decode(args[0], &result.UOP) + if err != nil { + return nil, fmt.Errorf("error unpacking UOP: %w", err) + } + err = mapstructure.Decode(args[1], &result.SOPs) + if err != nil { + return nil, fmt.Errorf("error unpacking SOPs: %w", err) + } + err = mapstructure.Decode(args[2], &result.DOP) + if err != nil { + return nil, fmt.Errorf("error unpacking DOP: %w", err) + } + return VerifyMetadata(txData, fromAddress, *result, fwdrDestAddress, dApp, to, metacalldata) +} + +func VerifyMetadata(txData []byte, fromAddress common.Address, result Metacalldata, fwdrDestAddress common.Address, dApp common.Address, to common.Address, metacalldata MetacalldataResponse) (*MetacalldataResponse, error) { + abi, err := abi.JSON(strings.NewReader(ABI)) + if err != nil { + return nil, fmt.Errorf("couldn't read ABI: %w", err) + } + + updateFn, ok := abi.Methods["update"] + if !ok { + return nil, errors.New("update method not found in ABI") + } + if len(result.UOP.Data) < 4 || !bytes.HasPrefix(result.UOP.Data, updateFn.ID) { + return nil, fmt.Errorf("incorrect method id in uop.Data: %v", result.UOP.Data) + } + + args, err := updateFn.Inputs.UnpackValues(result.UOP.Data[4:]) // remove function selector + if err != nil || len(args) < 2 { + return nil, fmt.Errorf("unpack failed, data: %v, err: %w", result.UOP.Data, err) + } + + destinationAddress, ok := args[0].(common.Address) + if !ok { + return nil, fmt.Errorf("incorrect type for update.from: %v", args[0]) + } + + updateCalldata, ok := args[1].([]byte) + if !ok { + return nil, fmt.Errorf("incorrect type for update.calldata: %v", args[1]) + } + + // DOP + if result.DOP.To != to || result.DOP.Control != dApp || result.DOP.Bundler != fromAddress { + return nil, fmt.Errorf("incorrect DOP: dop.To: %v, dop.Control: %v, dop.Bundler: %v, to: %v, dApp: %v, fromAddress: %v", + result.DOP.To, result.DOP.Control, result.DOP.Bundler, to, dApp, fromAddress) + } + + // SOP + atLeastOne := false + for _, sop := range result.SOPs { + if sop.To != to || sop.Control != dApp { + // Exit early + return nil, fmt.Errorf("incorrect SOP: sop.To: %v, sop.Control: %v, to: %v, dApp: %v", sop.To, sop.Control, to, dApp) + } + atLeastOne = true + } + if !atLeastOne { + return nil, nil + } + + // UOP + if result.UOP.To != to || + result.UOP.MaxFeePerGas == nil || metacalldata.MaxFeePerGas == nil || result.UOP.MaxFeePerGas.Cmp(metacalldata.MaxFeePerGas.ToInt()) != 0 || + result.UOP.Dapp != dApp || + result.UOP.Control != dApp || + destinationAddress != fwdrDestAddress || !bytes.Equal(updateCalldata, txData) { + return nil, fmt.Errorf("incorrect UOP: uop.To: %v, uop.MaxFeePerGas: %v, uop.Dapp: %v, uop.update.destinationAddress: %v, uop.update.calldata: %v, to: %v, metacall.MaxFeePerGas: %v, dApp: %v, fwdrDestAddress: %v, txData: %v", + result.UOP.To, result.UOP.MaxFeePerGas, result.UOP.Dapp, destinationAddress, updateCalldata, to, metacalldata.MaxFeePerGas, dApp, fwdrDestAddress, txData) + } + + return &metacalldata, nil +} + +func (a *MetaClient) SendOperation(ctx context.Context, tx *types.Transaction, attempt *types.Attempt, meta MetacalldataResponse) error { + if tx.Nonce == nil { + return fmt.Errorf("failed to create attempt for txID: %v: nonce empty", tx.ID) + } + + // TODO: fastest way to avoid overpaying, but might require additional checks. + tip := meta.MaxFeePerGas.ToInt() + if attempt.Fee.ValidDynamic() && meta.MaxFeePerGas.ToInt().Cmp(attempt.Fee.GasTipCap.ToInt()) >= 0 { + tip = attempt.Fee.GasTipCap.ToInt() + } + gas := meta.GasLimit.ToInt() + if !gas.IsUint64() { + return fmt.Errorf("gas value does not fit in uint64: %s", gas) + } + dynamicTx := evmtypes.DynamicFeeTx{ + Nonce: *tx.Nonce, + To: &meta.ToAddress, + Gas: gas.Uint64(), + GasTipCap: tip, + GasFeeCap: meta.MaxFeePerGas.ToInt(), + Data: meta.CallData, + } + + signedTx, err := a.ks.SignTx(ctx, tx.FromAddress, evmtypes.NewTx(&dynamicTx)) + if err != nil { + return fmt.Errorf("failed to sign attempt for txID: %v, err: %w", tx.ID, err) + } + a.lggr.Infow("Intercepted attempt for tx", "txID", tx.ID, "toAddress", meta.ToAddress, "gasLimit", meta.GasLimit, + "TipCap", tip, "FeeCap", meta.MaxFeePerGas) + return a.c.SendTransaction(ctx, signedTx) +} diff --git a/pkg/txm/clientwrappers/dualbroadcast/meta_client_fuzz_test.go b/pkg/txm/clientwrappers/dualbroadcast/meta_client_fuzz_test.go new file mode 100644 index 0000000000..d87486564c --- /dev/null +++ b/pkg/txm/clientwrappers/dualbroadcast/meta_client_fuzz_test.go @@ -0,0 +1,51 @@ +package dualbroadcast_test + +import ( + "encoding/hex" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-evm/pkg/txm/clientwrappers/dualbroadcast" +) + +func FuzzVerifyResponse(f *testing.F) { + txData, err := hex.DecodeString("6fadcf72000000000000000000000000b123c2e3a71b57f9678cf6212576f30d642ed89a000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002e4ba0cb29e0001aeec9ec2ddb59123858e897b26f52e2173aca2c577dcce62e7ddc70f16020000000000000000000000000000000000000000000000000000000000418a059c756341541f2283d4f124ac39dc9e718f96453582f38acfa885e4ffaf26eb7800000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000678010b601000203000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000075542db800000000000000000000000000000000000000000000000000000000755c8e4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000075961a520000000000000000000000000000000000000000000000000000000000000002b974b422c8e712d3320994bf4ecd31e37b35a117ef4102285206c45aef4b8709ae407e7a604410ef1e622ebdb4ad302638ff0cf49ad5ab33486c206bf8ecd7dd0000000000000000000000000000000000000000000000000000000000000002671dd95393e5963a3842a5f95d5e6b544de4906b1c304d41aebbb7c2acf9cb065fb96b17223872656de8f05a17ffb6ccc3261b606179cc33db445158aa408b7000000000000000000000000000000000000000000000000000000000") + require.NoError(f, err) + fromAddress := common.HexToAddress("0x7daAe72fc3d948B1fA90502f1b84b6A02CCe7dFd") + params := "randomData=0x0000000&destination=0x1b4cb47622705f0f67b6b18bbd1aa1a91fc77d37&dapp=0xc38d38333687ea295753c214744e839eddc7aebb" + fwdrDestAddress := common.HexToAddress("0x8ae79bb7cce2dc3d132c288971b0f74af02a3b4a") + toAddress := common.HexToAddress("0x1b4cb47622705f0f67b6b18bbd1aa1a91fc77d37") + + var metacallResponse dualbroadcast.MetacalldataResponse + metacallResponse.ToAddress = toAddress + var gasLimit hexutil.Big + require.NoError(f, gasLimit.UnmarshalText([]byte("0x3c97b8"))) + metacallResponse.GasLimit = &gasLimit + var maxFeePerGas hexutil.Big + require.NoError(f, maxFeePerGas.UnmarshalText([]byte("0x3b9aca00"))) + + validCalldata, err := hexutil.Decode("0x4317ca01000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000007200000000000000000000000000000000000000000000000000000000000000a400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b6065f79d99f29c3eda0ed1bda7ff88e7ee12f1e0000000000000000000000001b4cb47622705f0f67b6b18bbd1cb1a91fc77d3700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000186a00000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000009c834e6000000000000000000000000c38d38333687ea295753c214785e839eddc7aebb000000000000000000000000c38d38333687ea295753c214785e839eddc7aebb000000000000000000000000000000000000000000000000000000000000230400000000000000000000000000000000000000000000000000000000001e848000000000000000000000000000000000000000000000000000000000005b8d80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000062000000000000000000000000000000000000000000000000000000000000003e402a688ed0000000000000000000000008ae79bb7cce2dc3d132c288971b0f74af02a3b4a000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000003646fadcf72000000000000000000000000b123c2e3a71b57f9678cf6212576f30d642ed89a000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002e4ba0cb29e0001aeec9ec2ddb59123858e897b26f52e2173aca2c577dcce62e7ddc70f16020000000000000000000000000000000000000000000000000000000000418a059c756341541f2283d4f124ac39dc9e718f96453582f38acfa885e4ffaf26eb7800000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000678010b601000203000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000075542db800000000000000000000000000000000000000000000000000000000755c8e4000000000000000000000000000000000000000000000000000000000756106c80000000000000000000000000000000000000000000000000000000075961a520000000000000000000000000000000000000000000000000000000000000002b974b422c8e712d3320994bf4ecd31e37b35a117ef4102285206c45aef4b8709ae407e7a604410ef1e622ebdb4ad302638ff0cf49ad5ab33486c206bf8ecd7dd0000000000000000000000000000000000000000000000000000000000000002671dd95393e5963a3842a5f95d5e6b544de4906b1c304d41aebbb7c2acf9cb065fb96b17223872656de8f05a17ffb6ccc3261b606179cc33db445158aa408b700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041b6ae5c173d3c7577ea3b8b4d35a0dc33e0f2798d74173c5e6fee2f08b8ce20271287a87443e778a114890140a08be5faf05ebcdd59051f9747fdd35d0afe6aa01b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000377136653944bdd5d9f0db22987b7432e76c354f0000000000000000000000001b4cb47622705f0f67b6b18bbd1cb1a91fc77d3700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030d40000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000009c834e60000000000000000000000004b548c6faf4a3c74571c3e194e71cbf2a5172501000000000000000000000000c38d38333687ea295753c214785e839eddc7aebb0648e7c2a3b705562efd0e06adb8a63916ba5a06ee92a8544369a431298bce47000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000186a000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000084c000a702000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000326a27250470f3a518db1334faaa277f8282082d7f54fd9b0f6cfff8f403a74fcb27a12a3860a1146cf7306eb49aa9fa03ccb1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004191537f835005fcd243b52b7b6436df810a1ea0b18769b2b1417ee9dacc9f9dac309a8b9ef82d82d6572e5feeb7209d12739067afd315434001a2aadd35af77701c000000000000000000000000000000000000000000000000000000000000000000000000000000000000001f3c5ec2ef75a9e1e09b1d46f208669e81000ee60000000000000000000000001b4cb47622705f0f67b6b18bbd1cb1a91fc77d3700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009c834e6000000000000000000000000c38d38333687ea295753c214785e839eddc7aebb0000000000000000000000007daae72fc3d948b1fa90502f1b84b6a02cce7dfd0648e7c2a3b705562efd0e06adb8a63916ba5a06ee92a8544369a431298bce4725c222f384dd726dc226053a74775e0a491fd531d79c447da90ccfb40cedf3fe00000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000041d83608483c9649ecf98f3641342d24c864f2143d090b8d2610f3fcdfbd365b1a513bc40af478bcc5f254cc1899022cefacecdb54aff69b7c3e21ef11a4ed49df1b00000000000000000000000000000000000000000000000000000000000000") + require.NoError(f, err) + f.Add(validCalldata) + invalidCalldata1, err := hexutil.Decode("0x41") + require.NoError(f, err) + f.Add(invalidCalldata1) + invalidCalldata2, err := hexutil.Decode("0x4316ca01000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000007200000000000000000000000000000000000000000000000000000000000000a400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b6065f79d99f29c3eda0ed1bda7ff88e7ee12f1e0000000000000000000000001b4cb47622705f0f67b6b18bbd1aa1a91fc77d3700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000186a00000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000009c834e6000000000000000000000000c38d38333687ea295753c214744e839eddc7aebb000000000000000000000000c38d38333687ea295753c214744e839eddc7aebb000000000000000000000000000000000000000000000000000000000000230400000000000000000000000000000000000000000000000000000000001e848000000000000000000000000000000000000000000000000000000000005b8d80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000062000000000000000000000000000000000000000000000000000000000000003e402a688ed0000000000000000000000008ae79bb7cce2dc3d132c288971b0f74af02a3b4a000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000003646fadcf72000000000000000000000000b123c2e3a71b57f9678cf6212576f30d642ed89a000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002e4ba0cb29e0001aeec9ec2ddb59123858e897b26f52e2173aca2c577dcce62e7ddc70f16020000000000000000000000000000000000000000000000000000000000418a059c756341541f2283d4f124ac39dc9e718f96453582f38acfa885e4ffaf26eb7800000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000678010b601000203000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000075542db800000000000000000000000000000000000000000000000000000000755c8e4000000000000000000000000000000000000000000000000000000000756106c80000000000000000000000000000000000000000000000000000000075961a520000000000000000000000000000000000000000000000000000000000000002b974b422c8e712d3320994bf4ecd31e37b35a117ef4102285206c45aef4b8709ae407e7a604410ef1e622ebdb4ad302638ff0cf49ad5ab33486c206bf8ecd7dd0000000000000000000000000000000000000000000000000000000000000002671dd95393e5963a3842a5f95d5e6b544de4906b1c304d41aebbb7c2acf9cb065fb96b17223872656de8f05a17ffb6ccc3261b606179cc33db445158aa408b700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041b6ae5c173d3c7577ea3b8b4d35a0dc33e0f2798d74173c5e6fee2f08b8ce20271287a87443e778a114890140a08be5faf05ebcdd59051f9747fdd35d0afe6aa01b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000377136653944bdd5d9f0db22987b7432e76c354f0000000000000000000000001b4cb47622705f0f67b6b18bbd1aa1a91fc77d3700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030d40000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000009c834e60000000000000000000000004b548c6faf4a3c74571c3e194e71cbf2a5172501000000000000000000000000c38d38333687ea295753c214744e839eddc7aebb0648e7c2a3b705562efd0e06adb8a63916ba5a06ee92a8544369a431298bce47000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000186a000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000084c000a702000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000326a27250470f3a518db1334faaa277f8282082d7f54fd9b0f6cfff8f403a74fcb27a12a3860a1146cf7306eb49aa9fa03ccb1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004191537f835005fcd243b52b7b6436df810a1ea0b18769b2b1417ee9dacc9f9dac309a8b9ef82d82d6572e5feeb7209d12739067afd315434001a2aadd35af77701c000000000000000000000000000000000000000000000000000000000000000000000000000000000000001f3c5ec2ef75a9e1e09b1d46f208669e81000ee60000000000000000000000001b4cb47622705f0f67b6b18bbd1aa1a91fc77d3700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009c834e6000000000000000000000000c38d38333687ea295753c214744e839eddc7aebb0000000000000000000000007daae72fc3d948b1fa90502f1b84b6a02cce7dfd0648e7c2a3b705562efd0e06adb8a63916ba5a06ee92a8544369a431298bce4725c222f384dd726dc226053a74775e0a491fd531d79c447da90ccfb40cedf3fe00000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000041d83608483c9649ecf98f3641342d24c864f2143d090b8d2610f3fcdfbd365b1a513bc40af478bcc5f254cc1899022cefacecdb54aff69b7c3e21ef11a4ed49df1b00000000000000000000000000000000000000000000000000000000000000") + require.NoError(f, err) + f.Add(invalidCalldata2) + invalidCalldata3, err := hexutil.Decode("0x4317ca01000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000007200000000000000000000000000000000000000000000000000000000000000a400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b6065f79d99f29c3eda0ed1bda7ff88e7ee12f1e0000000000000000000000001b4cb47622705f0f67b6b18bbd1aa1a91fc77d3700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000186a00000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000009c834e6000000000000000000000000c38d38333687ea295753c214744e839eddc7aebb000000000000000000000000c38d38333687ea295753c214744e839eddc7aebb000000000000000000000000000000000000000000000000000000000000230400000000000000000000000000000000000000000000000000000000001e848000000000000000000000000000000000000000000000000000000000005b8d80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000062000000000000000000000000000000000000000000000000000000000000003e402a688ed0000000000000000000000008ae79bb7cce2dc3d132c288971b0f74af02a3b4a000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000003646fadcf72000000000000000000000000b123c2e3a71b57f9678cf6212576f30d642ed89a000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002e4ba0cb29e0001aeec9ec2ddb59123858e897b26f52e2173aca2c577dcce62e7ddc70f16020000000000000000000000000000000000000000000000000000000000418a059c756341541f2283d4f124ac39dc9e718f96453582f38acfa885e4ffaf26eb7800000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000678010b601000203000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000075542db800000000000000000000000000000000000000000000000000000000755c8e4000000000000000000000000000000000000000000000000000000000756106c80000000000000000000000000000000000000000000000000000000075961a520000000000000000000000000000000000000000000000000000000000000002b974b422c8e712d3320994bf4ecd31e37b35a117ef4102285206c45aef4b8709ae407e7a604410ef1e622ebdb4ad302638ff0cf49ad5ab33486c206bf8ecd7dd0000000000000000000000000000000000000000000000000000000000000002671dd95393e5963a3842a5f95d5e6b544de4906b1c304d41aebbb7c2acf9cb065fb96b17223872656de8f05a17ffb6ccc3261b606179cc33db445158aa408b700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041b6ae5c173d3c7577ea3b8b4d35a0dc33e0f2798d74173c5e6fee2f08b8ce20271287a87443e778a114890140a08be5faf05ebcdd59051f9747fdd35d0afe6aa01b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000377136653944bdd5d9f0db22987b7432e76c354f0000000000000000000000001b4cb47622705f0f67b6b18bbd1aa1a91fc77d3700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030d40000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000009c834e60000000000000000000000004b548c6faf4a3c74571c3e194e71cbf2a5172501000000000000000000000000c38d38333687ea295753c214744e839eddc7aebb0648e7c2a3b705562efd0e06adb8a63916ba5a06ee92a8544369a431298bce47000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b6065f0000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000084c000a702000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000326a27250470f3a518db1334faaa277f8282082d7f54fd9b0f6cfff8f403a74fcb27a12a3860a1146cf7306eb49aa9fa03ccb1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004191537f835005fcd243b52b7b6436df810a1ea0b18769b2b1417ee9dacc9f9dac309a8b9ef82d82d6572e5feeb7209d12739067afd315434001a2aadd35af77701c000000000000000000000000000000000000000000000000000000000000000000000000000000000000001f3c5ec2ef75a9e1e09b1d46f208669e81000ee60000000000000000000000001b4cb47622705f0f67b6b18bbd1aa1a91fc77d3700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009c834e6000000000000000000000000c38d38333687ea295753c214744e839eddc7aebb0000000000000000000000007daae72fc3d948b1fa90502f1b84b6a02cce7dfd0648e7c2a3b705562efd0e06adb8a63916ba5a06ee92a8544369a431298bce4725c222f384dd726dc226053a74775e0a491fd531d79c447da90ccfb40cedf3fe00000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000041d83608483c9649ecf98f3641342d24c864f2143d090b8d2610f3fcdfbd365b1a513bc40af478bcc5f254cc1899022cefacecdb54aff69b7c3e21ef11a4ed49df1b") + require.NoError(f, err) + f.Add(invalidCalldata3) + + f.Fuzz(func(t *testing.T, calldata []byte) { + metacallResponse.CallData = calldata + resp, err := dualbroadcast.VerifyResponse(metacallResponse, params, txData, fromAddress, fwdrDestAddress) + if err == nil && resp != nil { + require.Equal(t, toAddress, resp.ToAddress) + require.Equal(t, maxFeePerGas, resp.MaxFeePerGas) + } + }) +} diff --git a/pkg/txm/clientwrappers/dualbroadcast/meta_client_test.go b/pkg/txm/clientwrappers/dualbroadcast/meta_client_test.go new file mode 100644 index 0000000000..5f8fac42a8 --- /dev/null +++ b/pkg/txm/clientwrappers/dualbroadcast/meta_client_test.go @@ -0,0 +1,70 @@ +package dualbroadcast_test + +import ( + "encoding/hex" + "encoding/json" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-evm/pkg/txm/clientwrappers/dualbroadcast" +) + +func TestMetaClient_VerifyResponse(t *testing.T) { + responseData := []byte(`{"jsonrpc":"2.0","result":{"userOperation":{"from":"0xb6065f79d99f29c3eda0ed1bda7ff88e7ee12f1e","to":"0x1b4cb47622705f0f67b6b18bbd1aa1a91fc77d37","value":"0x0","gas":"0x186a00","maxFeePerGas":"0x3b9aca00","nonce":"0xf","deadline":"0x9c834e6","dapp":"0xc38d38333687ea295753c214744e839eddc7aebb","control":"0xc38d38333687ea295753c214744e839eddc7aebb","callConfig":"0x2304","dappGasLimit":"0x1e8480","solverGasLimit":"0x5b8d80","bundlerSurchargeRate":"0x0","sessionKey":"0x0000000000000000000000000000000000000000","data":"0x02a688ed0000000000000000000000008ae79bb7cce2dc3d132c288971b0f74af02a3b4a000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000003646fadcf72000000000000000000000000b123c2e3a71b57f9678cf6212576f30d642ed89a000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002e4ba0cb29e0001aeec9ec2ddb59123858e897b26f52e2173aca2c577dcce62e7ddc70f16020000000000000000000000000000000000000000000000000000000000418a059c756341541f2283d4f124ac39dc9e718f96453582f38acfa885e4ffaf26eb7800000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000678010b601000203000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000075542db800000000000000000000000000000000000000000000000000000000755c8e4000000000000000000000000000000000000000000000000000000000756106c80000000000000000000000000000000000000000000000000000000075961a520000000000000000000000000000000000000000000000000000000000000002b974b422c8e712d3320994bf4ecd31e37b35a117ef4102285206c45aef4b8709ae407e7a604410ef1e622ebdb4ad302638ff0cf49ad5ab33486c206bf8ecd7dd0000000000000000000000000000000000000000000000000000000000000002671dd95393e5963a3842a5f95d5e6b544de4906b1c304d41aebbb7c2acf9cb065fb96b17223872656de8f05a17ffb6ccc3261b606179cc33db445158aa408b700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","signature":"0xb6ae5c173d3c7577ea3b8b4d35a0dc33e0f2798d74173c5e6fee2f08b8ce20271287a87443e778a114890140a08be5faf05ebcdd59051f9747fdd35d0afe6aa01b"},"solverOperations":[{"from":"0x377136653944bdd5d9f0db22987b7432e76c354f","to":"0x1b4cb47622705f0f67b6b18bbd1aa1a91fc77d37","value":"0x0","gas":"0x30d40","maxFeePerGas":"0x3b9aca00","deadline":"0x9c834e6","solver":"0x4b548c6faf4a3c74571c3e194e71cbf2a5172501","control":"0xc38d38333687ea295753c214744e839eddc7aebb","userOpHash":"0x0648e7c2a3b705562efd0e06adb8a63916ba5a06ee92a8544369a431298bce47","bidToken":"0x0000000000000000000000000000000000000000","bidAmount":"0x186a0","data":"0xc000a702000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000326a27250470f3a518db1334faaa277f8282082d7f54fd9b0f6cfff8f403a74fcb27a12a3860a1146cf7306eb49aa9fa03ccb10000000000000000000000000000","signature":"0x91537f835005fcd243b52b7b6436df810a1ea0b18769b2b1417ee9dacc9f9dac309a8b9ef82d82d6572e5feeb7209d12739067afd315434001a2aadd35af77701c"}],"dAppOperation":{"from":"0x1f3c5ec2ef75a9e1e09b1d46f208669e81000ee6","to":"0x1b4cb47622705f0f67b6b18bbd1aa1a91fc77d37","nonce":"0x0","deadline":"0x9c834e6","control":"0xc38d38333687ea295753c214744e839eddc7aebb","bundler":"0x7daae72fc3d948b1fa90502f1b84b6a02cce7dfd","userOpHash":"0x0648e7c2a3b705562efd0e06adb8a63916ba5a06ee92a8544369a431298bce47","callChainHash":"0x25c222f384dd726dc226053a74775e0a491fd531d79c447da90ccfb40cedf3fe","signature":"0xd83608483c9649ecf98f3641342d24c864f2143d090b8d2610f3fcdfbd365b1a513bc40af478bcc5f254cc1899022cefacecdb54aff69b7c3e21ef11a4ed49df1b"},"metacallDestination":"0x1b4cb47622705f0f67b6b18bbd1aa1a91fc77d37","metacallGasLimit":"0x3c97b8","metacallMaxFeePerGas":"0x3b9aca00","metacallCallData":"0x4317ca01000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000007200000000000000000000000000000000000000000000000000000000000000a400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b6065f79d99f29c3eda0ed1bda7ff88e7ee12f1e0000000000000000000000001b4cb47622705f0f67b6b18bbd1aa1a91fc77d3700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000186a00000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000009c834e6000000000000000000000000c38d38333687ea295753c214744e839eddc7aebb000000000000000000000000c38d38333687ea295753c214744e839eddc7aebb000000000000000000000000000000000000000000000000000000000000230400000000000000000000000000000000000000000000000000000000001e848000000000000000000000000000000000000000000000000000000000005b8d80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000062000000000000000000000000000000000000000000000000000000000000003e402a688ed0000000000000000000000008ae79bb7cce2dc3d132c288971b0f74af02a3b4a000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000003646fadcf72000000000000000000000000b123c2e3a71b57f9678cf6212576f30d642ed89a000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002e4ba0cb29e0001aeec9ec2ddb59123858e897b26f52e2173aca2c577dcce62e7ddc70f16020000000000000000000000000000000000000000000000000000000000418a059c756341541f2283d4f124ac39dc9e718f96453582f38acfa885e4ffaf26eb7800000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000678010b601000203000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000075542db800000000000000000000000000000000000000000000000000000000755c8e4000000000000000000000000000000000000000000000000000000000756106c80000000000000000000000000000000000000000000000000000000075961a520000000000000000000000000000000000000000000000000000000000000002b974b422c8e712d3320994bf4ecd31e37b35a117ef4102285206c45aef4b8709ae407e7a604410ef1e622ebdb4ad302638ff0cf49ad5ab33486c206bf8ecd7dd0000000000000000000000000000000000000000000000000000000000000002671dd95393e5963a3842a5f95d5e6b544de4906b1c304d41aebbb7c2acf9cb065fb96b17223872656de8f05a17ffb6ccc3261b606179cc33db445158aa408b700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041b6ae5c173d3c7577ea3b8b4d35a0dc33e0f2798d74173c5e6fee2f08b8ce20271287a87443e778a114890140a08be5faf05ebcdd59051f9747fdd35d0afe6aa01b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000377136653944bdd5d9f0db22987b7432e76c354f0000000000000000000000001b4cb47622705f0f67b6b18bbd1aa1a91fc77d3700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030d40000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000009c834e60000000000000000000000004b548c6faf4a3c74571c3e194e71cbf2a5172501000000000000000000000000c38d38333687ea295753c214744e839eddc7aebb0648e7c2a3b705562efd0e06adb8a63916ba5a06ee92a8544369a431298bce47000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000186a000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000084c000a702000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000326a27250470f3a518db1334faaa277f8282082d7f54fd9b0f6cfff8f403a74fcb27a12a3860a1146cf7306eb49aa9fa03ccb1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004191537f835005fcd243b52b7b6436df810a1ea0b18769b2b1417ee9dacc9f9dac309a8b9ef82d82d6572e5feeb7209d12739067afd315434001a2aadd35af77701c000000000000000000000000000000000000000000000000000000000000000000000000000000000000001f3c5ec2ef75a9e1e09b1d46f208669e81000ee60000000000000000000000001b4cb47622705f0f67b6b18bbd1aa1a91fc77d3700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009c834e6000000000000000000000000c38d38333687ea295753c214744e839eddc7aebb0000000000000000000000007daae72fc3d948b1fa90502f1b84b6a02cce7dfd0648e7c2a3b705562efd0e06adb8a63916ba5a06ee92a8544369a431298bce4725c222f384dd726dc226053a74775e0a491fd531d79c447da90ccfb40cedf3fe00000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000041d83608483c9649ecf98f3641342d24c864f2143d090b8d2610f3fcdfbd365b1a513bc40af478bcc5f254cc1899022cefacecdb54aff69b7c3e21ef11a4ed49df1b00000000000000000000000000000000000000000000000000000000000000"},"id":1}`) + txData, err := hex.DecodeString("6fadcf72000000000000000000000000b123c2e3a71b57f9678cf6212576f30d642ed89a000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002e4ba0cb29e0001aeec9ec2ddb59123858e897b26f52e2173aca2c577dcce62e7ddc70f16020000000000000000000000000000000000000000000000000000000000418a059c756341541f2283d4f124ac39dc9e718f96453582f38acfa885e4ffaf26eb7800000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000678010b601000203000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000075542db800000000000000000000000000000000000000000000000000000000755c8e4000000000000000000000000000000000000000000000000000000000756106c80000000000000000000000000000000000000000000000000000000075961a520000000000000000000000000000000000000000000000000000000000000002b974b422c8e712d3320994bf4ecd31e37b35a117ef4102285206c45aef4b8709ae407e7a604410ef1e622ebdb4ad302638ff0cf49ad5ab33486c206bf8ecd7dd0000000000000000000000000000000000000000000000000000000000000002671dd95393e5963a3842a5f95d5e6b544de4906b1c304d41aebbb7c2acf9cb065fb96b17223872656de8f05a17ffb6ccc3261b606179cc33db445158aa408b7000000000000000000000000000000000000000000000000000000000") + require.NoError(t, err) + fromAddress := common.HexToAddress("0x7daAe72fc3d948B1fA90502f1b84b6A02CCe7dFd") + var response dualbroadcast.RequestResponse + require.NoError(t, json.Unmarshal(responseData, &response)) + params := "randomData=0x0000000&destination=0x1b4cb47622705f0f67b6b18bbd1aa1a91fc77d37&dapp=0xc38d38333687ea295753c214744e839eddc7aebb" + fwdrDestAddress := common.HexToAddress("0x8ae79bb7cce2dc3d132c288971b0f74af02a3b4a") + _, err = dualbroadcast.VerifyResponse(response.Result.MetacalldataResponse, params, txData, fromAddress, fwdrDestAddress) + require.NoError(t, err) +} + +func TestMetaClient_VerifyResponse_Error(t *testing.T) { + txData, err := hex.DecodeString("6fadcf72000000000000000000000000b123c2e3a71b57f9678cf6212576f30d642ed89a000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002e4ba0cb29e0001aeec9ec2ddb59123858e897b26f52e2173aca2c577dcce62e7ddc70f16020000000000000000000000000000000000000000000000000000000000418a059c756341541f2283d4f124ac39dc9e718f96453582f38acfa885e4ffaf26eb7800000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000678010b601000203000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000075542db800000000000000000000000000000000000000000000000000000000755c8e4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000075961a520000000000000000000000000000000000000000000000000000000000000002b974b422c8e712d3320994bf4ecd31e37b35a117ef4102285206c45aef4b8709ae407e7a604410ef1e622ebdb4ad302638ff0cf49ad5ab33486c206bf8ecd7dd0000000000000000000000000000000000000000000000000000000000000002671dd95393e5963a3842a5f95d5e6b544de4906b1c304d41aebbb7c2acf9cb065fb96b17223872656de8f05a17ffb6ccc3261b606179cc33db445158aa408b7000000000000000000000000000000000000000000000000000000000") + require.NoError(t, err) + fromAddress := common.HexToAddress("0x7daAe72fc3d948B1fA90502f1b84b6A02CCe7dFd") + params := "randomData=0x0000000&destination=0x1b4cb47622705f0f67b6b18bbd1aa1a91fc77d37&dapp=0xc38d38333687ea295753c214744e839eddc7aebb" + fwdrDestAddress := common.HexToAddress("0x8ae79bb7cce2dc3d132c288971b0f74af02a3b4a") + + var metacallResponse dualbroadcast.MetacalldataResponse + metacallResponse.ToAddress = common.HexToAddress("0x1b4cb47622705f0f67b6b18bbd1aa1a91fc77d37") + var gasLimit hexutil.Big + require.NoError(t, gasLimit.UnmarshalText([]byte("0x3c97b8"))) + metacallResponse.GasLimit = &gasLimit + var maxFeePerGas hexutil.Big + require.NoError(t, maxFeePerGas.UnmarshalText([]byte("0x3b9aca00"))) + metacallResponse.MaxFeePerGas = &maxFeePerGas + + t.Run("calldata too short", func(t *testing.T) { + metacallCallData := "0x41" + calldata, err := hexutil.Decode(metacallCallData) + require.NoError(t, err) + metacallResponse.CallData = calldata + _, err = dualbroadcast.VerifyResponse(metacallResponse, params, txData, fromAddress, fwdrDestAddress) + require.ErrorContains(t, err, "calldata too short") + }) + + t.Run("incorrect method id", func(t *testing.T) { + metacallCallData := "0x4316ca01000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000007200000000000000000000000000000000000000000000000000000000000000a400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b6065f79d99f29c3eda0ed1bda7ff88e7ee12f1e0000000000000000000000001b4cb47622705f0f67b6b18bbd1aa1a91fc77d3700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000186a00000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000009c834e6000000000000000000000000c38d38333687ea295753c214744e839eddc7aebb000000000000000000000000c38d38333687ea295753c214744e839eddc7aebb000000000000000000000000000000000000000000000000000000000000230400000000000000000000000000000000000000000000000000000000001e848000000000000000000000000000000000000000000000000000000000005b8d80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000062000000000000000000000000000000000000000000000000000000000000003e402a688ed0000000000000000000000008ae79bb7cce2dc3d132c288971b0f74af02a3b4a000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000003646fadcf72000000000000000000000000b123c2e3a71b57f9678cf6212576f30d642ed89a000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002e4ba0cb29e0001aeec9ec2ddb59123858e897b26f52e2173aca2c577dcce62e7ddc70f16020000000000000000000000000000000000000000000000000000000000418a059c756341541f2283d4f124ac39dc9e718f96453582f38acfa885e4ffaf26eb7800000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000678010b601000203000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000075542db800000000000000000000000000000000000000000000000000000000755c8e4000000000000000000000000000000000000000000000000000000000756106c80000000000000000000000000000000000000000000000000000000075961a520000000000000000000000000000000000000000000000000000000000000002b974b422c8e712d3320994bf4ecd31e37b35a117ef4102285206c45aef4b8709ae407e7a604410ef1e622ebdb4ad302638ff0cf49ad5ab33486c206bf8ecd7dd0000000000000000000000000000000000000000000000000000000000000002671dd95393e5963a3842a5f95d5e6b544de4906b1c304d41aebbb7c2acf9cb065fb96b17223872656de8f05a17ffb6ccc3261b606179cc33db445158aa408b700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041b6ae5c173d3c7577ea3b8b4d35a0dc33e0f2798d74173c5e6fee2f08b8ce20271287a87443e778a114890140a08be5faf05ebcdd59051f9747fdd35d0afe6aa01b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000377136653944bdd5d9f0db22987b7432e76c354f0000000000000000000000001b4cb47622705f0f67b6b18bbd1aa1a91fc77d3700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030d40000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000009c834e60000000000000000000000004b548c6faf4a3c74571c3e194e71cbf2a5172501000000000000000000000000c38d38333687ea295753c214744e839eddc7aebb0648e7c2a3b705562efd0e06adb8a63916ba5a06ee92a8544369a431298bce47000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000186a000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000084c000a702000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000326a27250470f3a518db1334faaa277f8282082d7f54fd9b0f6cfff8f403a74fcb27a12a3860a1146cf7306eb49aa9fa03ccb1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004191537f835005fcd243b52b7b6436df810a1ea0b18769b2b1417ee9dacc9f9dac309a8b9ef82d82d6572e5feeb7209d12739067afd315434001a2aadd35af77701c000000000000000000000000000000000000000000000000000000000000000000000000000000000000001f3c5ec2ef75a9e1e09b1d46f208669e81000ee60000000000000000000000001b4cb47622705f0f67b6b18bbd1aa1a91fc77d3700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009c834e6000000000000000000000000c38d38333687ea295753c214744e839eddc7aebb0000000000000000000000007daae72fc3d948b1fa90502f1b84b6a02cce7dfd0648e7c2a3b705562efd0e06adb8a63916ba5a06ee92a8544369a431298bce4725c222f384dd726dc226053a74775e0a491fd531d79c447da90ccfb40cedf3fe00000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000041d83608483c9649ecf98f3641342d24c864f2143d090b8d2610f3fcdfbd365b1a513bc40af478bcc5f254cc1899022cefacecdb54aff69b7c3e21ef11a4ed49df1b00000000000000000000000000000000000000000000000000000000000000" + calldata, err := hexutil.Decode(metacallCallData) + require.NoError(t, err) + metacallResponse.CallData = calldata + _, err = dualbroadcast.VerifyResponse(metacallResponse, params, txData, fromAddress, fwdrDestAddress) + require.ErrorContains(t, err, "selector mismatch") + }) + + t.Run("unpack error", func(t *testing.T) { + metacallCallData := "0x4317ca01000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000007200000000000000000000000000000000000000000000000000000000000000a400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b6065f79d99f29c3eda0ed1bda7ff88e7ee12f1e0000000000000000000000001b4cb47622705f0f67b6b18bbd1aa1a91fc77d3700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000186a00000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000009c834e6000000000000000000000000c38d38333687ea295753c214744e839eddc7aebb000000000000000000000000c38d38333687ea295753c214744e839eddc7aebb000000000000000000000000000000000000000000000000000000000000230400000000000000000000000000000000000000000000000000000000001e848000000000000000000000000000000000000000000000000000000000005b8d80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000062000000000000000000000000000000000000000000000000000000000000003e402a688ed0000000000000000000000008ae79bb7cce2dc3d132c288971b0f74af02a3b4a000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000003646fadcf72000000000000000000000000b123c2e3a71b57f9678cf6212576f30d642ed89a000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002e4ba0cb29e0001aeec9ec2ddb59123858e897b26f52e2173aca2c577dcce62e7ddc70f16020000000000000000000000000000000000000000000000000000000000418a059c756341541f2283d4f124ac39dc9e718f96453582f38acfa885e4ffaf26eb7800000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000678010b601000203000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000075542db800000000000000000000000000000000000000000000000000000000755c8e4000000000000000000000000000000000000000000000000000000000756106c80000000000000000000000000000000000000000000000000000000075961a520000000000000000000000000000000000000000000000000000000000000002b974b422c8e712d3320994bf4ecd31e37b35a117ef4102285206c45aef4b8709ae407e7a604410ef1e622ebdb4ad302638ff0cf49ad5ab33486c206bf8ecd7dd0000000000000000000000000000000000000000000000000000000000000002671dd95393e5963a3842a5f95d5e6b544de4906b1c304d41aebbb7c2acf9cb065fb96b17223872656de8f05a17ffb6ccc3261b606179cc33db445158aa408b700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041b6ae5c173d3c7577ea3b8b4d35a0dc33e0f2798d74173c5e6fee2f08b8ce20271287a87443e778a114890140a08be5faf05ebcdd59051f9747fdd35d0afe6aa01b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000377136653944bdd5d9f0db22987b7432e76c354f0000000000000000000000001b4cb47622705f0f67b6b18bbd1aa1a91fc77d3700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030d40000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000009c834e60000000000000000000000004b548c6faf4a3c74571c3e194e71cbf2a5172501000000000000000000000000c38d38333687ea295753c214744e839eddc7aebb0648e7c2a3b705562efd0e06adb8a63916ba5a06ee92a8544369a431298bce47000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b6065f0000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000084c000a702000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000326a27250470f3a518db1334faaa277f8282082d7f54fd9b0f6cfff8f403a74fcb27a12a3860a1146cf7306eb49aa9fa03ccb1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004191537f835005fcd243b52b7b6436df810a1ea0b18769b2b1417ee9dacc9f9dac309a8b9ef82d82d6572e5feeb7209d12739067afd315434001a2aadd35af77701c000000000000000000000000000000000000000000000000000000000000000000000000000000000000001f3c5ec2ef75a9e1e09b1d46f208669e81000ee60000000000000000000000001b4cb47622705f0f67b6b18bbd1aa1a91fc77d3700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009c834e6000000000000000000000000c38d38333687ea295753c214744e839eddc7aebb0000000000000000000000007daae72fc3d948b1fa90502f1b84b6a02cce7dfd0648e7c2a3b705562efd0e06adb8a63916ba5a06ee92a8544369a431298bce4725c222f384dd726dc226053a74775e0a491fd531d79c447da90ccfb40cedf3fe00000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000041d83608483c9649ecf98f3641342d24c864f2143d090b8d2610f3fcdfbd365b1a513bc40af478bcc5f254cc1899022cefacecdb54aff69b7c3e21ef11a4ed49df1b" + calldata, err := hexutil.Decode(metacallCallData) + require.NoError(t, err) + metacallResponse.CallData = calldata + _, err = dualbroadcast.VerifyResponse(metacallResponse, params, txData, fromAddress, fwdrDestAddress) + require.ErrorContains(t, err, "unpack error") + }) +} diff --git a/pkg/txm/clientwrappers/dualbroadcast/selector.go b/pkg/txm/clientwrappers/dualbroadcast/selector.go new file mode 100644 index 0000000000..3f955fb72f --- /dev/null +++ b/pkg/txm/clientwrappers/dualbroadcast/selector.go @@ -0,0 +1,23 @@ +package dualbroadcast + +import ( + "math/big" + "net/url" + "strings" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" + + "github.com/smartcontractkit/chainlink-evm/pkg/client" + "github.com/smartcontractkit/chainlink-evm/pkg/keys" + "github.com/smartcontractkit/chainlink-evm/pkg/txm" +) + +func SelectClient(lggr logger.Logger, client client.Client, keyStore keys.ChainStore, url *url.URL, chainID *big.Int) txm.Client { + urlString := url.String() + switch { + case strings.Contains(urlString, "flashbots"): + return NewFlashbotsClient(client, keyStore, url) + default: + return NewMetaClient(lggr, client, keyStore, url, chainID) + } +} diff --git a/pkg/txmgr/builder.go b/pkg/txmgr/builder.go index e5a42c911c..09cd4f9902 100644 --- a/pkg/txmgr/builder.go +++ b/pkg/txmgr/builder.go @@ -6,8 +6,6 @@ import ( "math/big" "time" - "github.com/ethereum/go-ethereum/common" - "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" "github.com/smartcontractkit/chainlink-evm/pkg/keys" @@ -23,6 +21,7 @@ import ( "github.com/smartcontractkit/chainlink-evm/pkg/logpoller" "github.com/smartcontractkit/chainlink-evm/pkg/txm" "github.com/smartcontractkit/chainlink-evm/pkg/txm/clientwrappers" + "github.com/smartcontractkit/chainlink-evm/pkg/txm/clientwrappers/dualbroadcast" "github.com/smartcontractkit/chainlink-evm/pkg/txm/storage" "github.com/smartcontractkit/chainlink-evm/pkg/types" ) @@ -84,7 +83,7 @@ func NewTxm( // NewEvmTxm creates a new concrete EvmTxm func NewEvmTxm( - chainId *big.Int, + chainID *big.Int, cfg txmgrtypes.TransactionManagerChainConfig, txCfg txmgrtypes.TransactionManagerTransactionsConfig, keyStore KeyStore, @@ -100,7 +99,7 @@ func NewEvmTxm( finalizer Finalizer, txmv2wrapper TxManager, ) *Txm { - return txmgr.NewTxm(chainId, cfg, txCfg, keyStore, lggr, checkerFactory, fwdMgr, txAttemptBuilder, txStore, broadcaster, confirmer, resender, tracker, finalizer, client.NewTxError, txmv2wrapper) + return txmgr.NewTxm(chainID, cfg, txCfg, keyStore, lggr, checkerFactory, fwdMgr, txAttemptBuilder, txStore, broadcaster, confirmer, resender, tracker, finalizer, client.NewTxError, txmv2wrapper) } func NewTxmV2( @@ -126,10 +125,14 @@ func NewTxmV2( var stuckTxDetector txm.StuckTxDetector if txConfig.AutoPurge().Enabled() { + var detectionURL string + if txConfig.AutoPurge().DetectionApiUrl() != nil { + detectionURL = txConfig.AutoPurge().DetectionApiUrl().String() + } stuckTxDetectorConfig := txm.StuckTxDetectorConfig{ BlockTime: *txmV2Config.BlockTime(), StuckTxBlockThreshold: *txConfig.AutoPurge().Threshold(), - DetectionURL: txConfig.AutoPurge().DetectionApiUrl().String(), + DetectionURL: detectionURL, } stuckTxDetector = txm.NewStuckTxDetector(lggr, chainConfig.ChainType(), stuckTxDetectorConfig) } @@ -144,8 +147,8 @@ func NewTxmV2( EmptyTxLimitDefault: fCfg.LimitDefault(), } var c txm.Client - if txmV2Config.DualBroadcast() != nil && *txmV2Config.DualBroadcast() { - c = clientwrappers.NewDualBroadcastClient(client, keyStore, txmV2Config.CustomURL()) + if txmV2Config.DualBroadcast() != nil && *txmV2Config.DualBroadcast() && txmV2Config.CustomURL() != nil { + c = dualbroadcast.SelectClient(lggr, client, keyStore, txmV2Config.CustomURL(), chainID) } else { c = clientwrappers.NewChainClient(client) } @@ -195,7 +198,7 @@ func NewEvmTracker( chainID *big.Int, lggr logger.Logger, ) *Tracker { - return txmgr.NewTracker[*big.Int, common.Address, common.Hash, common.Hash, *types.Receipt](txStore, keyStore, chainID, lggr) + return txmgr.NewTracker(txStore, keyStore, chainID, lggr) } // NewEvmBroadcaster returns a new concrete EvmBroadcaster