diff --git a/src/neoxp/Extensions/ContractManifestExtensions.cs b/src/neoxp/Extensions/ContractManifestExtensions.cs index b8bb7a17..4337dff9 100644 --- a/src/neoxp/Extensions/ContractManifestExtensions.cs +++ b/src/neoxp/Extensions/ContractManifestExtensions.cs @@ -9,6 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using Microsoft.AspNetCore.Diagnostics; using Neo.SmartContract; using Neo.SmartContract.Manifest; @@ -16,121 +17,189 @@ namespace NeoExpress; internal static class ContractManifestExtensions { - public static bool IsNep11Compliant(this ContractManifest manifest) + public static List Nep11CompliantErrors(this ContractManifest manifest) { - try - { - var symbolMethod = manifest.Abi.GetMethod("symbol", 0); - var decimalsMethod = manifest.Abi.GetMethod("decimals", 0); - var totalSupplyMethod = manifest.Abi.GetMethod("totalSupply", 0); - var balanceOfMethod1 = manifest.Abi.GetMethod("balanceOf", 1); - var balanceOfMethod2 = manifest.Abi.GetMethod("balanceOf", 2); - var tokensOfMethod = manifest.Abi.GetMethod("tokensOf", 1); - var ownerOfMethod = manifest.Abi.GetMethod("ownerOf", 1); - var transferMethod1 = manifest.Abi.GetMethod("transfer", 3); - var transferMethod2 = manifest.Abi.GetMethod("transfer", 5); - - var symbolValid = symbolMethod.Safe == true && - symbolMethod.ReturnType == ContractParameterType.String; - var decimalsValid = decimalsMethod.Safe == true && - decimalsMethod.ReturnType == ContractParameterType.Integer; - var totalSupplyValid = totalSupplyMethod.Safe == true && - totalSupplyMethod.ReturnType == ContractParameterType.Integer; - var balanceOfValid1 = balanceOfMethod1.Safe == true && - balanceOfMethod1.ReturnType == ContractParameterType.Integer && - balanceOfMethod1.Parameters[0].Type == ContractParameterType.Hash160; - var balanceOfValid2 = balanceOfMethod2?.Safe == true && - balanceOfMethod2?.ReturnType == ContractParameterType.Integer && - balanceOfMethod2?.Parameters[0].Type == ContractParameterType.Hash160 && - balanceOfMethod2?.Parameters[0].Type == ContractParameterType.ByteArray; - var tokensOfValid = tokensOfMethod.Safe == true && - tokensOfMethod.ReturnType == ContractParameterType.InteropInterface && - tokensOfMethod.Parameters[0].Type == ContractParameterType.Hash160; - var ownerOfValid1 = ownerOfMethod.Safe == true && - ownerOfMethod.ReturnType == ContractParameterType.Hash160 && - ownerOfMethod.Parameters[0].Type == ContractParameterType.ByteArray; - var ownerOfValid2 = ownerOfMethod.Safe == true && - ownerOfMethod.ReturnType == ContractParameterType.InteropInterface && - ownerOfMethod.Parameters[0].Type == ContractParameterType.ByteArray; - var transferValid1 = transferMethod1.Safe == false && - transferMethod1.ReturnType == ContractParameterType.Boolean && - transferMethod1.Parameters[0].Type == ContractParameterType.Hash160 && - transferMethod1.Parameters[1].Type == ContractParameterType.ByteArray && - transferMethod1.Parameters[2].Type == ContractParameterType.Any; - var transferValid2 = transferMethod2?.Safe == false && - transferMethod2?.ReturnType == ContractParameterType.Boolean && - transferMethod2?.Parameters[0].Type == ContractParameterType.Hash160 && - transferMethod2?.Parameters[1].Type == ContractParameterType.Hash160 && - transferMethod2?.Parameters[2].Type == ContractParameterType.Integer && - transferMethod2?.Parameters[3].Type == ContractParameterType.ByteArray && - transferMethod2?.Parameters[4].Type == ContractParameterType.Any; - var transferEvent = manifest.Abi.Events.Any(a => - a.Name == "Transfer" && - a.Parameters.Length == 4 && - a.Parameters[0].Type == ContractParameterType.Hash160 && - a.Parameters[1].Type == ContractParameterType.Hash160 && - a.Parameters[2].Type == ContractParameterType.Integer && - a.Parameters[3].Type == ContractParameterType.ByteArray); - - // Waiting on issue https://github.com/neo-project/neo-devpack-dotnet/issues/812 - - return (symbolValid && - decimalsValid && - totalSupplyValid && - (balanceOfValid2 || balanceOfValid1) && - tokensOfValid && - (ownerOfValid2 || ownerOfValid1) && - (transferValid2 || transferValid1) && - transferEvent); - } - catch - { - return false; - } + var errors = new List(); + var symbolMethod = manifest.Abi.GetMethod("symbol", 0); + var decimalsMethod = manifest.Abi.GetMethod("decimals", 0); + var totalSupplyMethod = manifest.Abi.GetMethod("totalSupply", 0); + var balanceOfMethod1 = manifest.Abi.GetMethod("balanceOf", 1); + var balanceOfMethod2 = manifest.Abi.GetMethod("balanceOf", 2); + var tokensOfMethod = manifest.Abi.GetMethod("tokensOf", 1); + var ownerOfMethod = manifest.Abi.GetMethod("ownerOf", 1); + var transferMethod1 = manifest.Abi.GetMethod("transfer", 3); + var transferMethod2 = manifest.Abi.GetMethod("transfer", 5); + + var symbolValid = symbolMethod != null && symbolMethod.Safe && + symbolMethod.ReturnType == ContractParameterType.String; + var decimalsValid = decimalsMethod != null && decimalsMethod.Safe && + decimalsMethod.ReturnType == ContractParameterType.Integer; + var totalSupplyValid = totalSupplyMethod != null && totalSupplyMethod.Safe && + totalSupplyMethod.ReturnType == ContractParameterType.Integer; + var balanceOfValid1 = balanceOfMethod1 != null && balanceOfMethod1.Safe && + balanceOfMethod1.ReturnType == ContractParameterType.Integer && + balanceOfMethod1.Parameters.Length == 1 && + balanceOfMethod1.Parameters[0].Type == ContractParameterType.Hash160; + var balanceOfValid2 = balanceOfMethod2?.Safe == true && + balanceOfMethod2?.ReturnType == ContractParameterType.Integer && + balanceOfMethod2?.Parameters.Length == 2 && + balanceOfMethod2?.Parameters[0].Type == ContractParameterType.Hash160 && + balanceOfMethod2?.Parameters[0].Type == ContractParameterType.ByteArray; + var tokensOfValid = tokensOfMethod != null && tokensOfMethod.Safe && + tokensOfMethod.ReturnType == ContractParameterType.InteropInterface && + tokensOfMethod.Parameters.Length == 1 && + tokensOfMethod.Parameters[0].Type == ContractParameterType.Hash160; + var ownerOfValid1 = ownerOfMethod != null && ownerOfMethod.Safe && + ownerOfMethod.ReturnType == ContractParameterType.Hash160 && + ownerOfMethod.Parameters.Length == 1 && + ownerOfMethod.Parameters[0].Type == ContractParameterType.ByteArray; + var ownerOfValid2 = ownerOfMethod != null && ownerOfMethod.Safe && + ownerOfMethod.ReturnType == ContractParameterType.InteropInterface && + ownerOfMethod.Parameters.Length == 1 && + ownerOfMethod.Parameters[0].Type == ContractParameterType.ByteArray; + var transferValid1 = transferMethod1 != null && transferMethod1.Safe == false && + transferMethod1.ReturnType == ContractParameterType.Boolean && + transferMethod1.Parameters.Length == 3 && + transferMethod1.Parameters[0].Type == ContractParameterType.Hash160 && + transferMethod1.Parameters[1].Type == ContractParameterType.ByteArray && + transferMethod1.Parameters[2].Type == ContractParameterType.Any; + var transferValid2 = transferMethod2?.Safe == false && + transferMethod2?.ReturnType == ContractParameterType.Boolean && + transferMethod2.Parameters.Length == 5 && + transferMethod2?.Parameters[0].Type == ContractParameterType.Hash160 && + transferMethod2?.Parameters[1].Type == ContractParameterType.Hash160 && + transferMethod2?.Parameters[2].Type == ContractParameterType.Integer && + transferMethod2?.Parameters[3].Type == ContractParameterType.ByteArray && + transferMethod2?.Parameters[4].Type == ContractParameterType.Any; + var transferEvent = manifest.Abi.Events.Any(a => + a.Name == "Transfer" && + a.Parameters.Length == 4 && + a.Parameters[0].Type == ContractParameterType.Hash160 && + a.Parameters[1].Type == ContractParameterType.Hash160 && + a.Parameters[2].Type == ContractParameterType.Integer && + a.Parameters[3].Type == ContractParameterType.ByteArray); + + + if (!symbolValid) + errors.Add($"Incomplete or unsafe NEP standard NEP-11 implementation: symbol"); + if (!decimalsValid) + errors.Add($"Incomplete or unsafe NEP standard NEP-11 implementation: decimals"); + + if (!totalSupplyValid) + errors.Add($"Incomplete or unsafe NEP standard NEP-11 implementation: totalSupply"); + + if (!balanceOfValid1 && !balanceOfValid2) + errors.Add($"Incomplete or unsafe NEP standard NEP-11 implementation: balanceOf"); + + if (!tokensOfValid) + errors.Add($"Incomplete or unsafe NEP standard NEP-11 implementation: tokensOf"); + + if (!ownerOfValid1 && !ownerOfValid2) + errors.Add($"Incomplete or unsafe NEP standard NEP-11 implementation: ownerOf"); + + if (!transferValid1 && !transferValid2) + errors.Add($"Incomplete NEP standard NEP-11 implementation: transfer"); + + if (!transferEvent) + errors.Add($"Incomplete NEP standard NEP-11 implementation: {nameof(transferEvent)}"); + + return errors; + } + + public static List Nep17CompliantErrors(this ContractManifest manifest) + { + var errors = new List(); + var symbolMethod = manifest.Abi.GetMethod("symbol", 0); + var decimalsMethod = manifest.Abi.GetMethod("decimals", 0); + var totalSupplyMethod = manifest.Abi.GetMethod("totalSupply", 0); + var balanceOfMethod = manifest.Abi.GetMethod("balanceOf", 1); + var transferMethod = manifest.Abi.GetMethod("transfer", 4); + + var symbolValid = symbolMethod != null && symbolMethod.Safe && + symbolMethod.ReturnType == ContractParameterType.String; + var decimalsValid = decimalsMethod != null && decimalsMethod.Safe && + decimalsMethod.ReturnType == ContractParameterType.Integer; + var totalSupplyValid = totalSupplyMethod != null && totalSupplyMethod.Safe && + totalSupplyMethod.ReturnType == ContractParameterType.Integer; + var balanceOfValid = balanceOfMethod != null && balanceOfMethod.Safe && + balanceOfMethod.ReturnType == ContractParameterType.Integer && + balanceOfMethod.Parameters.Length == 1 && + balanceOfMethod.Parameters[0].Type == ContractParameterType.Hash160; + var transferValid = transferMethod != null && transferMethod.Safe == false && + transferMethod.ReturnType == ContractParameterType.Boolean && + transferMethod.Parameters.Length == 4 && + transferMethod.Parameters[0].Type == ContractParameterType.Hash160 && + transferMethod.Parameters[1].Type == ContractParameterType.Hash160 && + transferMethod.Parameters[2].Type == ContractParameterType.Integer && + transferMethod.Parameters[3].Type == ContractParameterType.Any; + var transferEvent = manifest.Abi.Events.Any(s => + s.Name == "Transfer" && + s.Parameters.Length == 3 && + s.Parameters[0].Type == ContractParameterType.Hash160 && + s.Parameters[1].Type == ContractParameterType.Hash160 && + s.Parameters[2].Type == ContractParameterType.Integer); + + if (!symbolValid) + errors.Add("Incomplete or unsafe NEP standard NEP-17 implementation: symbol"); + if (!decimalsValid) + errors.Add("Incomplete or unsafe NEP standard NEP-17 implementation: decimals"); + if (!totalSupplyValid) + errors.Add("Incomplete or unsafe NEP standard NEP-17 implementation: totalSupply"); + if (!balanceOfValid) + errors.Add("Incomplete or unsafe NEP standard NEP-17 implementation: balanceOf"); + if (!transferValid) + errors.Add("Incomplete NEP standard NEP-17 implementation: transfer"); + + return errors; + } + + public static List Nep24CompliantErrors(this ContractManifest manifest) + { + var errors = new List(); + var royaltyInfoMethod = manifest.Abi.GetMethod("royaltyInfo", 0); + + var royaltyInfoValid = royaltyInfoMethod != null && royaltyInfoMethod.Safe && + royaltyInfoMethod.ReturnType == ContractParameterType.Array && + royaltyInfoMethod.Parameters.Length == 3 && + royaltyInfoMethod.Parameters[0].Type == ContractParameterType.ByteArray && + royaltyInfoMethod.Parameters[1].Type == ContractParameterType.Hash160 && + royaltyInfoMethod.Parameters[2].Type == ContractParameterType.Integer; + + if (!royaltyInfoValid) + errors.Add("Incomplete or unsafe NEP standard NEP-24 implementation: royaltyInfo"); + return errors; } - public static bool IsNep17Compliant(this ContractManifest manifest) + + public static List Nep26CompliantErrors(this ContractManifest manifest) { - try - { - var symbolMethod = manifest.Abi.GetMethod("symbol", 0); - var decimalsMethod = manifest.Abi.GetMethod("decimals", 0); - var totalSupplyMethod = manifest.Abi.GetMethod("totalSupply", 0); - var balanceOfMethod = manifest.Abi.GetMethod("balanceOf", 1); - var transferMethod = manifest.Abi.GetMethod("transfer", 4); - - var symbolValid = symbolMethod.Safe == true && - symbolMethod.ReturnType == ContractParameterType.String; - var decimalsValid = decimalsMethod.Safe == true && - decimalsMethod.ReturnType == ContractParameterType.Integer; - var totalSupplyValid = totalSupplyMethod.Safe == true && - totalSupplyMethod.ReturnType == ContractParameterType.Integer; - var balanceOfValid = balanceOfMethod.Safe == true && - balanceOfMethod.ReturnType == ContractParameterType.Integer && - balanceOfMethod.Parameters[0].Type == ContractParameterType.Hash160; - var transferValid = transferMethod.Safe == false && - transferMethod.ReturnType == ContractParameterType.Boolean && - transferMethod.Parameters[0].Type == ContractParameterType.Hash160 && - transferMethod.Parameters[1].Type == ContractParameterType.Hash160 && - transferMethod.Parameters[2].Type == ContractParameterType.Integer && - transferMethod.Parameters[3].Type == ContractParameterType.Any; - var transferEvent = manifest.Abi.Events.Any(s => - s.Name == "Transfer" && - s.Parameters.Length == 3 && - s.Parameters[0].Type == ContractParameterType.Hash160 && - s.Parameters[1].Type == ContractParameterType.Hash160 && - s.Parameters[2].Type == ContractParameterType.Integer); - - return (symbolValid && - decimalsValid && - totalSupplyValid && - balanceOfValid && - transferValid && - transferEvent); - } - catch - { - return false; - } + var errors = new List(); + var onNEP11PaymentMethod = manifest.Abi.GetMethod("onNEP11Payment", 4); + var onNEP11PaymentValid = onNEP11PaymentMethod is { ReturnType: ContractParameterType.Void } && + onNEP11PaymentMethod.Parameters.Length == 4 && + onNEP11PaymentMethod.Parameters[0].Type == ContractParameterType.Hash160 && + onNEP11PaymentMethod.Parameters[1].Type == ContractParameterType.Integer && + onNEP11PaymentMethod.Parameters[2].Type == ContractParameterType.String && + onNEP11PaymentMethod.Parameters[3].Type == ContractParameterType.Any; + + if (!onNEP11PaymentValid) + errors.Add("Incomplete NEP standard NEP-26 implementation: onNEP11Payment"); + return errors; + } + + + public static List Nep27CompliantErrors(this ContractManifest manifest) + { + var errors = new List(); + var onNEP17PaymentMethod = manifest.Abi.GetMethod("onNEP17Payment", 3); + var onNEP17PaymentValid = onNEP17PaymentMethod is { ReturnType: ContractParameterType.Void } && + onNEP17PaymentMethod.Parameters.Length == 3 && + onNEP17PaymentMethod.Parameters[0].Type == ContractParameterType.Hash160 && + onNEP17PaymentMethod.Parameters[1].Type == ContractParameterType.Integer && + onNEP17PaymentMethod.Parameters[2].Type == ContractParameterType.Any; + + if (!onNEP17PaymentValid) + errors.Add("Incomplete NEP standard NEP-27 implementation: onNEP17Payment"); + return errors; } } diff --git a/src/neoxp/TransactionExecutor.cs b/src/neoxp/TransactionExecutor.cs index f3b70850..b4cbd322 100644 --- a/src/neoxp/TransactionExecutor.cs +++ b/src/neoxp/TransactionExecutor.cs @@ -107,30 +107,74 @@ public async Task ContractDeployAsync(string contract, string accountName, strin throw new Exception($"Contract named {manifest.Name} already deployed. Use --force to deploy contract with conflicting name."); } - var nep11 = false; - var nep17 = false; - var standards = manifest.SupportedStandards; - for (var i = 0; i < standards.Length; i++) + if (manifest.SupportedStandards.Contains("NEP-11") && manifest.SupportedStandards.Contains("NEP-17")) { - if (standards[i] == "NEP-11") - nep11 = true; - if (standards[i] == "NEP-17") - nep17 = true; + throw new Exception($"{manifest.Name} Contract declares support for both NEP-11 and NEP-17 standards. Use --force to deploy contract with invalid supported standards declarations."); } - if (nep11 && nep17) + if (manifest.SupportedStandards.Contains("NEP-17")) { - throw new Exception($"{manifest.Name} Contract declares support for both NEP-11 and NEP-17 standards. Use --force to deploy contract with invalid supported standards declarations."); + var nep17CompliantErrors = manifest.Nep17CompliantErrors(); + if (nep17CompliantErrors.Count > 0) + { + foreach (var error in nep17CompliantErrors) + { + Console.WriteLine(error); + } + throw new Exception($"{manifest.Name} Contract declares support for NEP-17 standards. However is not NEP-17 compliant. Invalid methods/events."); + } + } + + if (manifest.SupportedStandards.Contains("NEP-11")) + { + var nep11CompliantErrors = manifest.Nep11CompliantErrors(); + if (nep11CompliantErrors.Count() > 0) + { + foreach (var error in nep11CompliantErrors) + { + Console.WriteLine(error); + } + throw new Exception($"{manifest.Name} Contract declares support for NEP-11 standards. However is not NEP-11 compliant. Invalid methods/events."); + } + } + + if (manifest.SupportedStandards.Contains("NEP-24")) + { + var nep24CompliantErrors = manifest.Nep24CompliantErrors(); + if (nep24CompliantErrors.Count() > 0) + { + foreach (var error in nep24CompliantErrors) + { + Console.WriteLine(error); + } + throw new Exception($"{manifest.Name} Contract declares support for NEP-24 standards. However is not NEP-24 compliant. Invalid methods/events."); + } } - if (nep17 && manifest.IsNep17Compliant() == false) + if (manifest.SupportedStandards.Contains("NEP-26")) { - throw new Exception($"{manifest.Name} Contract declares support for NEP-17 standards. However is not NEP-17 compliant. Invalid methods/events."); + var nep26CompliantErrors = manifest.Nep26CompliantErrors(); + if (nep26CompliantErrors.Count() > 0) + { + foreach (var error in nep26CompliantErrors) + { + Console.WriteLine(error); + } + throw new Exception($"{manifest.Name} Contract declares support for NEP-26 standards. However is not NEP-26 compliant. Invalid methods/events."); + } } - if (nep11 && manifest.IsNep11Compliant() == false) + if (manifest.SupportedStandards.Contains("NEP-27")) { - throw new Exception($"{manifest.Name} Contract declares support for NEP-11 standards. However is not NEP-11 compliant. Invalid methods/events."); + var nep27CompliantErrors = manifest.Nep27CompliantErrors(); + if (nep27CompliantErrors.Count() > 0) + { + foreach (var error in nep27CompliantErrors) + { + Console.WriteLine(error); + } + throw new Exception($"{manifest.Name} Contract declares support for NEP-27 standards. However is not NEP-27 compliant. Invalid methods/events."); + } } }