@@ -25,15 +25,19 @@ import (
2525 "github.com/smartcontractkit/chainlink-common/pkg/capabilities/actions/vault"
2626 "github.com/smartcontractkit/chainlink-common/pkg/jsonrpc2"
2727 "github.com/smartcontractkit/chainlink-evm/gethwrappers/workflow/generated/workflow_registry_wrapper_v2"
28+ "github.com/smartcontractkit/chainlink/deployment/cre/workflow_registry/v2/changeset"
2829 "github.com/smartcontractkit/chainlink/v2/core/capabilities/vault/vaulttypes"
2930 "github.com/smartcontractkit/tdh2/go/tdh2/tdh2easy"
3031
3132 "github.com/smartcontractkit/cre-cli/cmd/client"
33+ cmdCommon "github.com/smartcontractkit/cre-cli/cmd/common"
3234 "github.com/smartcontractkit/cre-cli/internal/client/graphqlclient"
3335 "github.com/smartcontractkit/cre-cli/internal/constants"
3436 "github.com/smartcontractkit/cre-cli/internal/credentials"
3537 "github.com/smartcontractkit/cre-cli/internal/environments"
3638 "github.com/smartcontractkit/cre-cli/internal/runtime"
39+ "github.com/smartcontractkit/cre-cli/internal/settings"
40+ "github.com/smartcontractkit/cre-cli/internal/types"
3741 "github.com/smartcontractkit/cre-cli/internal/validation"
3842)
3943
@@ -61,6 +65,7 @@ type Handler struct {
6165 Gw GatewayClient
6266 Wrc * client.WorkflowRegistryV2Client
6367 Credentials * credentials.Credentials
68+ Settings * settings.Settings
6469}
6570
6671// NewHandler creates a new handler instance.
@@ -85,6 +90,7 @@ func NewHandler(ctx *runtime.Context, secretsFilePath string) (*Handler, error)
8590 OwnerAddress : ctx .Settings .Workflow .UserWorkflowSettings .WorkflowOwnerAddress ,
8691 EnvironmentSet : ctx .EnvironmentSet ,
8792 Credentials : ctx .Credentials ,
93+ Settings : ctx .Settings ,
8894 }
8995 h .Gw = & HTTPClient {URL : h .EnvironmentSet .GatewayURL , Client : & http.Client {Timeout : 90 * time .Second }}
9096
@@ -173,7 +179,6 @@ func (h *Handler) ValidateInputs(inputs UpsertSecretsInputs) error {
173179}
174180
175181// TODO: use TxType interface
176- // TODO: implement changeset handling
177182func (h * Handler ) PackAllowlistRequestTxData (reqDigest [32 ]byte , duration time.Duration ) (string , error ) {
178183 contractABI , err := abi .JSON (strings .NewReader (workflow_registry_wrapper_v2 .WorkflowRegistryMetaData .ABI ))
179184 if err != nil {
@@ -404,19 +409,54 @@ func (h *Handler) Execute(
404409 return fmt .Errorf ("unsupported method %q (expected %q or %q)" , method , vaulttypes .MethodSecretsCreate , vaulttypes .MethodSecretsUpdate )
405410 }
406411
407- // MSIG step 1: write bundle & exit
408- if ownerType == constants .WorkflowOwnerTypeMSIG {
409- baseDir := filepath .Dir (h .SecretsFilePath )
410- filename := DeriveBundleFilename (digest ) // <digest>.json
411- bundlePath := filepath .Join (baseDir , filename )
412+ ownerAddr := common .HexToAddress (h .OwnerAddress )
413+
414+ allowlisted , err := h .Wrc .IsRequestAllowlisted (ownerAddr , digest )
415+ if err != nil {
416+ return fmt .Errorf ("allowlist check failed: %w" , err )
417+ }
418+ var txOut * client.TxOutput
419+ if ! allowlisted {
420+ if txOut , err = h .Wrc .AllowlistRequest (digest , duration ); err != nil {
421+ return fmt .Errorf ("allowlist request failed: %w" , err )
422+ }
423+ }
412424
413- ub := & UnsignedBundle {
414- RequestID : requestID ,
415- Method : method ,
416- DigestHex : "0x" + hex .EncodeToString (digest [:]),
417- RequestBody : requestBody ,
418- CreatedAt : time .Now ().UTC (),
425+ gatewayPost := func () error {
426+ respBody , status , err := h .Gw .Post (requestBody )
427+ if err != nil {
428+ return err
429+ }
430+ if status != http .StatusOK {
431+ return fmt .Errorf ("gateway returned a non-200 status code: %d" , status )
419432 }
433+ return h .ParseVaultGatewayResponse (method , respBody )
434+ }
435+
436+ if txOut == nil && allowlisted {
437+ fmt .Printf ("Digest already allowlisted; proceeding to gateway POST: owner=%s, digest=0x%x\n " , ownerAddr .Hex (), digest )
438+ return gatewayPost ()
439+ }
440+
441+ baseDir := filepath .Dir (h .SecretsFilePath )
442+ filename := DeriveBundleFilename (digest ) // <digest>.json
443+ bundlePath := filepath .Join (baseDir , filename )
444+
445+ ub := & UnsignedBundle {
446+ RequestID : requestID ,
447+ Method : method ,
448+ DigestHex : "0x" + hex .EncodeToString (digest [:]),
449+ RequestBody : requestBody ,
450+ CreatedAt : time .Now ().UTC (),
451+ }
452+
453+ switch txOut .Type {
454+ case client .Regular :
455+ fmt .Println ("Transaction confirmed" )
456+ fmt .Printf ("Digest allowlisted; proceeding to gateway POST: owner=%s, digest=0x%x\n " , ownerAddr .Hex (), digest )
457+ fmt .Printf ("View on explorer: \033 ]8;;%s/tx/%s\033 \\ %s/tx/%s\033 ]8;;\033 \\ \n " , h .EnvironmentSet .WorkflowRegistryChainExplorerURL , txOut .Hash , h .EnvironmentSet .WorkflowRegistryChainExplorerURL , txOut .Hash )
458+ return gatewayPost ()
459+ case client .Raw :
420460 if err := SaveBundle (bundlePath , ub ); err != nil {
421461 return fmt .Errorf ("failed to save unsigned bundle at %s: %w" , bundlePath , err )
422462 }
@@ -426,32 +466,45 @@ func (h *Handler) Execute(
426466 return fmt .Errorf ("failed to pack allowlist tx: %w" , err )
427467 }
428468 return h .LogMSIGNextSteps (txData , digest , bundlePath )
429- }
469+ case client .Changeset :
470+ chainSelector , err := settings .GetChainSelectorByChainName (h .EnvironmentSet .WorkflowRegistryChainName )
471+ if err != nil {
472+ return fmt .Errorf ("failed to get chain selector for chain %q: %w" , h .EnvironmentSet .WorkflowRegistryChainName , err )
473+ }
474+ mcmsConfig , err := settings .GetMCMSConfig (h .Settings , chainSelector )
475+ if err != nil {
476+ fmt .Println ("\n MCMS config not found or is incorrect, skipping MCMS config in changeset" )
477+ }
478+ csFile := types.ChangesetFile {
479+ Environment : h .Settings .Workflow .CLDSettings .Environment ,
480+ Domain : h .Settings .Workflow .CLDSettings .Domain ,
481+ Changesets : []types.Changeset {
482+ {
483+ AllowlistRequest : & types.AllowlistRequest {
484+ Payload : changeset.UserAllowlistRequestInput {
485+ ExpiryTimestamp : uint32 (time .Now ().Add (duration ).Unix ()), // #nosec G115 -- int64 to uint32 conversion; Unix() returns seconds since epoch, which fits in uint32 until 2106
486+ RequestDigest : common .Bytes2Hex (digest [:]),
487+ ChainSelector : chainSelector ,
488+ MCMSConfig : mcmsConfig ,
489+ WorkflowRegistryQualifier : h .Settings .Workflow .CLDSettings .WorkflowRegistryQualifier ,
490+ },
491+ },
492+ },
493+ },
494+ }
430495
431- // EOA: allowlist (if needed) and POST
432- ownerAddr := common .HexToAddress (h .OwnerAddress )
496+ fileName := fmt .Sprintf ("AllowlistRequest_%s_%s_%s.yaml" , requestID , h .Settings .Workflow .UserWorkflowSettings .WorkflowOwnerAddress , time .Now ().Format ("20060102_150405" ))
433497
434- allowlisted , err := h .Wrc .IsRequestAllowlisted (ownerAddr , digest )
435- if err != nil {
436- return fmt .Errorf ("allowlist check failed: %w" , err )
437- }
438- if ! allowlisted {
439- if err := h .Wrc .AllowlistRequest (digest , duration ); err != nil {
440- return fmt .Errorf ("allowlist request failed: %w" , err )
498+ if err := SaveBundle (bundlePath , ub ); err != nil {
499+ return fmt .Errorf ("failed to save unsigned bundle at %s: %w" , bundlePath , err )
441500 }
442- fmt .Printf ("Digest allowlisted; proceeding to gateway POST: owner=%s, digest=0x%x\n " , ownerAddr .Hex (), digest )
443- } else {
444- fmt .Printf ("Digest already allowlisted; proceeding to gateway POST: owner=%s, digest=0x%x\n " , ownerAddr .Hex (), digest )
445- }
446501
447- respBody , status , err := h .Gw .Post (requestBody )
448- if err != nil {
449- return err
450- }
451- if status != http .StatusOK {
452- return fmt .Errorf ("gateway returned a non-200 status code: %d" , status )
502+ return cmdCommon .WriteChangesetFile (fileName , & csFile , h .Settings )
503+
504+ default :
505+ h .Log .Warn ().Msgf ("Unsupported transaction type: %s" , txOut .Type )
453506 }
454- return h . ParseVaultGatewayResponse ( method , respBody )
507+ return nil
455508}
456509
457510// ParseVaultGatewayResponse parses the JSON-RPC response, decodes the SignedOCRResponse payload
0 commit comments