feat: ContractVersion in OperationMetadata (#979)#986
Conversation
🦋 Changeset detectedLatest commit: c35fcdb The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
There was a problem hiding this comment.
Pull request overview
This PR updates MCMS operation metadata handling to use ContractVersion instead of ContractTypeAndVersion, and adjusts TON analysis to decode via TON additional fields.
Changes:
- Bumps
github.com/smartcontractkit/mcmsto a newer pseudo-version. - Replaces UPF metadata
contractTypeAndVersionwithcontractVersion. - Updates TON analyzer/tests and fixtures for
contractTypeFull-based decoding.
Reviewed changes
Copilot reviewed 12 out of 13 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
go.mod |
Updates the MCMS dependency version. |
go.sum |
Updates MCMS checksums. |
experimental/analyzer/upf/upf.go |
Emits ContractVersion in UPF metadata. |
experimental/analyzer/upf/types.go |
Replaces UPF metadata type/version field with semver contract version. |
experimental/analyzer/upf/upf_test.go |
Updates TON UPF test transaction construction and expectations. |
experimental/analyzer/ton_analyzer.go |
Uses TON additional fields to resolve the decoder contract interface. |
experimental/analyzer/ton_analyzer_test.go |
Updates TON analyzer tests for ContractVersion and contractTypeFull. |
experimental/analyzer/decoded_call.go |
Resolves contract version from transaction metadata before datastore fallback. |
experimental/analyzer/report_builder_test.go |
Supplies TON additional fields in decode-failure tests. |
engine/test/internal/mcmsutils/testdata/timelock_proposal.json |
Removes deprecated contract type/version fixture field. |
engine/test/internal/mcmsutils/testdata/proposal.json |
Removes deprecated contract type/version fixture field. |
engine/cld/commands/mcms/testdata/proposal.json |
Removes deprecated contract type/version fixture fields. |
.changeset/bold-olives-smell.md |
Adds a minor changeset for the metadata change. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if tv.Version.Original() != "" { | ||
| cv = tv.Version.String() | ||
| // If version wasn't already set from transaction metadata, use datastore version | ||
| if contractVersion == "" && tv.Version.Original() != "" { |
There was a problem hiding this comment.
I think it should be the opposite: the datastore value should have precedence over the proposal metadata.
@ecPablo @ChrisAmora , what are your thoughts?
There was a problem hiding this comment.
Yes, it's more likely that datastore has more up-to-date data. For example, I generate proposalA that uses contractVersion 1.0.0, then very shortly after, someone updates datastore to contractVersion 2.0.0. I'd expect the decoded version to show me the 2.0.0 as that's the current on chain state. The only reasons where this could not be true is if we have out-of-cld systems updating the contracts, in that case maybe the proposal might be safer source of truth. But I don't think that's the case here.
There was a problem hiding this comment.
Ok, let's keep system context (DS) to have priority. Override can be made with updating the DS directly with the proposal PR (if something is off).
Finally, we should also be able to interact with ad-hoc contracts which are not stored, and for that cases the proposal input ref (type, version) should resolve properly.
There was a problem hiding this comment.
Let's just settle how to handle this (because it affects other chains too) and then we can merge. Though I'd appreciate if you could enhance the UPF test as requested.
| bindings.ShortRBAC, | ||
| semver.MustParse("1.2.3"), | ||
| bindings.TypeRBAC, |
There was a problem hiding this comment.
nit: for TON we don't need to input both the bindings.ShortRBAC (short) and bindings.TypeRBAC (full) as the former can be derived from the latter
There was a problem hiding this comment.
Not if the full type is a trait type. Short type is only used for the contract type as stored in the DS.
| return "" | ||
| } | ||
|
|
||
| fullyQualifiedName := string(additionalFields.ContractTypeFull) |
There was a problem hiding this comment.
nit: Let's extract into a type/func which we document and allows anybody to produce a FQN
| ContractType: string(bindings.TypeRBAC), | ||
| ContractVersion: "0.0.0", | ||
| Address: s.targetAddr.String(), | ||
| Method: string(bindings.TypeRBAC) + "::GrantRole(0x95cd540f)", |
There was a problem hiding this comment.
nit: this DecodedCall should also contain a contract version, maybe as part of the Method by using a FQN vs ContractTypeFull (missing version)
There was a problem hiding this comment.
This was already done but it was not covered by test. Added a case to confirm this
| if tv.Version.Original() != "" { | ||
| cv = tv.Version.String() | ||
| // If version wasn't already set from transaction metadata, use datastore version | ||
| if contractVersion == "" && tv.Version.Original() != "" { |
There was a problem hiding this comment.
Ok, let's keep system context (DS) to have priority. Override can be made with updating the DS directly with the proposal PR (if something is off).
Finally, we should also be able to interact with ad-hoc contracts which are not stored, and for that cases the proposal input ref (type, version) should resolve properly.
| dsAddContract(t, ds, 16423721717087811551, "RmnXLft1mSEwDgMKu2okYuHkiazxntFFcZFrrcXxYg7", "RMNRemote 1.0.0") | ||
|
|
||
| // ---- TON: testnet | ||
| dsAddContract(t, ds, chainsel.TON_TESTNET.Selector, "EQADa3W6G0nSiTV4a6euRA42fU9QxSEnb-WeDpcrtWzA2jM8", "MCMS 1.0.0") |
| errStr := fmt.Errorf("failed to decode TON transaction: %w", err) | ||
| errStr := "failed to decode TON transaction: " + err.Error() | ||
| if typeErr != "" { | ||
| errStr += "; additionally" + typeErr |
| wantErrContain: "unknown contract interface: link.chain.ton.lib.access.RBAC@0.0.0", | ||
| }, | ||
| { | ||
| name: "success - RBAC GrantRol with version", |
|
|
||
| // ---- TON: testnet | ||
| dsAddContract(t, ds, chainsel.TON_TESTNET.Selector, "EQADa3W6G0nSiTV4a6euRA42fU9QxSEnb-WeDpcrtWzA2jM8", "MCMS 1.0.0") | ||
| dsAddContract(t, ds, chainsel.TON_TESTNET.Selector, "EQADa3W6G0nSiTV4a6euRA42fU9QxSEnb-WeDpcrtWzA2jM8", "Router 1.2.3") |
|
| fullyQualifiedName := string(additionalFields.ContractTypeFull) | ||
| // If ContractVersion is provided, append it to the fully qualified name to ensure the decoder uses the correct version. | ||
| // If it is skipped, the decoder will use the latest version available for the contract type. | ||
| // Note: we don't use contractVersion from resolveContractInfo because that only represents the short type used by the datastore. | ||
| if mcmsTx.ContractVersion != nil { | ||
| fullyQualifiedName += "@" + mcmsTx.ContractVersion.String() | ||
| } |
There was a problem hiding this comment.
We don't want this in TON because we only want to select a version explicitly via TX params




feat: ContractVersion in OperationMetadata (#979)
feat: ContractFullyQualifiedName in TON's AdditionalFields
fix: revert ContractTypeAndVersion in OperationMetadata (#979)