diff --git a/deployment/keystone/changeset/internal/test/utils.go b/deployment/keystone/changeset/internal/test/utils.go index 0a23f7e60a7..21c1736ac8e 100644 --- a/deployment/keystone/changeset/internal/test/utils.go +++ b/deployment/keystone/changeset/internal/test/utils.go @@ -21,14 +21,47 @@ import ( "github.com/smartcontractkit/chainlink/deployment/keystone/changeset/internal" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry" kcr "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry" + workflow_registry "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/workflow/generated/workflow_registry_wrapper" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey" ) +type SetupTestWorkflowRegistryResponse struct { + Registry *workflow_registry.WorkflowRegistry + Chain deployment.Chain + RegistrySelector uint64 + AddressBook deployment.AddressBook +} + +func SetupTestWorkflowRegistry(t *testing.T, lggr logger.Logger, chainSel uint64) *SetupTestWorkflowRegistryResponse { + chain := testChain(t) + + deployer, err := kslib.NewWorkflowRegistryDeployer() + require.NoError(t, err) + resp, err := deployer.Deploy(kslib.DeployRequest{Chain: chain}) + require.NoError(t, err) + + addressBook := deployment.NewMemoryAddressBookFromMap( + map[uint64]map[string]deployment.TypeAndVersion{ + chainSel: map[string]deployment.TypeAndVersion{ + resp.Address.Hex(): resp.Tv, + }, + }, + ) + + return &SetupTestWorkflowRegistryResponse{ + Registry: deployer.Contract(), + Chain: chain, + RegistrySelector: chain.Selector, + AddressBook: addressBook, + } +} + type Don struct { Name string P2PIDs []p2pkey.PeerID CapabilityConfigs []internal.CapabilityConfig } + type SetupTestRegistryRequest struct { P2pToCapabilities map[p2pkey.PeerID][]kcr.CapabilitiesRegistryCapability NopToNodes map[kcr.CapabilitiesRegistryNodeOperator][]*internal.P2PSignerEnc diff --git a/deployment/keystone/changeset/workflowregistry/deploy.go b/deployment/keystone/changeset/workflowregistry/deploy.go new file mode 100644 index 00000000000..352336dd168 --- /dev/null +++ b/deployment/keystone/changeset/workflowregistry/deploy.go @@ -0,0 +1,26 @@ +package workflowregistry + +import ( + "fmt" + + "github.com/smartcontractkit/chainlink/deployment" + kslib "github.com/smartcontractkit/chainlink/deployment/keystone" +) + +var _ deployment.ChangeSet[uint64] = Deploy + +func Deploy(env deployment.Environment, registrySelector uint64) (deployment.ChangesetOutput, error) { + lggr := env.Logger + chain, ok := env.Chains[registrySelector] + if !ok { + return deployment.ChangesetOutput{}, fmt.Errorf("chain not found in environment") + } + ab := deployment.NewMemoryAddressBook() + wrResp, err := kslib.DeployWorkflowRegistry(chain, ab) + if err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("failed to deploy CapabilitiesRegistry: %w", err) + } + lggr.Infof("Deployed %s chain selector %d addr %s", wrResp.Tv.String(), chain.Selector, wrResp.Address.String()) + + return deployment.ChangesetOutput{AddressBook: ab}, nil +} diff --git a/deployment/keystone/changeset/workflowregistry/deploy_test.go b/deployment/keystone/changeset/workflowregistry/deploy_test.go new file mode 100644 index 00000000000..16eb6fa8512 --- /dev/null +++ b/deployment/keystone/changeset/workflowregistry/deploy_test.go @@ -0,0 +1,38 @@ +package workflowregistry + +import ( + "testing" + + "go.uber.org/zap/zapcore" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink/deployment/environment/memory" +) + +func Test_Deploy(t *testing.T) { + t.Parallel() + lggr := logger.Test(t) + cfg := memory.MemoryEnvironmentConfig{ + Nodes: 1, // nodes unused but required in config + Chains: 2, + } + env := memory.NewMemoryEnvironment(t, lggr, zapcore.DebugLevel, cfg) + + registrySel := env.AllChainSelectors()[0] + + resp, err := Deploy(env, registrySel) + require.NoError(t, err) + require.NotNil(t, resp) + // OCR3 should be deployed on chain 0 + addrs, err := resp.AddressBook.AddressesForChain(registrySel) + require.NoError(t, err) + require.Len(t, addrs, 1) + + // nothing on chain 1 + require.NotEqual(t, registrySel, env.AllChainSelectors()[1]) + oaddrs, _ := resp.AddressBook.AddressesForChain(env.AllChainSelectors()[1]) + assert.Len(t, oaddrs, 0) +} diff --git a/deployment/keystone/changeset/workflowregistry/strategies.go b/deployment/keystone/changeset/workflowregistry/strategies.go new file mode 100644 index 00000000000..f799092d4ce --- /dev/null +++ b/deployment/keystone/changeset/workflowregistry/strategies.go @@ -0,0 +1,86 @@ +package workflowregistry + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + + "github.com/smartcontractkit/ccip-owner-contracts/pkg/gethwrappers" + "github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/mcms" + "github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock" + + "github.com/smartcontractkit/chainlink/deployment" + "github.com/smartcontractkit/chainlink/deployment/common/proposalutils" + kslib "github.com/smartcontractkit/chainlink/deployment/keystone" + "github.com/smartcontractkit/chainlink/deployment/keystone/changeset" +) + +type strategy interface { + Apply(callFn func(opts *bind.TransactOpts) (*types.Transaction, error)) (deployment.ChangesetOutput, error) +} + +type simpleTransaction struct { + chain deployment.Chain +} + +func (s *simpleTransaction) Apply(callFn func(opts *bind.TransactOpts) (*types.Transaction, error)) (deployment.ChangesetOutput, error) { + tx, err := callFn(s.chain.DeployerKey) + if err != nil { + return deployment.ChangesetOutput{}, err + } + + _, err = s.chain.Confirm(tx) + return deployment.ChangesetOutput{}, err +} + +type mcmsTransaction struct { + Config *changeset.MCMSConfig + Description string + Address common.Address + ChainSel uint64 + ContractSet *kslib.ContractSet +} + +func (m *mcmsTransaction) Apply(callFn func(opts *bind.TransactOpts) (*types.Transaction, error)) (deployment.ChangesetOutput, error) { + opts := deployment.SimTransactOpts() + + tx, err := callFn(opts) + if err != nil { + return deployment.ChangesetOutput{}, err + } + + op := timelock.BatchChainOperation{ + ChainIdentifier: mcms.ChainIdentifier(m.ChainSel), + Batch: []mcms.Operation{ + { + Data: tx.Data(), + To: m.Address, + Value: big.NewInt(0), + }, + }, + } + + timelocksPerChain := map[uint64]common.Address{ + m.ChainSel: m.ContractSet.Timelock.Address(), + } + proposerMCMSes := map[uint64]*gethwrappers.ManyChainMultiSig{ + m.ChainSel: m.ContractSet.ProposerMcm, + } + + proposal, err := proposalutils.BuildProposalFromBatches( + timelocksPerChain, + proposerMCMSes, + []timelock.BatchChainOperation{op}, + m.Description, + m.Config.MinDuration, + ) + if err != nil { + return deployment.ChangesetOutput{}, err + } + + return deployment.ChangesetOutput{ + Proposals: []timelock.MCMSWithTimelockProposal{*proposal}, + }, nil +} diff --git a/deployment/keystone/changeset/workflowregistry/update_allowed_dons.go b/deployment/keystone/changeset/workflowregistry/update_allowed_dons.go new file mode 100644 index 00000000000..7d3a20add02 --- /dev/null +++ b/deployment/keystone/changeset/workflowregistry/update_allowed_dons.go @@ -0,0 +1,81 @@ +package workflowregistry + +import ( + "errors" + "fmt" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/core/types" + + "github.com/smartcontractkit/chainlink/deployment" + + kslib "github.com/smartcontractkit/chainlink/deployment/keystone" + "github.com/smartcontractkit/chainlink/deployment/keystone/changeset" + workflow_registry "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/workflow/generated/workflow_registry_wrapper" +) + +var _ deployment.ChangeSet[*UpdateAllowedDonsRequest] = UpdateAllowedDons + +type UpdateAllowedDonsRequest struct { + RegistryChainSel uint64 + DonIDs []uint32 + Allowed bool + + MCMSConfig *changeset.MCMSConfig +} + +func (r *UpdateAllowedDonsRequest) Validate() error { + if len(r.DonIDs) == 0 { + return errors.New("Must provide at least one DonID") + } + return nil +} + +// UpdateAllowedDons updates the list of DONs that workflows can be sent to. +func UpdateAllowedDons(env deployment.Environment, req *UpdateAllowedDonsRequest) (deployment.ChangesetOutput, error) { + if err := req.Validate(); err != nil { + return deployment.ChangesetOutput{}, err + } + + resp, err := kslib.GetContractSets(env.Logger, &kslib.GetContractSetsRequest{ + Chains: env.Chains, + AddressBook: env.ExistingAddresses, + }) + if err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("failed to get contract sets: %w", err) + } + + cs := resp.ContractSets[req.RegistryChainSel] + if cs.WorkflowRegistry == nil { + return deployment.ChangesetOutput{}, errors.New("could not find workflow registry") + } + registry := cs.WorkflowRegistry + + chain, ok := env.Chains[req.RegistryChainSel] + if !ok { + return deployment.ChangesetOutput{}, fmt.Errorf("registry chain selector %d does not exist in environment", req.RegistryChainSel) + } + + var s strategy + if req.MCMSConfig != nil { + s = &mcmsTransaction{ + Config: req.MCMSConfig, + Description: "proposal to update allowed dons", + Address: registry.Address(), + ChainSel: chain.Selector, + ContractSet: &cs, + } + } else { + s = &simpleTransaction{ + chain: chain, + } + } + + return s.Apply(func(opts *bind.TransactOpts) (*types.Transaction, error) { + tx, err := registry.UpdateAllowedDONs(chain.DeployerKey, req.DonIDs, req.Allowed) + if err != nil { + err = kslib.DecodeErr(workflow_registry.WorkflowRegistryABI, err) + } + return tx, err + }) +} diff --git a/deployment/keystone/changeset/workflowregistry/update_allowed_dons_test.go b/deployment/keystone/changeset/workflowregistry/update_allowed_dons_test.go new file mode 100644 index 00000000000..53e738a3f72 --- /dev/null +++ b/deployment/keystone/changeset/workflowregistry/update_allowed_dons_test.go @@ -0,0 +1,69 @@ +package workflowregistry_test + +import ( + "testing" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" + + chain_selectors "github.com/smartcontractkit/chain-selectors" + + "github.com/smartcontractkit/chainlink/deployment" + kstest "github.com/smartcontractkit/chainlink/deployment/keystone/changeset/internal/test" + "github.com/smartcontractkit/chainlink/deployment/keystone/changeset/workflowregistry" +) + +func TestUpdateAllowedDons(t *testing.T) { + lggr := logger.Test(t) + + chainSel := chain_selectors.ETHEREUM_TESTNET_SEPOLIA.Selector + resp := kstest.SetupTestWorkflowRegistry(t, lggr, chainSel) + registry := resp.Registry + + dons, err := registry.GetAllAllowedDONs(&bind.CallOpts{}) + require.NoError(t, err) + + assert.Len(t, dons, 0) + + env := deployment.Environment{ + Logger: lggr, + Chains: map[uint64]deployment.Chain{ + chainSel: resp.Chain, + }, + ExistingAddresses: resp.AddressBook, + } + + _, err = workflowregistry.UpdateAllowedDons( + env, + &workflowregistry.UpdateAllowedDonsRequest{ + RegistryChainSel: chainSel, + DonIDs: []uint32{1}, + Allowed: true, + }, + ) + require.NoError(t, err) + + dons, err = registry.GetAllAllowedDONs(&bind.CallOpts{}) + require.NoError(t, err) + + assert.Len(t, dons, 1) + assert.Equal(t, dons[0], uint32(1)) + + _, err = workflowregistry.UpdateAllowedDons( + env, + &workflowregistry.UpdateAllowedDonsRequest{ + RegistryChainSel: chainSel, + DonIDs: []uint32{1}, + Allowed: false, + }, + ) + require.NoError(t, err) + + dons, err = registry.GetAllAllowedDONs(&bind.CallOpts{}) + require.NoError(t, err) + + assert.Len(t, dons, 0) +} diff --git a/deployment/keystone/changeset/workflowregistry/update_authorized_addresses.go b/deployment/keystone/changeset/workflowregistry/update_authorized_addresses.go new file mode 100644 index 00000000000..f05c6cd58c9 --- /dev/null +++ b/deployment/keystone/changeset/workflowregistry/update_authorized_addresses.go @@ -0,0 +1,106 @@ +package workflowregistry + +import ( + "errors" + "fmt" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + + "github.com/smartcontractkit/chainlink/deployment" + + kslib "github.com/smartcontractkit/chainlink/deployment/keystone" + "github.com/smartcontractkit/chainlink/deployment/keystone/changeset" + workflow_registry "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/workflow/generated/workflow_registry_wrapper" +) + +var _ deployment.ChangeSet[*UpdateAuthorizedAddressesRequest] = UpdateAuthorizedAddresses + +type UpdateAuthorizedAddressesRequest struct { + RegistryChainSel uint64 + + Addresses []string + Allowed bool + + MCMSConfig *changeset.MCMSConfig +} + +func (r *UpdateAuthorizedAddressesRequest) Validate() error { + if len(r.Addresses) == 0 { + return errors.New("Must provide at least 1 address") + } + + return nil +} + +func getWorkflowRegistry(env deployment.Environment, chainSel uint64) (*workflow_registry.WorkflowRegistry, error) { + resp, err := kslib.GetContractSets(env.Logger, &kslib.GetContractSetsRequest{ + Chains: env.Chains, + AddressBook: env.ExistingAddresses, + }) + if err != nil { + return nil, fmt.Errorf("failed to get contract sets: %w", err) + } + + cs := resp.ContractSets[chainSel] + if cs.WorkflowRegistry == nil { + return nil, errors.New("could not find workflow registry") + } + + return cs.WorkflowRegistry, nil +} + +// UpdateAuthorizedAddresses updates the list of DONs that workflows can be sent to. +func UpdateAuthorizedAddresses(env deployment.Environment, req *UpdateAuthorizedAddressesRequest) (deployment.ChangesetOutput, error) { + if err := req.Validate(); err != nil { + return deployment.ChangesetOutput{}, err + } + + resp, err := kslib.GetContractSets(env.Logger, &kslib.GetContractSetsRequest{ + Chains: env.Chains, + AddressBook: env.ExistingAddresses, + }) + if err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("failed to get contract sets: %w", err) + } + + cs := resp.ContractSets[req.RegistryChainSel] + if cs.WorkflowRegistry == nil { + return deployment.ChangesetOutput{}, errors.New("could not find workflow registry") + } + registry := cs.WorkflowRegistry + + chain, ok := env.Chains[req.RegistryChainSel] + if !ok { + return deployment.ChangesetOutput{}, fmt.Errorf("registry chain selector %d does not exist in environment", req.RegistryChainSel) + } + + var addr []common.Address + for _, a := range req.Addresses { + addr = append(addr, common.HexToAddress(a)) + } + + var s strategy + if req.MCMSConfig != nil { + s = &mcmsTransaction{ + Config: req.MCMSConfig, + Description: "proposal to update authorized addresses", + Address: registry.Address(), + ChainSel: chain.Selector, + ContractSet: &cs, + } + } else { + s = &simpleTransaction{ + chain: chain, + } + } + + return s.Apply(func(opts *bind.TransactOpts) (*types.Transaction, error) { + tx, err := registry.UpdateAuthorizedAddresses(opts, addr, req.Allowed) + if err != nil { + err = kslib.DecodeErr(workflow_registry.WorkflowRegistryABI, err) + } + return tx, err + }) +} diff --git a/deployment/keystone/changeset/workflowregistry/update_authorized_addresses_test.go b/deployment/keystone/changeset/workflowregistry/update_authorized_addresses_test.go new file mode 100644 index 00000000000..47ec23c9e24 --- /dev/null +++ b/deployment/keystone/changeset/workflowregistry/update_authorized_addresses_test.go @@ -0,0 +1,71 @@ +package workflowregistry_test + +import ( + "testing" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + chain_selectors "github.com/smartcontractkit/chain-selectors" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" + + "github.com/smartcontractkit/chainlink/deployment" + kstest "github.com/smartcontractkit/chainlink/deployment/keystone/changeset/internal/test" + "github.com/smartcontractkit/chainlink/deployment/keystone/changeset/workflowregistry" +) + +func TestUpdateAuthorizedAddresses(t *testing.T) { + lggr := logger.Test(t) + + chainSel := chain_selectors.ETHEREUM_TESTNET_SEPOLIA.Selector + resp := kstest.SetupTestWorkflowRegistry(t, lggr, chainSel) + registry := resp.Registry + + dons, err := registry.GetAllAuthorizedAddresses(&bind.CallOpts{}) + require.NoError(t, err) + + assert.Len(t, dons, 0) + + env := deployment.Environment{ + Logger: lggr, + Chains: map[uint64]deployment.Chain{ + chainSel: resp.Chain, + }, + ExistingAddresses: resp.AddressBook, + } + + addr := "0xc0ffee254729296a45a3885639AC7E10F9d54979" + _, err = workflowregistry.UpdateAuthorizedAddresses( + env, + &workflowregistry.UpdateAuthorizedAddressesRequest{ + RegistryChainSel: chainSel, + Addresses: []string{addr}, + Allowed: true, + }, + ) + require.NoError(t, err) + + dons, err = registry.GetAllAuthorizedAddresses(&bind.CallOpts{}) + require.NoError(t, err) + + assert.Len(t, dons, 1) + assert.Equal(t, dons[0], common.HexToAddress(addr)) + + _, err = workflowregistry.UpdateAuthorizedAddresses( + env, + &workflowregistry.UpdateAuthorizedAddressesRequest{ + RegistryChainSel: chainSel, + Addresses: []string{addr}, + Allowed: false, + }, + ) + require.NoError(t, err) + + dons, err = registry.GetAllAuthorizedAddresses(&bind.CallOpts{}) + require.NoError(t, err) + + assert.Len(t, dons, 0) +} diff --git a/deployment/keystone/contract_set.go b/deployment/keystone/contract_set.go index ee503a54b4d..51b5c823600 100644 --- a/deployment/keystone/contract_set.go +++ b/deployment/keystone/contract_set.go @@ -58,6 +58,21 @@ func DeployCapabilitiesRegistry(chain deployment.Chain, ab deployment.AddressBoo return capabilitiesRegistryResp, nil } +// DeployWorkflowRegistry deploys the WorkflowRegistry contract to the chain +// and saves the address in the address book. This mutates the address book. +func DeployWorkflowRegistry(chain deployment.Chain, ab deployment.AddressBook) (*DeployResponse, error) { + deployer, err := NewWorkflowRegistryDeployer() + resp, err := deployer.Deploy(DeployRequest{Chain: chain}) + if err != nil { + return nil, fmt.Errorf("failed to deploy WorkflowRegistry: %w", err) + } + err = ab.Save(chain.Selector, resp.Address.String(), resp.Tv) + if err != nil { + return nil, fmt.Errorf("failed to save WorkflowRegistry: %w", err) + } + return resp, nil +} + // DeployOCR3 deploys the OCR3Capability contract to the chain // and saves the address in the address book. This mutates the address book. func DeployOCR3(chain deployment.Chain, ab deployment.AddressBook) (*DeployResponse, error) { diff --git a/deployment/keystone/state.go b/deployment/keystone/state.go index 0ac7cdc89ed..38b89b792c7 100644 --- a/deployment/keystone/state.go +++ b/deployment/keystone/state.go @@ -14,6 +14,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/forwarder" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/ocr3_capability" + workflow_registry "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/workflow/generated/workflow_registry_wrapper" ) type GetContractSetsRequest struct { @@ -30,6 +31,7 @@ type ContractSet struct { OCR3 *ocr3_capability.OCR3Capability Forwarder *forwarder.KeystoneForwarder CapabilitiesRegistry *capabilities_registry.CapabilitiesRegistry + WorkflowRegistry *workflow_registry.WorkflowRegistry } func (cs ContractSet) TransferableContracts() []common.Address { @@ -43,6 +45,9 @@ func (cs ContractSet) TransferableContracts() []common.Address { if cs.CapabilitiesRegistry != nil { out = append(out, cs.CapabilitiesRegistry.Address()) } + if cs.WorkflowRegistry != nil { + out = append(out, cs.WorkflowRegistry.Address()) + } return out } @@ -105,6 +110,12 @@ func loadContractSet(lggr logger.Logger, chain deployment.Chain, addresses map[s return nil, fmt.Errorf("failed to create OCR3Capability contract from address %s: %w", addr, err) } out.OCR3 = c + case WorkflowRegistry: + c, err := workflow_registry.NewWorkflowRegistry(common.HexToAddress(addr), chain.Client) + if err != nil { + return nil, fmt.Errorf("failed to create OCR3Capability contract from address %s: %w", addr, err) + } + out.WorkflowRegistry = c default: lggr.Warnw("unknown contract type", "type", tv.Type) // ignore unknown contract types diff --git a/deployment/keystone/types.go b/deployment/keystone/types.go index d406487043c..cb1373d446c 100644 --- a/deployment/keystone/types.go +++ b/deployment/keystone/types.go @@ -21,6 +21,7 @@ import ( var ( CapabilitiesRegistry deployment.ContractType = "CapabilitiesRegistry" // https://github.com/smartcontractkit/chainlink/blob/50c1b3dbf31bd145b312739b08967600a5c67f30/contracts/src/v0.8/keystone/CapabilitiesRegistry.sol#L392 + WorkflowRegistry deployment.ContractType = "WorkflowRegistry" // https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.8/workflow/dev/WorkflowRegistry.sol KeystoneForwarder deployment.ContractType = "KeystoneForwarder" // https://github.com/smartcontractkit/chainlink/blob/50c1b3dbf31bd145b312739b08967600a5c67f30/contracts/src/v0.8/keystone/KeystoneForwarder.sol#L90 OCR3Capability deployment.ContractType = "OCR3Capability" // https://github.com/smartcontractkit/chainlink/blob/50c1b3dbf31bd145b312739b08967600a5c67f30/contracts/src/v0.8/keystone/OCR3Capability.sol#L12 FeedConsumer deployment.ContractType = "FeedConsumer" // no type and a version in contract https://github.com/smartcontractkit/chainlink/blob/89183a8a5d22b1aeca0ade3b76d16aa84067aa57/contracts/src/v0.8/keystone/KeystoneFeedsConsumer.sol#L1 diff --git a/deployment/keystone/workflow_registry_deployer.go b/deployment/keystone/workflow_registry_deployer.go new file mode 100644 index 00000000000..794e6ad0202 --- /dev/null +++ b/deployment/keystone/workflow_registry_deployer.go @@ -0,0 +1,65 @@ +package keystone + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" + + "github.com/smartcontractkit/chainlink/deployment" + workflow_registry "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/workflow/generated/workflow_registry_wrapper" +) + +type WorkflowRegistryDeployer struct { + lggr logger.Logger + contract *workflow_registry.WorkflowRegistry +} + +func NewWorkflowRegistryDeployer() (*WorkflowRegistryDeployer, error) { + lggr, err := logger.New() + if err != nil { + return nil, err + } + return &WorkflowRegistryDeployer{lggr: lggr}, nil +} + +func (c *WorkflowRegistryDeployer) Contract() *workflow_registry.WorkflowRegistry { + return c.contract +} + +func (c *WorkflowRegistryDeployer) Deploy(req DeployRequest) (*DeployResponse, error) { + est, err := estimateDeploymentGas(req.Chain.Client, workflow_registry.WorkflowRegistryABI) + if err != nil { + return nil, fmt.Errorf("failed to estimate gas: %w", err) + } + c.lggr.Debugf("WorkflowRegistry estimated gas: %d", est) + + addr, tx, wr, err := workflow_registry.DeployWorkflowRegistry( + req.Chain.DeployerKey, + req.Chain.Client) + if err != nil { + return nil, DecodeErr(workflow_registry.WorkflowRegistryABI, err) + } + + _, err = req.Chain.Confirm(tx) + if err != nil { + return nil, fmt.Errorf("failed to confirm and save WorkflowRegistry: %w", err) + } + tvStr, err := wr.TypeAndVersion(&bind.CallOpts{}) + if err != nil { + return nil, fmt.Errorf("failed to get type and version: %w", err) + } + + tv, err := deployment.TypeAndVersionFromString(tvStr) + if err != nil { + return nil, fmt.Errorf("failed to parse type and version from %s: %w", tvStr, err) + } + resp := &DeployResponse{ + Address: addr, + Tx: tx.Hash(), + Tv: tv, + } + c.contract = wr + return resp, nil +}