Skip to content

Commit af2cb57

Browse files
feat: ContractVersion in OperationMetadata (#979) (#986)
feat: ContractVersion in OperationMetadata (#979) feat: ContractFullyQualifiedName in TON's AdditionalFields fix: revert ContractTypeAndVersion in OperationMetadata (#979)
1 parent 4562a68 commit af2cb57

13 files changed

Lines changed: 260 additions & 192 deletions

File tree

.changeset/bold-olives-smell.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"chainlink-deployments-framework": minor
3+
---
4+
5+
Use Metadata.ContractVersion instead of ContractTypeAndVersion

engine/cld/commands/mcms/testdata/proposal.json

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,39 +24,34 @@
2424
"data": "ebpQlw==",
2525
"value": 0,
2626
"contractType": "",
27-
"contractTypeAndVersion": "",
2827
"tags": null
2928
},
3029
{
3130
"to": "0x53850c7ee54fce09174f21f6c4b5602e4ca46353",
3231
"data": "ebpQlw==",
3332
"value": 0,
3433
"contractType": "",
35-
"contractTypeAndVersion": "",
3634
"tags": null
3735
},
3836
{
3937
"to": "0x7ae20d26f2969ae44054a02e1acf19c3eecff148",
4038
"data": "ebpQlw==",
4139
"value": 0,
4240
"contractType": "",
43-
"contractTypeAndVersion": "",
4441
"tags": null
4542
},
4643
{
4744
"to": "0xed54914840214c01c06de50737ae9d9fe184cc26",
4845
"data": "ebpQlw==",
4946
"value": 0,
5047
"contractType": "",
51-
"contractTypeAndVersion": "",
5248
"tags": null
5349
},
5450
{
5551
"to": "0xfa2a8aa2982999f3df3b66405886c4007d6d8418",
5652
"data": "ebpQlw==",
5753
"value": 0,
5854
"contractType": "",
59-
"contractTypeAndVersion": "",
6055
"tags": null
6156
}
6257
]

engine/test/internal/mcmsutils/encoding_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ func TestDecodeProposal(t *testing.T) {
5151
assert.Equal(t, "v1", proposal.Version)
5252
assert.Equal(t, mcmstypes.ProposalKind("Proposal"), proposal.Kind)
5353
require.Contains(t, proposal.ChainMetadata, mcmstypes.ChainSelector(3379446385462418246))
54-
require.Len(t, proposal.Operations, 1)
54+
require.Len(t, proposal.Operations, 2)
5555
})
5656

5757
t.Run("failed decoding", func(t *testing.T) {

engine/test/internal/mcmsutils/testdata/proposal.json

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,18 @@
1313
}
1414
},
1515
"operations": [
16+
{
17+
"chainSelector": 3379446385462418246,
18+
"transaction": {
19+
"to": "0x123",
20+
"additionalFields": {
21+
"value": 0
22+
},
23+
"data": "",
24+
"tags": null,
25+
"contractType": ""
26+
}
27+
},
1628
{
1729
"chainSelector": 3379446385462418246,
1830
"transaction": {
@@ -23,7 +35,7 @@
2335
"data": "",
2436
"tags": null,
2537
"contractType": "",
26-
"contractTypeAndVersion": ""
38+
"contractVersion": "1.2.3"
2739
}
2840
}
2941
]

engine/test/internal/mcmsutils/testdata/timelock_proposal.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,14 @@
2323
"transactions": [
2424
{
2525
"to": "0x0000000000000000000000000000000000000000",
26-
"additionalFields": {"value": 0},
26+
"additionalFields": {
27+
"value": 0
28+
},
2729
"data": "ZGF0YQ==",
2830
"contractType": "",
29-
"contractTypeAndVersion": "",
3031
"tags": null
3132
}
3233
]
3334
}
3435
]
35-
}
36+
}

