Skip to content

Commit 2f1eb9d

Browse files
feat(utils): introduce merge sequence
Introduced ExecuteOnChainSequenceAndMerge util which originated from ccip tooling api [here](https://github.com/smartcontractkit/chainlink-ccip/blob/298ed1c38d5cda61a688beb2494a96a5409daf3d/deployment/utils/sequences/sequences.go#L46) This utils allows user to execute multiple sequence and accumulate their results into an object call OnChainOutput JIRA: https://smartcontract-it.atlassian.net/browse/CLD-2464
1 parent 7332038 commit 2f1eb9d

2 files changed

Lines changed: 145 additions & 0 deletions

File tree

changeset/sequenceutils/merge.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package sequenceutils
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/smartcontractkit/chainlink-deployments-framework/operations"
7+
)
8+
9+
// ExecuteOnChainSequenceAndMerge executes a sequence and merges the output into the given OnChainOutput.
10+
// On sequence execution failure, the accumulated agg is returned unchanged together with the error.
11+
func ExecuteOnChainSequenceAndMerge[IN any, DEP any](
12+
b operations.Bundle,
13+
deps DEP,
14+
seq *operations.Sequence[IN, OnChainOutput, DEP],
15+
input IN,
16+
agg OnChainOutput,
17+
) (OnChainOutput, error) {
18+
report, err := operations.ExecuteSequence(b, seq, deps, input)
19+
if err != nil {
20+
return agg, fmt.Errorf("failed to execute %s: %w", seq.ID(), err)
21+
}
22+
agg.BatchOps = append(agg.BatchOps, report.Output.BatchOps...)
23+
agg.Metadata.Addresses = append(agg.Metadata.Addresses, report.Output.Metadata.Addresses...)
24+
agg.Metadata.Contracts = append(agg.Metadata.Contracts, report.Output.Metadata.Contracts...)
25+
agg.Metadata.Chains = append(agg.Metadata.Chains, report.Output.Metadata.Chains...)
26+
if report.Output.Metadata.Env != nil {
27+
if agg.Metadata.Env != nil {
28+
return agg, fmt.Errorf("conflicting env metadata from sequence %s", seq.ID())
29+
}
30+
agg.Metadata.Env = report.Output.Metadata.Env
31+
}
32+
33+
return agg, nil
34+
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package sequenceutils
2+
3+
import (
4+
"errors"
5+
"testing"
6+
7+
"github.com/Masterminds/semver/v3"
8+
mcms_types "github.com/smartcontractkit/mcms/types"
9+
"github.com/stretchr/testify/require"
10+
11+
"github.com/smartcontractkit/chainlink-deployments-framework/datastore"
12+
"github.com/smartcontractkit/chainlink-deployments-framework/operations"
13+
)
14+
15+
func TestExecuteOnChainSequenceAndMerge_success(t *testing.T) {
16+
t.Parallel()
17+
18+
env := testEnvironment(t)
19+
seq := testSequence(t, func(_ operations.Bundle, _ struct{}, _ struct{}) (OnChainOutput, error) {
20+
return OnChainOutput{
21+
BatchOps: []mcms_types.BatchOperation{sampleBatchOp()},
22+
Metadata: datastore.MetadataBundle{
23+
Addresses: []datastore.AddressRef{{
24+
Address: "0xabc",
25+
ChainSelector: 1,
26+
Type: "Timelock",
27+
Version: semver.MustParse("1.0.0"),
28+
}},
29+
Chains: []datastore.ChainMetadata{{ChainSelector: 1, Metadata: "chain-a"}},
30+
},
31+
}, nil
32+
})
33+
34+
agg, err := ExecuteOnChainSequenceAndMerge(env.OperationsBundle, struct{}{}, seq, struct{}{}, OnChainOutput{})
35+
require.NoError(t, err)
36+
require.Len(t, agg.BatchOps, 1)
37+
require.Len(t, agg.Metadata.Addresses, 1)
38+
require.Len(t, agg.Metadata.Chains, 1)
39+
}
40+
41+
func TestExecuteOnChainSequenceAndMerge_preservesAggOnExecuteFailure(t *testing.T) {
42+
t.Parallel()
43+
44+
env := testEnvironment(t)
45+
seqErr := errors.New("sequence failed")
46+
47+
okSeq := testSequence(t, func(_ operations.Bundle, _ struct{}, _ struct{}) (OnChainOutput, error) {
48+
return OnChainOutput{
49+
Metadata: datastore.MetadataBundle{
50+
Addresses: []datastore.AddressRef{{
51+
Address: "0xabc",
52+
ChainSelector: 1,
53+
Type: "Timelock",
54+
Version: semver.MustParse("1.0.0"),
55+
}},
56+
},
57+
}, nil
58+
})
59+
failSeq := testSequence(t, func(_ operations.Bundle, _ struct{}, _ struct{}) (OnChainOutput, error) {
60+
return OnChainOutput{}, seqErr
61+
})
62+
63+
agg, err := ExecuteOnChainSequenceAndMerge(env.OperationsBundle, struct{}{}, okSeq, struct{}{}, OnChainOutput{})
64+
require.NoError(t, err)
65+
require.Len(t, agg.Metadata.Addresses, 1)
66+
67+
agg, err = ExecuteOnChainSequenceAndMerge(env.OperationsBundle, struct{}{}, failSeq, struct{}{}, agg)
68+
require.Error(t, err)
69+
require.ErrorContains(t, err, seqErr.Error())
70+
require.Len(t, agg.Metadata.Addresses, 1)
71+
}
72+
73+
func TestExecuteOnChainSequenceAndMerge_appendsChainsWithoutDeduping(t *testing.T) {
74+
t.Parallel()
75+
76+
env := testEnvironment(t)
77+
seq := testSequence(t, func(_ operations.Bundle, _ struct{}, _ struct{}) (OnChainOutput, error) {
78+
return OnChainOutput{
79+
Metadata: datastore.MetadataBundle{
80+
Chains: []datastore.ChainMetadata{{ChainSelector: 1, Metadata: "a"}},
81+
},
82+
}, nil
83+
})
84+
85+
agg := OnChainOutput{}
86+
agg, err := ExecuteOnChainSequenceAndMerge(env.OperationsBundle, struct{}{}, seq, struct{}{}, agg)
87+
require.NoError(t, err)
88+
agg, err = ExecuteOnChainSequenceAndMerge(env.OperationsBundle, struct{}{}, seq, struct{}{}, agg)
89+
require.NoError(t, err)
90+
require.Len(t, agg.Metadata.Chains, 2)
91+
require.Equal(t, uint64(1), agg.Metadata.Chains[0].ChainSelector)
92+
require.Equal(t, uint64(1), agg.Metadata.Chains[1].ChainSelector)
93+
}
94+
95+
func TestExecuteOnChainSequenceAndMerge_envConflict(t *testing.T) {
96+
t.Parallel()
97+
98+
env := testEnvironment(t)
99+
envMeta := &datastore.EnvMetadata{Metadata: "staging"}
100+
seq := testSequence(t, func(_ operations.Bundle, _ struct{}, _ struct{}) (OnChainOutput, error) {
101+
return OnChainOutput{
102+
Metadata: datastore.MetadataBundle{Env: envMeta},
103+
}, nil
104+
})
105+
106+
agg := OnChainOutput{Metadata: datastore.MetadataBundle{Env: &datastore.EnvMetadata{Metadata: "prod"}}}
107+
agg, err := ExecuteOnChainSequenceAndMerge(env.OperationsBundle, struct{}{}, seq, struct{}{}, agg)
108+
require.Error(t, err)
109+
require.ErrorContains(t, err, "conflicting env metadata")
110+
require.Equal(t, "prod", agg.Metadata.Env.Metadata)
111+
}

0 commit comments

Comments
 (0)