diff --git a/deployment/ccip/1_6_0/sequences/connect_chains.go b/deployment/ccip/1_6_0/sequences/connect_chains.go index 2ebf025f5..facbc7ec9 100644 --- a/deployment/ccip/1_6_0/sequences/connect_chains.go +++ b/deployment/ccip/1_6_0/sequences/connect_chains.go @@ -169,7 +169,7 @@ var ConfigureLaneLegAsSource = cldf_ops.NewSequence( plan := !sender.Equals(owner) // plan if sender is not owner _inputMCMS.Add(opston.AsCells(r.Output.Plans), plan, []types.OperationMetadata{ { - ContractType: contractType, + ContractType: string(contractType), // TODO: Is this a place where we are mixing types? Should we convert to string or map to short type? Tags: []string{}, }, }) @@ -228,7 +228,7 @@ var ConfigureLaneLegAsSource = cldf_ops.NewSequence( plan := !sender.Equals(owner) // plan if sender is not owner _inputMCMS.Add(opston.AsCells(r.Output.Plans), plan, []types.OperationMetadata{ { - ContractType: contractType, + ContractType: string(contractType), // TODO: Is this a place where we are mixing types? Should we convert to string or map to short type? Tags: []string{}, }, }) @@ -256,7 +256,7 @@ var ConfigureLaneLegAsSource = cldf_ops.NewSequence( plan := !sender.Equals(owner) // plan if sender is not owner _inputMCMS.Add(r.Output, plan, []types.OperationMetadata{ { - ContractType: contractType, + ContractType: string(contractType), // TODO: Is this a place where we are mixing types? Should we convert to string or map to short type? Tags: []string{}, }, }) @@ -286,7 +286,7 @@ var ConfigureLaneLegAsSource = cldf_ops.NewSequence( plan := !sender.Equals(owner) // plan if sender is not owner _inputMCMS.Add(r.Output, plan, []types.OperationMetadata{ { - ContractType: contractType, + ContractType: string(contractType), // TODO: Is this a place where we are mixing types? Should we convert to string or map to short type? Tags: []string{}, }, }) @@ -344,7 +344,7 @@ var ConfigureLaneLegAsDest = cldf_ops.NewSequence( plan := !sender.Equals(owner) // plan if sender is not owner _inputMCMS.Add(r.Output, plan, []types.OperationMetadata{ { - ContractType: contractType, + ContractType: string(contractType), // TODO: Is this a place where we are mixing types? Should we convert to string or map to short type? Tags: []string{}, }, }) @@ -371,7 +371,7 @@ var ConfigureLaneLegAsDest = cldf_ops.NewSequence( plan := !sender.Equals(owner) // plan if sender is not owner _inputMCMS.Add(r.Output, plan, []types.OperationMetadata{ { - ContractType: contractType, + ContractType: string(contractType), // TODO: Is this a place where we are mixing types? Should we convert to string or map to short type? Tags: []string{}, }, }) diff --git a/deployment/ccip/1_6_0/sequences/fastcurse.go b/deployment/ccip/1_6_0/sequences/fastcurse.go index c35c2c489..411d3ca68 100644 --- a/deployment/ccip/1_6_0/sequences/fastcurse.go +++ b/deployment/ccip/1_6_0/sequences/fastcurse.go @@ -309,7 +309,10 @@ func (a *TonCurseAdapter) Curse() *cldf_ops.Sequence[api.CurseInput, sequences.O out := sequences.OnChainOutput{} meta := []types.OperationMetadata{ - {ContractType: contractType, Tags: []string{}}, // TODO: add appropriate tags + { + ContractType: string(contractType), // TODO: Is this a place where we are mixing types? Should we convert to string or map to short type? + Tags: []string{}, + }, // TODO: add appropriate tags } return mcms.WithOperationOutput(out, r.Output, types.ChainSelector(in.ChainSelector), meta) @@ -401,7 +404,10 @@ func (a *TonCurseAdapter) Uncurse() *cldf_ops.Sequence[api.CurseInput, sequences out := sequences.OnChainOutput{} meta := []types.OperationMetadata{ - {ContractType: contractType, Tags: []string{}}, // TODO: add appropriate tags + { + ContractType: string(contractType), // TODO: Is this a place where we are mixing types? Should we convert to string or map to short type? + Tags: []string{}, + }, // TODO: add appropriate tags } return mcms.WithOperationOutput(out, r.Output, types.ChainSelector(in.ChainSelector), meta) diff --git a/deployment/ccip/1_6_0/sequences/transfer_ownership.go b/deployment/ccip/1_6_0/sequences/transfer_ownership.go index ada01b509..8f3c9ab8f 100644 --- a/deployment/ccip/1_6_0/sequences/transfer_ownership.go +++ b/deployment/ccip/1_6_0/sequences/transfer_ownership.go @@ -139,7 +139,7 @@ func (a *TonTransferOwnershipAdapter) SequenceTransferOwnershipViaMCMS() *cldfop plan := !deployerAddr.Equals(currentOwner) _inputMCMS.Add(opston.AsCells(r.Output.Plans), plan, []types.OperationMetadata{ { - ContractType: contractType, + ContractType: string(contractType), // TODO: Is this a place where we are mixing types? Should we convert to string or map to short type? Tags: []string{}, }, }) @@ -218,7 +218,7 @@ func (a *TonTransferOwnershipAdapter) SequenceAcceptOwnership() *cldfops.Sequenc plan := !sender.Equals(proposedOwner) _inputMCMS.Add(opston.AsCells(r.Output.Plans), plan, []types.OperationMetadata{ { - ContractType: contractType, + ContractType: string(contractType), // TODO: Is this a place where we are mixing types? Should we convert to string or map to short type? Tags: []string{}, }, }) diff --git a/deployment/ccip/sequence/deploy_ccip.go b/deployment/ccip/sequence/deploy_ccip.go index a43f17968..8e4ceced1 100644 --- a/deployment/ccip/sequence/deploy_ccip.go +++ b/deployment/ccip/sequence/deploy_ccip.go @@ -68,7 +68,7 @@ func deployCCIPSequence(b operations.Bundle, dp *dep.DependencyProvider, in Depl // Fetch the contract code using the Fully Qualified Name of the contracts instead of the types used in the datastore retrieveContractsOpts := utils.RetrieveCompiledContractsOpts{ Package: contractsPackage, - Contracts: []string{ + Contracts: []tvm.FullyQualifiedType{ bindings.TypeRouter, bindings.TypeFeeQuoter, bindings.TypeOffRamp, diff --git a/deployment/ccip/sequence/update_lanes.go b/deployment/ccip/sequence/update_lanes.go index f093a1ae6..86cd1f334 100644 --- a/deployment/ccip/sequence/update_lanes.go +++ b/deployment/ccip/sequence/update_lanes.go @@ -101,7 +101,7 @@ func updateLanes(b cldf_ops.Bundle, dp *dep.DependencyProvider, in UpdateTonLane plan := !sender.Equals(owner) // plan if sender is not owner _inputMCMS.Add(opston.AsCells(r.Output.Plans), plan, []types.OperationMetadata{ { - ContractType: contractType, + ContractType: string(contractType), // TODO: Is this a place where we are mixing types? Should we convert to string or map to short type? Tags: []string{}, }, }) @@ -152,7 +152,7 @@ func updateLanes(b cldf_ops.Bundle, dp *dep.DependencyProvider, in UpdateTonLane plan := !sender.Equals(owner) // plan if sender is not owner _inputMCMS.Add(opston.AsCells(r.Output.Plans), plan, []types.OperationMetadata{ { - ContractType: contractType, + ContractType: string(contractType), // TODO: Is this a place where we are mixing types? Should we convert to string or map to short type? Tags: []string{}, }, }) @@ -179,7 +179,7 @@ func updateLanes(b cldf_ops.Bundle, dp *dep.DependencyProvider, in UpdateTonLane plan := !sender.Equals(owner) // plan if sender is not owner _inputMCMS.Add(r.Output, plan, []types.OperationMetadata{ { - ContractType: contractType, + ContractType: string(contractType), // TODO: Is this a place where we are mixing types? Should we convert to string or map to short type? Tags: []string{}, }, }) @@ -207,7 +207,7 @@ func updateLanes(b cldf_ops.Bundle, dp *dep.DependencyProvider, in UpdateTonLane plan := !sender.Equals(owner) // plan if sender is not owner _inputMCMS.Add(r.Output, plan, []types.OperationMetadata{ { - ContractType: contractType, + ContractType: string(contractType), // TODO: Is this a place where we are mixing types? Should we convert to string or map to short type? Tags: []string{}, }, }) @@ -233,7 +233,7 @@ func updateLanes(b cldf_ops.Bundle, dp *dep.DependencyProvider, in UpdateTonLane plan := !sender.Equals(owner) // plan if sender is not owner _inputMCMS.Add(r.Output, plan, []types.OperationMetadata{ { - ContractType: contractType, + ContractType: string(contractType), // TODO: Is this a place where we are mixing types? Should we convert to string or map to short type? Tags: []string{}, }, }) diff --git a/deployment/pkg/ops/mcms/deploy.go b/deployment/pkg/ops/mcms/deploy.go index 7af4cec6b..cca429adc 100644 --- a/deployment/pkg/ops/mcms/deploy.go +++ b/deployment/pkg/ops/mcms/deploy.go @@ -113,7 +113,7 @@ func deployMCMSSequence(b cldfops.Bundle, dp *dep.DependencyProvider, in DeployM retrieveContractsInput := utils.RetrieveCompiledContractsOpts{ Package: contractsPackage, - Contracts: []string{ + Contracts: []tvm.FullyQualifiedType{ bindings.TypeTimelock, bindings.TypeMCMS, }, diff --git a/deployment/pkg/ops/ton/send_test.go b/deployment/pkg/ops/ton/send_test.go index 4e1567a19..252fe0c28 100644 --- a/deployment/pkg/ops/ton/send_test.go +++ b/deployment/pkg/ops/ton/send_test.go @@ -210,7 +210,7 @@ func messageEnvelopeRoundTrip(t *testing.T, seed int64, iterations int, writeArt } } -func testMakeExecuteOp(t *testing.T, contract string, opcode uint64, decoded *codec.MessageEnvelope[any]) operations.Report[ton.SendMessagesInput, ton.SendMessagesOutput] { +func testMakeExecuteOp(t *testing.T, contract tvm.FullyQualifiedType, opcode uint64, decoded *codec.MessageEnvelope[any]) operations.Report[ton.SendMessagesInput, ton.SendMessagesOutput] { t.Helper() // Setup execution environment @@ -243,7 +243,7 @@ func testMakeExecuteOp(t *testing.T, contract string, opcode uint64, decoded *co return r } -func testMakeExecuteSeq(t *testing.T, contract string, envelopes []*codec.MessageEnvelope[any]) { +func testMakeExecuteSeq(t *testing.T, contract tvm.FullyQualifiedType, envelopes []*codec.MessageEnvelope[any]) { t.Helper() n := len(envelopes) diff --git a/deployment/pkg/ops/ton/types.go b/deployment/pkg/ops/ton/types.go index 2228278de..e0f8b6135 100644 --- a/deployment/pkg/ops/ton/types.go +++ b/deployment/pkg/ops/ton/types.go @@ -12,6 +12,7 @@ import ( "github.com/smartcontractkit/chainlink-ton/pkg/bindings" "github.com/smartcontractkit/chainlink-ton/pkg/ton/codec" "github.com/smartcontractkit/chainlink-ton/pkg/ton/tlbe" + "github.com/smartcontractkit/chainlink-ton/pkg/ton/tvm" ) // PlannerOption is an interface an op IN type providing @@ -122,8 +123,8 @@ type ContractCodeProvider interface { // Describes a contract by its package and identifier, used to retrieve compiled code from a ContractCodeProvider. type ContractMetadata struct { - Package string `json:"package"` // Name of the package where the contract is defined (e.g., "github.com/smartcontractkit/chainlink-ton@contracts/v1.6.3") - ID string `json:"id"` // Contract identifier within the package (indexed by fully qualified name e.g., "link.chain.ton.mcms.RBACTimelock") + Package string `json:"package"` // Name of the package where the contract is defined (e.g., "github.com/smartcontractkit/chainlink-ton@contracts/v1.6.3") + ID tvm.FullyQualifiedType `json:"id"` // Contract identifier within the package } func (m ContractMetadata) Key() string { diff --git a/deployment/state/state.go b/deployment/state/state.go index f49beb301..cc174bd39 100644 --- a/deployment/state/state.go +++ b/deployment/state/state.go @@ -7,16 +7,18 @@ import ( "github.com/Masterminds/semver/v3" "github.com/rs/zerolog/log" - ds "github.com/smartcontractkit/chainlink-deployments-framework/datastore" - cldf "github.com/smartcontractkit/chainlink-deployments-framework/deployment" "github.com/xssnick/tonutils-go/address" "golang.org/x/sync/errgroup" + ds "github.com/smartcontractkit/chainlink-deployments-framework/datastore" + cldf "github.com/smartcontractkit/chainlink-deployments-framework/deployment" + "github.com/smartcontractkit/chainlink-ton/deployment/view/feequoter" "github.com/smartcontractkit/chainlink-ton/deployment/view/offramp" "github.com/smartcontractkit/chainlink-ton/deployment/view/onramp" "github.com/smartcontractkit/chainlink-ton/deployment/view/router" "github.com/smartcontractkit/chainlink-ton/pkg/bindings" + "github.com/smartcontractkit/chainlink-ton/pkg/ton/tvm" ) // Duplicates of chainlink/deployment/ccip/ to avoid import loops @@ -47,7 +49,7 @@ var ( // TODO: Currently we cannot unify to use only the long format from pkg/bindings/index.go everywhere, // Is there a cleaner way to manage these two types together? -var LongToShortContractType = map[string]ds.ContractType{ +var LongToShortContractType = map[tvm.FullyQualifiedType]ds.ContractType{ bindings.TypeRouter: Router, bindings.TypeFeeQuoter: FeeQuoter, bindings.TypeOnRamp: OnRamp, diff --git a/deployment/utils/compiled_contracts.go b/deployment/utils/compiled_contracts.go index 887ea1156..4d0e404a2 100644 --- a/deployment/utils/compiled_contracts.go +++ b/deployment/utils/compiled_contracts.go @@ -19,6 +19,7 @@ import ( "github.com/smartcontractkit/chainlink-ton/deployment/ccip/helpers" "github.com/smartcontractkit/chainlink-ton/deployment/pkg/ops/ton" "github.com/smartcontractkit/chainlink-ton/pkg/bindings" + "github.com/smartcontractkit/chainlink-ton/pkg/ton/tvm" "github.com/smartcontractkit/chainlink-ton/pkg/ton/wrappers" ) @@ -47,8 +48,8 @@ type ContractEntryMetadata struct { // ContractPackageMetadata is the schema for contracts-pkg.json bundled in each release. type ContractPackageMetadata struct { - Version string `json:"version"` - Contracts map[string]ContractEntryMetadata `json:"contracts"` + Version string `json:"version"` + Contracts map[tvm.FullyQualifiedType]ContractEntryMetadata `json:"contracts"` } // defaultPackageMetadata is used as a fallback for releases prior to the introduction of @@ -56,7 +57,7 @@ type ContractPackageMetadata struct { // original filenames. var defaultPackageMetadata = &ContractPackageMetadata{ Version: "1.6.0", - Contracts: map[string]ContractEntryMetadata{ + Contracts: map[tvm.FullyQualifiedType]ContractEntryMetadata{ bindings.TypeRouter: {Path: "Router.compiled.json", Version: "1.6.0"}, bindings.TypeFeeQuoter: {Path: "FeeQuoter.compiled.json", Version: "1.6.0"}, bindings.TypeOnRamp: {Path: "OnRamp.compiled.json", Version: "1.6.0"}, @@ -77,15 +78,15 @@ var defaultPackageMetadata = &ContractPackageMetadata{ // - local (maps to {repo-root}/contracts/build) type RetrieveCompiledContractsOpts struct { Package string - Contracts []string // Fully qualified contract names from pkg/bindings/index.go (e.g. bindings.TypeRouter) - PkgsDir string // optional base directory for the local package cache (passed through to DownloadArtifacts) + Contracts []tvm.FullyQualifiedType // Fully qualified contract names from pkg/bindings/index.go (e.g. bindings.TypeRouter) + PkgsDir string // optional base directory for the local package cache (passed through to DownloadArtifacts) } // RetrieveCompiledTONContracts resolves the package path, reads the package metadata, // and loads each requested contract individually from disk. -// returns map[string]ton.CompiledContract, keyed by Fully Qualified Name (e.g. bindings.TypeRouter = link.chain.ton.ccip.Router) +// returns map[tvm.FullyQualifiedType]ton.CompiledContract, keyed by Fully Qualified Name (e.g. bindings.TypeRouter = link.chain.ton.ccip.Router) -func RetrieveCompiledTONContracts(ctx context.Context, log logger.Logger, in *RetrieveCompiledContractsOpts) (map[string]ton.CompiledContract, error) { +func RetrieveCompiledTONContracts(ctx context.Context, log logger.Logger, in *RetrieveCompiledContractsOpts) (map[tvm.FullyQualifiedType]ton.CompiledContract, error) { if in == nil { return nil, errors.New("input options cannot be nil") } @@ -107,13 +108,13 @@ func RetrieveCompiledTONContracts(ctx context.Context, log logger.Logger, in *Re contractNames := in.Contracts if len(contractNames) == 0 { // No filter: collect all contract names from metadata. - contractNames = make([]string, 0, len(meta.Contracts)) + contractNames = make([]tvm.FullyQualifiedType, 0, len(meta.Contracts)) for contractName := range meta.Contracts { contractNames = append(contractNames, contractName) } } - compiledContracts := make(map[string]ton.CompiledContract, len(contractNames)) + compiledContracts := make(map[tvm.FullyQualifiedType]ton.CompiledContract, len(contractNames)) for _, contractName := range contractNames { contract, err := ReadCompiledContract(ton.ContractMetadata{Package: in.Package, ID: contractName}, pkgPath, meta) if err != nil { diff --git a/deployment/utils/compiled_contracts_test.go b/deployment/utils/compiled_contracts_test.go index 3eb6ceaf7..8e91e79af 100644 --- a/deployment/utils/compiled_contracts_test.go +++ b/deployment/utils/compiled_contracts_test.go @@ -18,6 +18,7 @@ import ( "github.com/smartcontractkit/chainlink-ton/deployment/pkg/ops/ton" "github.com/smartcontractkit/chainlink-ton/pkg/bindings" + "github.com/smartcontractkit/chainlink-ton/pkg/ton/tvm" ) // Sample compiled contract JSON (minimal valid Tolk compiled contract) @@ -375,9 +376,10 @@ func TestReadPackageMetadata_WithValidFile(t *testing.T) { result, err := LoadPackageMetadata(dir) require.NoError(t, err) assert.Equal(t, "1.6.3", result.Version) - require.Contains(t, result.Contracts, "link.chain.ton.ccip.Router") - assert.Equal(t, "Router.compiled.json", result.Contracts["link.chain.ton.ccip.Router"].Path) - assert.Equal(t, "1.6.3", result.Contracts["link.chain.ton.ccip.Router"].Version) + const rtType = tvm.FullyQualifiedType("link.chain.ton.ccip.Router") + require.Contains(t, result.Contracts, rtType) + assert.Equal(t, "Router.compiled.json", result.Contracts[rtType].Path) + assert.Equal(t, "1.6.3", result.Contracts[rtType].Version) } func TestReadPackageMetadata_MissingFileUsesFallback(t *testing.T) { @@ -417,7 +419,7 @@ func writePkgDir(t *testing.T, pkgMeta *ContractPackageMetadata) string { func testReceiverMeta(version string) *ContractPackageMetadata { return &ContractPackageMetadata{ Version: version, - Contracts: map[string]ContractEntryMetadata{ + Contracts: map[tvm.FullyQualifiedType]ContractEntryMetadata{ bindings.TypeTestReceiver: {Path: "ccip.test.receiver.compiled.json", Version: version}, }, } @@ -471,7 +473,7 @@ func TestReadCompiledContract_InvalidVersionInMetadata(t *testing.T) { dir := t.TempDir() meta := &ContractPackageMetadata{ Version: "1.6.3", - Contracts: map[string]ContractEntryMetadata{ + Contracts: map[tvm.FullyQualifiedType]ContractEntryMetadata{ bindings.TypeTestReceiver: {Path: "ccip.test.receiver.compiled.json", Version: "not-a-version"}, }, } diff --git a/deployment/utils/operation/deploy_ton_contract.go b/deployment/utils/operation/deploy_ton_contract.go index c75417871..66b1cb688 100644 --- a/deployment/utils/operation/deploy_ton_contract.go +++ b/deployment/utils/operation/deploy_ton_contract.go @@ -27,7 +27,7 @@ import ( ) type DeployContractInput struct { - Name string + Name tvm.FullyQualifiedType Storage any MessageBody any ContractCode *cell.Cell @@ -46,7 +46,7 @@ var DeployTONContractOp = cldfops.NewOperation( ) func (i *DeployContractInput) Validate() error { - if strings.TrimSpace(i.Name) == "" { + if strings.TrimSpace(string(i.Name)) == "" { return errors.New("name field is required") } if i.ContractCode == nil { diff --git a/integration-tests/deployment/mcms/cs_deployer_test.go b/integration-tests/deployment/mcms/cs_deployer_test.go index 22e66b687..b2bd8d73d 100644 --- a/integration-tests/deployment/mcms/cs_deployer_test.go +++ b/integration-tests/deployment/mcms/cs_deployer_test.go @@ -10,9 +10,10 @@ import ( "github.com/xssnick/tonutils-go/tlb" chainsel "github.com/smartcontractkit/chain-selectors" - "github.com/smartcontractkit/chainlink-common/pkg/logger" mcmstypes "github.com/smartcontractkit/mcms/types" + "github.com/smartcontractkit/chainlink-common/pkg/logger" + cldfchain "github.com/smartcontractkit/chainlink-deployments-framework/chain" cldfops "github.com/smartcontractkit/chainlink-deployments-framework/operations" @@ -199,7 +200,7 @@ func TestDeployMCMSWithDeployerAPI(t *testing.T) { mcmsAddr := suiteState.Proposer tv, err := tvm.CallGetter(ctx, chain.Client, mc, mcmsAddr, toncommon.GetTypeAndVersion) require.NoError(t, err) - require.Equal(t, bindings.TypeMCMS, tv.Type, "MCMS contract type should match") + require.EqualValues(t, bindings.TypeMCMS, tv.Type, "MCMS contract type should match") t.Log("Verified MCMS contract type and version") config, err := tvm.CallGetterLatest(ctx, chain.Client, mcmsAddr, mcms.GetConfig) @@ -227,7 +228,7 @@ func TestDeployMCMSWithDeployerAPI(t *testing.T) { mcmsAddr = suiteState.Proposer tv, err = tvm.CallGetter(ctx, chain.Client, mc, mcmsAddr, toncommon.GetTypeAndVersion) require.NoError(t, err) - require.Equal(t, bindings.TypeMCMS, tv.Type, "MCMS contract type should match") + require.EqualValues(t, bindings.TypeMCMS, tv.Type, "MCMS contract type should match") t.Log("Verified MCMS contract type and version") timelockAddr = suiteState.Timelock diff --git a/pkg/bindings/getters.go b/pkg/bindings/getters.go index 33b1de9ed..4127d3d0f 100644 --- a/pkg/bindings/getters.go +++ b/pkg/bindings/getters.go @@ -9,6 +9,7 @@ import ( "github.com/smartcontractkit/chainlink-ton/pkg/ccip/bindings/onramp" "github.com/smartcontractkit/chainlink-ton/pkg/ccip/bindings/ownable2step" "github.com/smartcontractkit/chainlink-ton/pkg/ccip/bindings/router" + "github.com/smartcontractkit/chainlink-ton/pkg/ton/tvm" ) // GetterMap is a map of getter method names to their Getter definitions. @@ -19,7 +20,7 @@ type GetterMap map[string]interface{} // The outer map keys are contract type identifiers (e.g., "link.chain.ton.ccip.Router"). // The inner map keys are getter method names (e.g., "owner", "onRamp"). // The values are Getter[A, R] instances representing typed getter methods. -var TypeToGetterMap = map[string]GetterMap{ +var TypeToGetterMap = map[tvm.FullyQualifiedType]GetterMap{ // CCIP contract types TypeRouter: { "owner": router.GetOwner, diff --git a/pkg/bindings/index.go b/pkg/bindings/index.go index f691b42ad..9ff016021 100644 --- a/pkg/bindings/index.go +++ b/pkg/bindings/index.go @@ -18,48 +18,47 @@ import ( "github.com/smartcontractkit/chainlink-ton/pkg/ccip/bindings/router" ) -// TODO we should use a type alias for these, sometihng like ContractTypeLong const ( - PkgLib = "link.chain.ton.lib" - PkgCCIP = "link.chain.ton.ccip" - PkgMCMS = "link.chain.ton.mcms" + PkgLib tvm.FullyQualifiedType = "link.chain.ton.lib" + PkgCCIP tvm.FullyQualifiedType = "link.chain.ton.ccip" + PkgMCMS tvm.FullyQualifiedType = "link.chain.ton.mcms" // Third-party contract types - PkgJetton = "com.github.ton-blockchain.jetton-contract" + PkgJetton tvm.FullyQualifiedType = "com.github.ton-blockchain.jetton-contract" // TODO: move these constants to their respective packages // Contract types // Libs and traits - TypeOwnable = PkgLib + ".access.Ownable" - TypeRBAC = PkgLib + ".access.RBAC" - TypeWithdrawable = PkgLib + ".funding.Withdrawable" - TypeUpgradeable = PkgLib + ".versioning.Upgradeable" + TypeOwnable tvm.FullyQualifiedType = PkgLib + ".access.Ownable" + TypeRBAC tvm.FullyQualifiedType = PkgLib + ".access.RBAC" + TypeWithdrawable tvm.FullyQualifiedType = PkgLib + ".funding.Withdrawable" + TypeUpgradeable tvm.FullyQualifiedType = PkgLib + ".versioning.Upgradeable" // MCMS - TypeMCMS = PkgMCMS + ".MCMS" - TypeTimelock = PkgMCMS + ".Timelock" + TypeMCMS tvm.FullyQualifiedType = PkgMCMS + ".MCMS" + TypeTimelock tvm.FullyQualifiedType = PkgMCMS + ".Timelock" // CCIP - TypeRouter = PkgCCIP + ".Router" - TypeOnRamp = PkgCCIP + ".OnRamp" - TypeOffRamp = PkgCCIP + ".OffRamp" - TypeFeeQuoter = PkgCCIP + ".FeeQuoter" - TypeSendExecutor = PkgCCIP + ".CCIPSendExecutor" - TypeDeployable = PkgCCIP + ".Deployable" - TypeMerkleRoot = PkgCCIP + ".MerkleRoot" - TypeReceiveExecutor = PkgCCIP + ".ReceiveExecutor" - TypeTestReceiver = PkgCCIP + ".test.Receiver" + TypeRouter tvm.FullyQualifiedType = PkgCCIP + ".Router" + TypeOnRamp tvm.FullyQualifiedType = PkgCCIP + ".OnRamp" + TypeOffRamp tvm.FullyQualifiedType = PkgCCIP + ".OffRamp" + TypeFeeQuoter tvm.FullyQualifiedType = PkgCCIP + ".FeeQuoter" + TypeSendExecutor tvm.FullyQualifiedType = PkgCCIP + ".CCIPSendExecutor" + TypeDeployable tvm.FullyQualifiedType = PkgCCIP + ".Deployable" + TypeMerkleRoot tvm.FullyQualifiedType = PkgCCIP + ".MerkleRoot" + TypeReceiveExecutor tvm.FullyQualifiedType = PkgCCIP + ".ReceiveExecutor" + TypeTestReceiver tvm.FullyQualifiedType = PkgCCIP + ".test.Receiver" // Jetton - TypeJettonWallet = PkgJetton + ".contracts.jetton-wallet" - TypeJettonMinter = PkgJetton + ".contracts.jetton-minter" + TypeJettonWallet tvm.FullyQualifiedType = PkgJetton + ".contracts.jetton-wallet" + TypeJettonMinter tvm.FullyQualifiedType = PkgJetton + ".contracts.jetton-minter" ) // AllContractTypes lists every fully qualified name for contracts present in the bindings var AllContractTypes = []struct { SimpleName string - ContractType string + ContractType tvm.FullyQualifiedType }{ {"Router", TypeRouter}, {"FeeQuoter", TypeFeeQuoter}, diff --git a/pkg/ton/codec/debug/decoders/ccip/ccipcommon/common.go b/pkg/ton/codec/debug/decoders/ccip/ccipcommon/common.go index 2de371b6b..9f9ae1dd5 100644 --- a/pkg/ton/codec/debug/decoders/ccip/ccipcommon/common.go +++ b/pkg/ton/codec/debug/decoders/ccip/ccipcommon/common.go @@ -19,7 +19,7 @@ func NewDecoder(tlbsCtx tvm.TLBMap) lib.ContractDecoder { } // ContractType implements lib.ContractDecoder. -func (d *decoder) ContractType() string { +func (d *decoder) ContractType() tvm.FullyQualifiedType { return bindings.PkgCCIP } diff --git a/pkg/ton/codec/debug/decoders/ccip/ccipsendexecutor/ccipsendexecutor.go b/pkg/ton/codec/debug/decoders/ccip/ccipsendexecutor/ccipsendexecutor.go index 1c17908b4..576c3d161 100644 --- a/pkg/ton/codec/debug/decoders/ccip/ccipsendexecutor/ccipsendexecutor.go +++ b/pkg/ton/codec/debug/decoders/ccip/ccipsendexecutor/ccipsendexecutor.go @@ -22,7 +22,7 @@ func NewDecoder(tlbsCtx tvm.TLBMap) lib.ContractDecoder { return &decoder{tlbsCtx} } -func (d *decoder) ContractType() string { +func (d *decoder) ContractType() tvm.FullyQualifiedType { return bindings.TypeSendExecutor } diff --git a/pkg/ton/codec/debug/decoders/ccip/feequoter/feequoter.go b/pkg/ton/codec/debug/decoders/ccip/feequoter/feequoter.go index 454b861f5..ba2be991a 100644 --- a/pkg/ton/codec/debug/decoders/ccip/feequoter/feequoter.go +++ b/pkg/ton/codec/debug/decoders/ccip/feequoter/feequoter.go @@ -22,7 +22,7 @@ func NewDecoder(tlbsCtx tvm.TLBMap) lib.ContractDecoder { return &decoder{tlbsCtx} } -func (d *decoder) ContractType() string { +func (d *decoder) ContractType() tvm.FullyQualifiedType { return bindings.TypeFeeQuoter } diff --git a/pkg/ton/codec/debug/decoders/ccip/offramp/decoder.go b/pkg/ton/codec/debug/decoders/ccip/offramp/decoder.go index ea4429e99..f0f5d65cb 100644 --- a/pkg/ton/codec/debug/decoders/ccip/offramp/decoder.go +++ b/pkg/ton/codec/debug/decoders/ccip/offramp/decoder.go @@ -21,7 +21,7 @@ func NewDecoder(tlbsCtx tvm.TLBMap) lib.ContractDecoder { return &decoder{tlbsCtx} } -func (d *decoder) ContractType() string { +func (d *decoder) ContractType() tvm.FullyQualifiedType { return bindings.TypeOffRamp } diff --git a/pkg/ton/codec/debug/decoders/ccip/onramp/onramp.go b/pkg/ton/codec/debug/decoders/ccip/onramp/onramp.go index 6ba256ff8..83dff37f1 100644 --- a/pkg/ton/codec/debug/decoders/ccip/onramp/onramp.go +++ b/pkg/ton/codec/debug/decoders/ccip/onramp/onramp.go @@ -25,7 +25,7 @@ func NewDecoder(tlbsCtx tvm.TLBMap) lib.ContractDecoder { return &decoder{tlbsCtx} } -func (d *decoder) ContractType() string { +func (d *decoder) ContractType() tvm.FullyQualifiedType { return bindings.TypeOnRamp } diff --git a/pkg/ton/codec/debug/decoders/ccip/router/router.go b/pkg/ton/codec/debug/decoders/ccip/router/router.go index c12b19819..d96b3d16e 100644 --- a/pkg/ton/codec/debug/decoders/ccip/router/router.go +++ b/pkg/ton/codec/debug/decoders/ccip/router/router.go @@ -22,7 +22,7 @@ func NewDecoder(tlbsCtx tvm.TLBMap) lib.ContractDecoder { return &decoder{tlbsCtx} } -func (d *decoder) ContractType() string { +func (d *decoder) ContractType() tvm.FullyQualifiedType { return bindings.TypeRouter } diff --git a/pkg/ton/codec/debug/decoders/jetton/common.go b/pkg/ton/codec/debug/decoders/jetton/common.go index b4a98222f..8d6988a75 100644 --- a/pkg/ton/codec/debug/decoders/jetton/common.go +++ b/pkg/ton/codec/debug/decoders/jetton/common.go @@ -15,15 +15,15 @@ var TLBs = jetton.TLBs type decoder struct { tlbsCtx tvm.TLBMap - contractType string + contractType tvm.FullyQualifiedType } -func NewDecoder(tlbsCtx tvm.TLBMap, t string) lib.ContractDecoder { +func NewDecoder(tlbsCtx tvm.TLBMap, t tvm.FullyQualifiedType) lib.ContractDecoder { return &decoder{tlbsCtx: tlbsCtx, contractType: t} } // ContractType implements lib.ContractDecoder. -func (d *decoder) ContractType() string { +func (d *decoder) ContractType() tvm.FullyQualifiedType { return d.contractType } diff --git a/pkg/ton/codec/debug/decoders/jetton/minter/jetton_minter.go b/pkg/ton/codec/debug/decoders/jetton/minter/jetton_minter.go index 92eed930d..3d525a948 100644 --- a/pkg/ton/codec/debug/decoders/jetton/minter/jetton_minter.go +++ b/pkg/ton/codec/debug/decoders/jetton/minter/jetton_minter.go @@ -23,7 +23,7 @@ func NewDecoder(tlbsCtx tvm.TLBMap) lib.ContractDecoder { } // ContractType implements lib.ContractDecoder. -func (d *decoder) ContractType() string { +func (d *decoder) ContractType() tvm.FullyQualifiedType { return "com.github.ton-blockchain.jetton-contract.contracts.jetton-minter" } diff --git a/pkg/ton/codec/debug/decoders/jetton/wallet/jetton_wallet.go b/pkg/ton/codec/debug/decoders/jetton/wallet/jetton_wallet.go index 8a8ced5dd..1b81cadc0 100644 --- a/pkg/ton/codec/debug/decoders/jetton/wallet/jetton_wallet.go +++ b/pkg/ton/codec/debug/decoders/jetton/wallet/jetton_wallet.go @@ -23,7 +23,7 @@ func NewDecoder(tlbsCtx tvm.TLBMap) lib.ContractDecoder { } // ContractType implements lib.ContractDecoder. -func (d *decoder) ContractType() string { +func (d *decoder) ContractType() tvm.FullyQualifiedType { return "com.github.ton-blockchain.jetton-contract.contracts.jetton-wallet" } diff --git a/pkg/ton/codec/debug/decoders/lib/access/rbac/decoder.go b/pkg/ton/codec/debug/decoders/lib/access/rbac/decoder.go index 45f995b61..cc36697a5 100644 --- a/pkg/ton/codec/debug/decoders/lib/access/rbac/decoder.go +++ b/pkg/ton/codec/debug/decoders/lib/access/rbac/decoder.go @@ -22,7 +22,7 @@ func NewDecoder(tlbsCtx tvm.TLBMap) lib.ContractDecoder { return &decoder{tlbsCtx} } -func (d *decoder) ContractType() string { +func (d *decoder) ContractType() tvm.FullyQualifiedType { return bindings.TypeRBAC } diff --git a/pkg/ton/codec/debug/decoders/mcms/mcms/decoder.go b/pkg/ton/codec/debug/decoders/mcms/mcms/decoder.go index 777e1d6b9..7b71539b3 100644 --- a/pkg/ton/codec/debug/decoders/mcms/mcms/decoder.go +++ b/pkg/ton/codec/debug/decoders/mcms/mcms/decoder.go @@ -22,7 +22,7 @@ func NewDecoder(tlbsCtx tvm.TLBMap) lib.ContractDecoder { return &decoder{tlbsCtx} } -func (d *decoder) ContractType() string { +func (d *decoder) ContractType() tvm.FullyQualifiedType { return bindings.TypeMCMS } diff --git a/pkg/ton/codec/debug/decoders/mcms/timelock/decoder.go b/pkg/ton/codec/debug/decoders/mcms/timelock/decoder.go index 702481366..027f69daf 100644 --- a/pkg/ton/codec/debug/decoders/mcms/timelock/decoder.go +++ b/pkg/ton/codec/debug/decoders/mcms/timelock/decoder.go @@ -22,7 +22,7 @@ func NewDecoder(tlbsCtx tvm.TLBMap) lib.ContractDecoder { return &decoder{tlbsCtx} } -func (d *decoder) ContractType() string { +func (d *decoder) ContractType() tvm.FullyQualifiedType { return bindings.TypeTimelock } diff --git a/pkg/ton/codec/debug/lib/lib.go b/pkg/ton/codec/debug/lib/lib.go index 6ea7b0c2f..e31e13af2 100644 --- a/pkg/ton/codec/debug/lib/lib.go +++ b/pkg/ton/codec/debug/lib/lib.go @@ -35,7 +35,7 @@ type BodyInfo interface { } type ContractDecoder interface { - ContractType() string + ContractType() tvm.FullyQualifiedType InternalMessageInfo(body *cell.Cell) (MessageInfo, error) ExternalMessageInfo(body *cell.Cell) (MessageInfo, error) EventInfo(dstAddr *address.Address, msg *cell.Cell) (MessageInfo, error) @@ -59,7 +59,7 @@ func NewMessageInfo(name string, msg any) (MessageInfo, error) { } // NewMessageInfoFromCell attempts to decode the given cell using the provided TL-B candidates mapped by their opcodes. -func NewMessageInfoFromCell(t string, msg *cell.Cell, tlbs tvm.TLBMap, tlbsCtx tvm.TLBMap) (MessageInfo, error) { +func NewMessageInfoFromCell(t tvm.FullyQualifiedType, msg *cell.Cell, tlbs tvm.TLBMap, tlbsCtx tvm.TLBMap) (MessageInfo, error) { typeName, norm, err := codec.DecodeTLBValToJSON(msg, tlbs) if err != nil { return nil, fmt.Errorf("failed to decode message for contract %s: %w", t, err) diff --git a/pkg/ton/codec/debug/pretty_print.go b/pkg/ton/codec/debug/pretty_print.go index e2f864b56..c3e7566b1 100644 --- a/pkg/ton/codec/debug/pretty_print.go +++ b/pkg/ton/codec/debug/pretty_print.go @@ -37,7 +37,7 @@ type TypeAndVersion struct { type DebuggerEnvironment struct { existingAddresses map[string]TypeAndVersion - contracts map[string]lib.ContractDecoder + contracts map[tvm.FullyQualifiedType]lib.ContractDecoder writerFactory func(DebuggerEnvironment) lib.DebuggerVisualization } @@ -69,7 +69,7 @@ func NewDebuggerSequenceTrace(addresses map[string]TypeAndVersion, outputFmt seq } } -func defaultDecoders() map[string]lib.ContractDecoder { +func defaultDecoders() map[tvm.FullyQualifiedType]lib.ContractDecoder { tlbs := make(tvm.TLBMap) // Jetton contract types maps.Copy(tlbs, wallet.TLBs) @@ -84,7 +84,7 @@ func defaultDecoders() map[string]lib.ContractDecoder { maps.Copy(tlbs, mcms.TLBs) maps.Copy(tlbs, timelock.TLBs) - t := make(map[string]lib.ContractDecoder) + t := make(map[tvm.FullyQualifiedType]lib.ContractDecoder) registerDecoder(t, wallet.NewDecoder(tlbs)) registerDecoder(t, minter.NewDecoder(tlbs)) registerDecoder(t, router.NewDecoder(tlbs)) @@ -98,7 +98,7 @@ func defaultDecoders() map[string]lib.ContractDecoder { return t } -func registerDecoder(t map[string]lib.ContractDecoder, decoder lib.ContractDecoder) { +func registerDecoder(t map[tvm.FullyQualifiedType]lib.ContractDecoder, decoder lib.ContractDecoder) { t[decoder.ContractType()] = decoder } diff --git a/pkg/ton/codec/envelope.go b/pkg/ton/codec/envelope.go index 06e78b404..bab4e506f 100644 --- a/pkg/ton/codec/envelope.go +++ b/pkg/ton/codec/envelope.go @@ -18,7 +18,7 @@ import ( // MessageMeta keeps the information required to serialize/deserialize TL-B messages. type MessageMeta struct { - Contract string + Contract tvm.FullyQualifiedType Opcode uint64 // Go runtime type information @@ -26,7 +26,7 @@ type MessageMeta struct { GoType reflect.Type } -func NewMessageMetaFromValue(contract string, v any) (MessageMeta, error) { +func NewMessageMetaFromValue(contract tvm.FullyQualifiedType, v any) (MessageMeta, error) { v, err := EnsureTLBStructPointer(v) if err != nil { return MessageMeta{}, fmt.Errorf("failed to ensure TL-B struct pointer: %w", err) @@ -72,7 +72,7 @@ func EnsureTLBStructPointer(value any) (any, error) { return ptr.Interface(), nil } -func NewMessageMeta(contract string, typ reflect.Type) (MessageMeta, error) { +func NewMessageMeta(contract tvm.FullyQualifiedType, typ reflect.Type) (MessageMeta, error) { opcode, err := tvm.ExtractMagic(typ) if err != nil { return MessageMeta{}, fmt.Errorf("failed to parse opcode for %s: %w", typ, err) @@ -97,10 +97,10 @@ func (m MessageMeta) QualifiedKey() string { // messageJSON is the JSON representation of a MessageEnvelope, used in marshaling/unmarshaling. type messageJSON struct { - Contract string `json:"contract"` - Type string `json:"type"` - Opcode string `json:"opcode"` - Payload json.RawMessage `json:"payload"` + Contract tvm.FullyQualifiedType `json:"contract"` + Type string `json:"type"` + Opcode string `json:"opcode"` + Payload json.RawMessage `json:"payload"` } // MessageEnvelope is the JSON-friendly representation of a TL-B message. @@ -113,7 +113,7 @@ type MessageEnvelope[T any] struct { } // WrapMessage prepares a type-safe envelope for the provided TL-B message. -func WrapMessage[T any](contract string, val T) (*MessageEnvelope[T], error) { +func WrapMessage[T any](contract tvm.FullyQualifiedType, val T) (*MessageEnvelope[T], error) { meta, err := NewMessageMetaFromValue(contract, val) if err != nil { return nil, err @@ -125,7 +125,7 @@ func WrapMessage[T any](contract string, val T) (*MessageEnvelope[T], error) { }, nil } -func MustWrapMessage[T any](contract string, val T) *MessageEnvelope[T] { +func MustWrapMessage[T any](contract tvm.FullyQualifiedType, val T) *MessageEnvelope[T] { env, err := WrapMessage(contract, val) if err != nil { panic(fmt.Sprintf("failed to wrap message: %v", err)) diff --git a/pkg/ton/codec/resolvers/contract_data_to_cell.go b/pkg/ton/codec/resolvers/contract_data_to_cell.go index 97916a6a8..02cdfd72f 100644 --- a/pkg/ton/codec/resolvers/contract_data_to_cell.go +++ b/pkg/ton/codec/resolvers/contract_data_to_cell.go @@ -42,7 +42,7 @@ func (r *contractDataToCellResolver) Resolve(input map[string]any) (*cell.Cell, return nil, fmt.Errorf("missing 'contract' field in input: %v", input) } - contractStr, ok := contract.(string) + contractStr, ok := contract.(tvm.FullyQualifiedType) if !ok { return nil, fmt.Errorf("invalid 'contract' field type: %T", contract) } diff --git a/pkg/ton/tvm/registry.go b/pkg/ton/tvm/registry.go index 0ed3c0a69..5fa5e0ac2 100644 --- a/pkg/ton/tvm/registry.go +++ b/pkg/ton/tvm/registry.go @@ -51,10 +51,15 @@ func (m TLBMap) MustWithStorageType(storage any) TLBMap { return tlbMap } +// Fully Qualified Type is a identifier for a contract type following a reverse domain name notation. +// +// (e.g., "link.chain.ton.mcms.RBACTimelock") +type FullyQualifiedType string + // ContractTLBRegistry is a registry of TL-B types for decoding contract storage, messages, and events. // -// It maps contract types (string) to their corresponding TLBMap. -type ContractTLBRegistry map[string]TLBMap +// It maps contract types (FullyQualifiedType) to their corresponding TLBMap. +type ContractTLBRegistry map[FullyQualifiedType]TLBMap // SnapshotTLBMap creates a combined TLBMap from all registered contract types. // This is useful for decoding messages when the contract type is not known in advance. @@ -70,7 +75,7 @@ func (r ContractTLBRegistry) Snapshot() TLBMap { } // Lookup retrieves the TL-B type for the given contract and opcode. -func (r ContractTLBRegistry) Lookup(contract string, opcode uint64) (any, bool) { +func (r ContractTLBRegistry) Lookup(contract FullyQualifiedType, opcode uint64) (any, bool) { tlbs, ok := r[contract] if !ok { return nil, false