diff --git a/core/capabilities/targets/write_target.go b/core/capabilities/targets/write_target.go index 4584be26b5a..0363f4ba23a 100644 --- a/core/capabilities/targets/write_target.go +++ b/core/capabilities/targets/write_target.go @@ -4,8 +4,10 @@ import ( "context" "encoding/json" "fmt" + "math/big" "github.com/ethereum/go-ethereum/common" + "github.com/google/uuid" chainselectors "github.com/smartcontractkit/chain-selectors" @@ -13,13 +15,10 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/loop" commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink-common/pkg/values" - txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" - evmconfig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" - evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/forwarder" "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" relayevmtypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" ) @@ -28,15 +27,15 @@ var ( ) type WriteTarget struct { - relayer loop.Relayer - config evmconfig.ChainScopedConfig - cr commontypes.ContractReader - cw EvmChainWriter + relayer loop.Relayer + cr commontypes.ContractReader + cw commontypes.ChainWriter + forwarderAddress string capabilities.CapabilityInfo lggr logger.Logger } -func NewWriteTarget(ctx context.Context, relayer loop.Relayer, chain legacyevm.Chain, lggr logger.Logger) (*WriteTarget, error) { +func NewEvmWriteTarget(ctx context.Context, relayer loop.Relayer, chain legacyevm.Chain, lggr logger.Logger) (*WriteTarget, error) { // generate ID based on chain selector name := fmt.Sprintf("write_%v", chain.ID()) chainName, err := chainselectors.NameFromChainId(chain.ID().Uint64()) @@ -53,7 +52,7 @@ func NewWriteTarget(ctx context.Context, relayer loop.Relayer, chain legacyevm.C ) // EVM-specific init - config := chain.Config() + config := chain.Config().EVM().ChainWriter() // Initialize a reader to check whether a value was already transmitted on chain contractReaderConfigEncoded, err := json.Marshal(relayevmtypes.ChainReaderConfig{ @@ -75,18 +74,37 @@ func NewWriteTarget(ctx context.Context, relayer loop.Relayer, chain legacyevm.C if err != nil { return nil, err } + cr.Bind(ctx, []commontypes.BoundContract{{ + Address: config.ForwarderAddress().String(), + Name: "forwarder", + }}) - cw := EvmChainWriter{ - chain, + logger := lggr.Named("WriteTarget") + + chainWriterConfig := relayevmtypes.ChainWriterConfig{ + Contracts: map[string]relayevmtypes.ChainWriter{ + "forwarder": { + ContractABI: forwarder.KeystoneForwarderABI, + Configs: map[string]*relayevmtypes.ChainWriterDefinition{ + "report": { + ChainSpecificName: "report", + Checker: "simulate", + FromAddress: config.FromAddress().Address(), + GasLimit: 200_000, + }, + }, + }, + }, } + cw := evm.NewChainWriterService(logger, chain.Client(), chain.TxManager(), chainWriterConfig) return &WriteTarget{ relayer, - config, cr, cw, + config.ForwarderAddress().String(), info, - lggr.Named("WriteTarget"), + logger, }, nil } @@ -118,45 +136,8 @@ func success() <-chan capabilities.CapabilityResponse { return callback } -type EvmChainWriter struct { - chain legacyevm.Chain -} - -func (cw *EvmChainWriter) CreateTransaction(ctx context.Context, reqConfig EvmConfig) (tx txmgr.Tx, err error) { - txm := cw.chain.TxManager() - config := cw.chain.Config().EVM().ChainWriter() - - // construct forwarder payload - calldata, err := forwardABI.Pack("report", common.HexToAddress(reqConfig.Address), inputs.Report, inputs.Signatures) - if err != nil { - return tx, err - } - - txMeta := &txmgr.TxMeta{ - // FwdrDestAddress could also be set for better logging but it's used for various purposes around Operator Forwarders - // WorkflowExecutionID: &request.Metadata.WorkflowExecutionID, // TODO: remove? - } - req := txmgr.TxRequest{ - FromAddress: config.FromAddress().Address(), - ToAddress: config.ForwarderAddress().Address(), - EncodedPayload: calldata, - FeeLimit: uint64(defaultGasLimit), - Meta: txMeta, - Strategy: txmgrcommon.NewSendEveryStrategy(), - Checker: txmgr.TransmitCheckerSpec{ - CheckerType: txmgr.TransmitCheckerTypeSimulate, - }, - } - return txm.CreateTransaction(ctx, req) -} - -var forwardABI = evmtypes.MustGetABI(forwarder.KeystoneForwarderMetaData.ABI) - -const defaultGasLimit = 200000 - func (cap *WriteTarget) Execute(ctx context.Context, request capabilities.CapabilityRequest) (<-chan capabilities.CapabilityResponse, error) { cap.lggr.Debugw("Execute", "request", request) - // TODO: idempotency reqConfig, err := parseConfig(request.Config) if err != nil { @@ -169,27 +150,12 @@ func (cap *WriteTarget) Execute(ctx context.Context, request capabilities.Capabi if inputs.Report == nil { // We received any empty report -- this means we should skip transmission. cap.lggr.Debugw("Skipping empty report", "request", request) - callback := make(chan capabilities.CapabilityResponse) - go func() { - // TODO: cast tx.Error to Err (or Value to Value?) - callback <- capabilities.CapabilityResponse{ - Value: nil, - Err: nil, - } - close(callback) - }() - return callback, nil + return success(), nil } // TODO: validate encoded report is prefixed with workflowID and executionID that match the request meta - config := cap.config.EVM().ChainWriter() - // Check whether value was already transmitted on chain - cap.cr.Bind(ctx, []commontypes.BoundContract{{ - Address: config.ForwarderAddress().String(), - Name: "forwarder", - }}) queryInputs := struct { Receiver string WorkflowExecutionID []byte @@ -206,22 +172,18 @@ func (cap *WriteTarget) Execute(ctx context.Context, request capabilities.Capabi return success(), nil } - tx, err := cap.cw.CreateTransaction(ctx, reqConfig) + txID, err := uuid.NewUUID() // TODO(archseer): it seems odd that CW expects us to generate an ID, rather than return one if err != nil { return nil, err } - cap.lggr.Debugw("Transaction submitted", "request", request, "transaction", tx) - - callback := make(chan capabilities.CapabilityResponse) - go func() { - // TODO: cast tx.Error to Err (or Value to Value?) - callback <- capabilities.CapabilityResponse{ - Value: nil, - Err: nil, - } - close(callback) - }() - return callback, nil + args := []any{common.HexToAddress(reqConfig.Address), inputs.Report, inputs.Signatures} + meta := commontypes.TxMeta{WorkflowExecutionID: &request.Metadata.WorkflowExecutionID} + value := big.NewInt(0) + if err := cap.cw.SubmitTransaction(ctx, "forwarder", "report", args, txID, cap.forwarderAddress, &meta, *value); err != nil { + return nil, err + } + cap.lggr.Debugw("Transaction submitted", "request", request, "transaction", txID) + return success(), nil } func (cap *WriteTarget) RegisterToWorkflow(ctx context.Context, request capabilities.RegisterToWorkflowRequest) error {