experimental/analyzer/decoded_call.go

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,30 +31,35 @@ func (d *DecodedCall) String(context *FieldContext) string {
3131
// resolveContractInfo looks up the contract type and version from the proposal
3232
// context's registered addresses.
3333
func resolveContractInfo(ctx ProposalContext, chainSelector uint64, mcmsTx types.Transaction) (contractType, contractVersion string) {
34+
// Use ContractVersion from transaction metadata (set by proposal creator) as fallback
35+
if mcmsTx.ContractVersion != nil {
36+
contractVersion = mcmsTx.ContractVersion.String()
37+
}
38+
3439
dpc, ok := ctx.(*DefaultProposalContext)
3540
if !ok {
36-
return mcmsTx.ContractType, ""
41+
return mcmsTx.ContractType, contractVersion
3742
}
3843

3944
addresses, ok := dpc.AddressesByChain[chainSelector]
4045
if !ok {
41-
return mcmsTx.ContractType, ""
46+
return mcmsTx.ContractType, contractVersion
4247
}
4348

4449
tv, ok := addresses[mcmsTx.To]
4550
if !ok {
46-
return mcmsTx.ContractType, ""
51+
return mcmsTx.ContractType, contractVersion
4752
}
4853

4954
ct := string(tv.Type)
5055
if ct == "" {
5156
ct = mcmsTx.ContractType
5257
}
5358

54-
var cv string
59+
// Override with the DataStore version if available
5560
if tv.Version.Original() != "" {
56-
cv = tv.Version.String()
61+
contractVersion = tv.Version.String()
5762
}
5863

59-
return ct, cv
64+
return ct, contractVersion
6065
}
Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
package analyzer
22

33
import (
4+
"encoding/json"
45
"fmt"
56

67
"github.com/smartcontractkit/mcms/sdk"
78
"github.com/smartcontractkit/mcms/sdk/ton"
89
"github.com/smartcontractkit/mcms/types"
910

10-
"github.com/smartcontractkit/chainlink-deployments-framework/deployment"
11-
1211
"github.com/smartcontractkit/chainlink-ton/pkg/bindings"
1312
)
1413

@@ -29,37 +28,43 @@ func AnalyzeTONTransactions(ctx ProposalContext, chainSelector uint64, txs []typ
2928

3029
// AnalyzeTONTransaction decodes a single TON transaction using the MCMS TON decoder.
3130
//
32-
// Unlike Aptos/Sui analyzers, this function does not unmarshal AdditionalFields because
33-
// the TON decoder only requires tx.Data (BOC cell) and tx.ContractType (metadata).
34-
// AdditionalFields in TON is only used by the encoder/timelock_converter for the Value field.
35-
//
3631
// On decode failure, this function returns a DecodedCall with the error in the Method field
3732
// instead of returning an error. This allows the proposal to continue processing even if
3833
// a single transaction fails to decode.
3934
func AnalyzeTONTransaction(ctx ProposalContext, decoder sdk.Decoder, chainSelector uint64, mcmsTx types.Transaction) (*DecodedCall, error) {
40-
contractTypeAndVersion, err := deployment.TypeAndVersionFromString(mcmsTx.ContractTypeAndVersion)
41-
if err != nil {
42-
contractType, contractVersion := resolveContractInfo(ctx, chainSelector, mcmsTx)
43-
errStr := fmt.Errorf("failed to decode TON transaction: failed to parse contract type and version: %w", err)
35+
contractType, contractVersion := resolveContractInfo(ctx, chainSelector, mcmsTx)
4436

45-
return &DecodedCall{
46-
Address: mcmsTx.To,
47-
Method: errStr.Error(),
48-
ContractType: contractType,
49-
ContractVersion: contractVersion,
50-
}, nil
51-
}
52-
decodedOp, err := decoder.Decode(mcmsTx, contractTypeAndVersion.Type.String())
53-
if err != nil {
37+
var typeErr string
38+
fullyQualifiedName := func() string {
39+
var additionalFields ton.AdditionalFields
40+
if err := json.Unmarshal(mcmsTx.AdditionalFields, &additionalFields); err != nil {
41+
typeErr = fmt.Sprintf("; additionally failed to unmarshal TON additional fields: %s", err)
42+
return ""
43+
}
44+
45+
fullyQualifiedName := string(additionalFields.ContractTypeFull)
46+
// If ContractVersion is provided, append it to the fully qualified name to ensure the decoder uses the correct version.
47+
// If it is skipped, the decoder will use the latest version available for the contract type.
48+
// Note: we don't use contractVersion from resolveContractInfo because that only represents the short type used by the datastore.
49+
if mcmsTx.ContractVersion != nil {
50+
fullyQualifiedName += "@" + mcmsTx.ContractVersion.String()
51+
}
52+
53+
return fullyQualifiedName
54+
}()
55+
56+
decodedOp, errDec := decoder.Decode(mcmsTx, fullyQualifiedName)
57+
if errDec != nil {
5458
// Don't return an error to not block the whole proposal decoding because of a single transaction decode failure.
5559
// Instead, put the error message in the Method field so it's visible in the report.
56-
errStr := fmt.Errorf("failed to decode TON transaction: %w", err)
60+
errStr := "failed to decode TON transaction: " + errDec.Error() + typeErr
5761

62+
//nolint:nilerr // We are intentionally not returning an error here to allow the proposal to be processed even if decoding fails.
5863
return &DecodedCall{
5964
Address: mcmsTx.To,
60-
Method: errStr.Error(),
61-
ContractType: contractTypeAndVersion.Type.String(),
62-
ContractVersion: contractTypeAndVersion.Version.String(),
65+
Method: errStr,
66+
ContractType: contractType,
67+
ContractVersion: contractVersion,
6368
}, nil
6469
}
6570

@@ -73,7 +78,7 @@ func AnalyzeTONTransaction(ctx ProposalContext, decoder sdk.Decoder, chainSelect
7378
Method: decodedOp.MethodName(),
7479
Inputs: namedArgs,
7580
Outputs: []NamedField{},
76-
ContractType: contractTypeAndVersion.Type.String(),
77-
ContractVersion: contractTypeAndVersion.Version.String(),
81+
ContractType: contractType,
82+
ContractVersion: contractVersion,
7883
}, nil
7984
}

0 commit comments

Comments
 (0)