diff --git a/src/Neo/SmartContract/Manifest/ContractMethodDescriptor.cs b/src/Neo/SmartContract/Manifest/ContractMethodDescriptor.cs index ee5b779529..b39fc8af12 100644 --- a/src/Neo/SmartContract/Manifest/ContractMethodDescriptor.cs +++ b/src/Neo/SmartContract/Manifest/ContractMethodDescriptor.cs @@ -28,6 +28,11 @@ public class ContractMethodDescriptor : ContractEventDescriptor, IEquatable public ContractParameterType ReturnType { get; set; } + /// + /// NEP-25 extended return type + /// + public ExtendedType ExtendedReturnType { get; set; } + /// /// The position of the method in the contract script. /// @@ -46,14 +51,28 @@ public override void FromStackItem(StackItem stackItem) ReturnType = (ContractParameterType)(byte)@struct[2].GetInteger(); Offset = (int)@struct[3].GetInteger(); Safe = @struct[4].GetBoolean(); + if (@struct.Count >= 6) + { + ExtendedReturnType = new ExtendedType(); + ExtendedReturnType.FromStackItem((VM.Types.Array)@struct[5], 0); + } + else + { + ExtendedReturnType = null; + } } public override StackItem ToStackItem(IReferenceCounter referenceCounter) { - Struct @struct = (Struct)base.ToStackItem(referenceCounter); + var @struct = (Struct)base.ToStackItem(referenceCounter); @struct.Add((byte)ReturnType); @struct.Add(Offset); @struct.Add(Safe); + if (ExtendedReturnType != null) + { + var structExtended = new Struct(referenceCounter); + @struct.Add(ExtendedReturnType.ToStackItem(referenceCounter, structExtended)); + } return @struct; } @@ -70,7 +89,8 @@ public override StackItem ToStackItem(IReferenceCounter referenceCounter) Parameters = ((JArray)json["parameters"]).Select(u => ContractParameterDefinition.FromJson((JObject)u)).ToArray(), ReturnType = Enum.Parse(json["returntype"].GetString()), Offset = json["offset"].GetInt32(), - Safe = json["safe"].GetBoolean() + Safe = json["safe"].GetBoolean(), + ExtendedReturnType = json["extendedreturntype"] != null ? ExtendedType.FromJson((JObject)json["extendedreturntype"]) : null }; if (string.IsNullOrEmpty(descriptor.Name)) throw new FormatException(); _ = descriptor.Parameters.ToDictionary(p => p.Name); @@ -89,6 +109,10 @@ public override JObject ToJson() json["returntype"] = ReturnType.ToString(); json["offset"] = Offset; json["safe"] = Safe; + if (ExtendedReturnType != null) + { + json["extendedreturntype"] = ExtendedReturnType.ToJson(); + } return json; } diff --git a/src/Neo/SmartContract/Manifest/ContractParameterDefinition.cs b/src/Neo/SmartContract/Manifest/ContractParameterDefinition.cs index 3d14655234..635dd6edd2 100644 --- a/src/Neo/SmartContract/Manifest/ContractParameterDefinition.cs +++ b/src/Neo/SmartContract/Manifest/ContractParameterDefinition.cs @@ -32,16 +32,39 @@ public class ContractParameterDefinition : IInteroperable, IEquatable public ContractParameterType Type { get; set; } + /// + /// NEP-25 extended type + /// + public ExtendedType ExtendedType { get; set; } + void IInteroperable.FromStackItem(StackItem stackItem) { - Struct @struct = (Struct)stackItem; + var @struct = (Struct)stackItem; Name = @struct[0].GetString(); Type = (ContractParameterType)(byte)@struct[1].GetInteger(); + + if (@struct.Count >= 3) + { + ExtendedType = new ExtendedType(); + ExtendedType.FromStackItem((VM.Types.Array)@struct[5], 0); + } + else + { + ExtendedType = null; + } } public StackItem ToStackItem(IReferenceCounter referenceCounter) { - return new Struct(referenceCounter) { Name, (byte)Type }; + var @struct = new Struct(referenceCounter) { Name, (byte)Type }; + + if (ExtendedType != null) + { + var structExtended = new Struct(referenceCounter); + @struct.Add(ExtendedType.ToStackItem(referenceCounter, structExtended)); + } + + return @struct; } /// @@ -51,10 +74,11 @@ public StackItem ToStackItem(IReferenceCounter referenceCounter) /// The converted parameter. public static ContractParameterDefinition FromJson(JObject json) { - ContractParameterDefinition parameter = new() + var parameter = new ContractParameterDefinition() { Name = json["name"].GetString(), - Type = Enum.Parse(json["type"].GetString()) + Type = Enum.Parse(json["type"].GetString()), + ExtendedType = json["extendedtype"] != null ? ExtendedType.FromJson((JObject)json["extendedtype"]) : null, }; if (string.IsNullOrEmpty(parameter.Name)) throw new FormatException(); @@ -72,6 +96,10 @@ public JObject ToJson() var json = new JObject(); json["name"] = Name; json["type"] = Type.ToString(); + if (ExtendedType != null) + { + json["extendedtype"] = ExtendedType.ToJson(); + } return json; } diff --git a/src/Neo/SmartContract/Manifest/ExtendedType.cs b/src/Neo/SmartContract/Manifest/ExtendedType.cs new file mode 100644 index 0000000000..f9904102bc --- /dev/null +++ b/src/Neo/SmartContract/Manifest/ExtendedType.cs @@ -0,0 +1,94 @@ +// Copyright (C) 2015-2025 The Neo Project. +// +// ExtendedType.cs file belongs to the neo project and is free +// software distributed under the MIT software license, see the +// accompanying file LICENSE in the main directory of the +// repository or http://www.opensource.org/licenses/mit-license.php +// for more details. +// +// Redistribution and use in source and binary forms with or without +// modifications are permitted. + +#nullable enable + +using Neo.Json; +using Neo.VM; +using Neo.VM.Types; +using System; + +namespace Neo.SmartContract.Manifest +{ + public class ExtendedType : IInteroperable + { + /// + /// The type of the parameter. It can be any value of except . + /// + public ContractParameterType Type { get; set; } + + /// + /// NamedType is used to refer to one of the types defined in the namedtypes object of Contract, + /// so namedtypes MUST contain a field named name. + /// This field is only used for structures (ordered set of named values of diffent types), + /// if used other fields MUST NOT be set, except for the type which MUST be an Array. + /// Value string MUST start with a letter and can contain alphanumeric characters and dots. + /// It MUST NOT be longer than 64 characters. + /// + public string? NamedType { get; set; } + + void IInteroperable.FromStackItem(StackItem stackItem) + { + FromStackItem((VM.Types.Array)stackItem, 0); + } + + internal void FromStackItem(VM.Types.Array @struct, int startIndex) + { + Type = (ContractParameterType)(byte)@struct[startIndex].GetInteger(); + if (!Enum.IsDefined(typeof(ContractParameterType), Type)) throw new FormatException(); + NamedType = @struct[startIndex + 1].GetString(); + } + + internal StackItem ToStackItem(IReferenceCounter referenceCounter, Struct @struct) + { + @struct.Add((byte)Type); + @struct.Add(NamedType ?? StackItem.Null); + return @struct; + } + + StackItem IInteroperable.ToStackItem(IReferenceCounter referenceCounter) + { + var @struct = new Struct(referenceCounter); + ToStackItem(referenceCounter, @struct); + return @struct; + } + + /// + /// Converts the type from a JSON object. + /// + /// The method represented by a JSON object. + /// The extended type. + public static ExtendedType FromJson(JObject json) + { + ExtendedType type = new() + { + Type = Enum.Parse(json["type"]?.GetString() ?? throw new FormatException()), + NamedType = json["namedtype"]?.GetString(), + }; + if (!Enum.IsDefined(typeof(ContractParameterType), type.Type)) throw new FormatException(); + return type; + } + + /// + /// Converts the parameter to a JSON object. + /// + /// The parameter represented by a JSON object. + public virtual JObject ToJson() + { + var json = new JObject(); + json["type"] = Type.ToString(); + json["namedtype"] = NamedType; + return json; + } + } +} + +#nullable disable