From 87aeb806a5b388b8d1cd1a414172ea0686ec757b Mon Sep 17 00:00:00 2001 From: Shawn Date: Fri, 8 May 2020 15:25:25 +0800 Subject: [PATCH 01/21] add NFT templete and UT --- src/Neo.SmartContract.Framework/Helper.cs | 10 ++ templates/Template.NFT.CSharp/NFTContract.cs | 160 ++++++++++++++++++ .../Template.NFT.CSharp.csproj | 11 ++ tests/Template.NEP5.UnitTests/UnitTest_NFT.cs | 159 +++++++++++++++++ 4 files changed, 340 insertions(+) create mode 100644 templates/Template.NFT.CSharp/NFTContract.cs create mode 100644 templates/Template.NFT.CSharp/Template.NFT.CSharp.csproj create mode 100644 tests/Template.NEP5.UnitTests/UnitTest_NFT.cs diff --git a/src/Neo.SmartContract.Framework/Helper.cs b/src/Neo.SmartContract.Framework/Helper.cs index f2074d206..af0ceb39e 100644 --- a/src/Neo.SmartContract.Framework/Helper.cs +++ b/src/Neo.SmartContract.Framework/Helper.cs @@ -148,6 +148,16 @@ public static byte ToByte(this int source) return (byte)(source + 0); } + public static bool Equals(this byte[] left, byte[] right) + { + if (left.Length != right.Length) return false; + for (int i = 0; i < left.Length; i++) + { + if (left[i] != right[i]) return false; + } + return true; + } + [OpCode(OpCode.CAT)] public extern static byte[] Concat(this byte[] first, byte[] second); diff --git a/templates/Template.NFT.CSharp/NFTContract.cs b/templates/Template.NFT.CSharp/NFTContract.cs new file mode 100644 index 000000000..1d1824109 --- /dev/null +++ b/templates/Template.NFT.CSharp/NFTContract.cs @@ -0,0 +1,160 @@ +using Neo.SmartContract.Framework; +using Neo.SmartContract.Framework.Services.Neo; +using System; +using System.ComponentModel; +using System.Numerics; +using Helper = Neo.SmartContract.Framework.Helper; + +namespace NFTContract +{ + /// + /// Non-Fungible Token Smart Contract Template + /// + public class NFTContract : SmartContract + { + [DisplayName("mintedToken")] + public static event Action MintedToken;//(byte[] to, byte[] tokenId, byte[] properties); + + [DisplayName("transferred")] + public static event Action Transferred; //(byte[] from , byte[] to, BigInteger amount, byte[] TokenId) + + //super admin address + private static readonly byte[] superAdmin = Helper.ToScriptHash("Nj9Epc1x2sDmd6yH5qJPYwXRqSRf5X6KHE"); + private static string TotalSupplyKey() => "totalSupply"; + + private static StorageContext Context() => Storage.CurrentContext; + private static byte[] TokenOwnerKey(byte[] tokenId, byte[] owner) => new byte[] { 0x10 }.Concat(tokenId).Concat(owner); + private static byte[] TotalBalanceKey(byte[] owner) => new byte[] { 0x11 }.Concat(owner); + private static byte[] TokenBalanceKey(byte[] owner, byte[] tokenId) => new byte[] { 0x12 }.Concat(owner).Concat(tokenId); + private static byte[] PropertiesKey(byte[] tokenId) => new byte[] { 0x13 }.Concat(tokenId); + + private const int TOKEN_DECIMALS = 8; + private const int MAX_AMOUNT = 100_000_000; + + public static string Name() + { + return "MyNFT"; + } + + public static string Symbol() + { + return "MNFT"; + } + + public static string SupportedStandards() + { + return "{\"NEP-10\", \"NEP-11\"}"; + } + + public static BigInteger TotalSupply() + { + return Storage.Get(Context(), TotalSupplyKey()).ToBigInteger(); + } + + public static int Decimals() + { + return TOKEN_DECIMALS; + } + + public static Enumerator OwnerOf(byte[] tokenid) + { + return Storage.Find(Context(), new byte[] { 0x10 }.Concat(tokenid)).Values; + } + + public static Enumerator TokensOf(byte[] owner) + { + if (owner.Length != 20) throw new FormatException("The parameter 'owner' should be 20-byte address."); ; + return Storage.Find(Context(), new byte[] { 0x10 }.Concat(owner)).Values; + } + + public static byte[] Properties(byte[] tokenid) + { + return Storage.Get(Context(), PropertiesKey(tokenid)); + } + + public static bool MintNFT(byte[] tokenId, byte[] owner, byte[] properties) + { + if (!Runtime.CheckWitness(superAdmin)) return false; + + if (owner.Length != 20) throw new FormatException("The parameter 'owner' should be 20-byte address."); ; + if (properties.Length > 2048) throw new FormatException("The length of 'properties' should be less than 2048."); ; + + if (Storage.Get(Context(), TokenOwnerKey(tokenId, owner)) != null) return false; + + Storage.Put(Context(), PropertiesKey(tokenId), properties); + Storage.Put(Context(), TokenOwnerKey(tokenId, owner), owner); + + var totalSupplyKey = TotalSupplyKey(); + var totalSupply = Storage.Get(Context(), totalSupplyKey); + if (totalSupply is null) + Storage.Put(Context(), totalSupplyKey, 1); + else + Storage.Put(Context(), totalSupplyKey, totalSupply.ToBigInteger() + 1); + + var tokenBalanceKey = TokenBalanceKey(owner, tokenId); + Storage.Put(Context(), tokenBalanceKey, MAX_AMOUNT); + + var totalBalanceKey = TotalBalanceKey(owner); + var totalBalance = Storage.Get(Context(), totalBalanceKey); + if (totalBalance is null) + Storage.Put(Context(), totalBalanceKey, MAX_AMOUNT); + else + Storage.Put(Context(), totalBalanceKey, totalBalance.ToBigInteger() + MAX_AMOUNT); + + //notify + MintedToken(owner, tokenId, properties); + return true; + } + + public static BigInteger BalanceOf(byte[] owner, byte[] tokenid) + { + if (owner.Length != 20) throw new FormatException("The parameter 'owner' should be 20-byte address."); + if (tokenid is null) + return Storage.Get(Context(), TotalBalanceKey(owner)).ToBigInteger(); + else + return Storage.Get(Context(), TokenBalanceKey(owner, tokenid)).ToBigInteger(); + } + + public static bool Transfer(byte[] from, byte[] to, BigInteger amount, byte[] tokenId) + { + if (from.Length != 20 || to.Length != 20) throw new FormatException("The parameters 'from' and 'to' should be 20-byte addresses."); + if (amount < 0 || amount > MAX_AMOUNT) throw new FormatException("The parameters 'amount' is out of range."); + + if (!Runtime.CheckWitness(from)) return false; + + if (from.Equals(to)) + { + Transferred(from, to, amount, tokenId); + return true; + } + + var fromTokenBalance = Storage.Get(Context(), TokenBalanceKey(from, tokenId)); + var fromTotalBalance = Storage.Get(Context(), TotalBalanceKey(from)); + if (fromTokenBalance == null || fromTokenBalance.ToBigInteger() < amount) return false; + var fromNewBalance = fromTokenBalance.ToBigInteger() - amount; + if (fromNewBalance == 0) + { + Storage.Delete(Context(), TokenOwnerKey(tokenId, from)); + } + Storage.Put(Context(), TokenBalanceKey(from, tokenId), fromNewBalance); + Storage.Put(Context(), TotalBalanceKey(from), fromTotalBalance.ToBigInteger() - amount); + + var toTokenBalance = Storage.Get(Context(), TokenBalanceKey(to, tokenId)); + var toTotalBalance = Storage.Get(Context(), TotalBalanceKey(to)); + if (toTokenBalance is null && amount > 0) + { + Storage.Put(Context(), TokenOwnerKey(tokenId, to), to); + Storage.Put(Context(), TokenBalanceKey(to, tokenId), amount); + } + else + { + Storage.Put(Context(), TokenBalanceKey(to, tokenId), toTokenBalance.ToBigInteger() + amount); + Storage.Put(Context(), TotalBalanceKey(to), toTotalBalance.ToBigInteger() + amount); + } + + //notify + Transferred(from, to, amount, tokenId); + return true; + } + } +} diff --git a/templates/Template.NFT.CSharp/Template.NFT.CSharp.csproj b/templates/Template.NFT.CSharp/Template.NFT.CSharp.csproj new file mode 100644 index 000000000..dc0fa3c87 --- /dev/null +++ b/templates/Template.NFT.CSharp/Template.NFT.CSharp.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.1 + + + + + + + diff --git a/tests/Template.NEP5.UnitTests/UnitTest_NFT.cs b/tests/Template.NEP5.UnitTests/UnitTest_NFT.cs new file mode 100644 index 000000000..1608e23cd --- /dev/null +++ b/tests/Template.NEP5.UnitTests/UnitTest_NFT.cs @@ -0,0 +1,159 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Compiler.MSIL.UnitTests.Utils; +using Neo.IO; +using Neo.Network.P2P.Payloads; +using Neo.Persistence; +using Neo.VM.Types; +using Neo.SmartContract.Iterators; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace Neo.SmartContract.Framework.UnitTests +{ + [TestClass] + public class NFTTest + { + private TestEngine _engine; + + [TestInitialize] + public void Init() + { + _engine = new TestEngine(TriggerType.Application, new TestScriptContainer(), null); + _engine.AddEntryScript("../../../../../templates/Template.NFT.CSharp/NFTContract.cs"); + } + [TestMethod] + public void Test_GetName() + { + var result = _engine.ExecuteTestCaseStandard("name").Pop(); + + StackItem wantResult = "MyNFT"; + Assert.AreEqual(wantResult.ConvertTo(StackItemType.ByteString), result.ConvertTo(StackItemType.ByteString)); + } + + [TestMethod] + public void Test_GetDecimals() + { + var result = _engine.ExecuteTestCaseStandard("decimals").Pop(); + + var wantResult = 8; + Assert.AreEqual(wantResult, result as Integer); + } + + [TestMethod] + public void Test_MintNFT() + { + var hash = _engine.CurrentScriptHash; + var snapshot = _engine.Snapshot as TestSnapshot; + + snapshot.Contracts.Add(hash, new Ledger.ContractState() + { + Manifest = new Manifest.ContractManifest() + { + Features = Manifest.ContractFeatures.HasStorage + } + }); + + var tokenid = System.Text.Encoding.Default.GetBytes("abc"); + var owner = Wallets.Helper.ToScriptHash("Nj9Epc1x2sDmd6yH5qJPYwXRqSRf5X6KHE").ToArray(); + var to = Wallets.Helper.ToScriptHash("NTegNkUTqL5UUqb5MjsHP4cbXftkhuZA1p").ToArray(); + var properties = "NFT properties"; + + // Mint NFT + var result = _engine.ExecuteTestCaseStandard("mintNFT", tokenid, owner, properties).Pop(); + var wantResult = true; + Assert.AreEqual(wantResult, result.ConvertTo(StackItemType.Boolean)); + + _engine.Reset(); + result = _engine.ExecuteTestCaseStandard("totalSupply").Pop(); + var wantTotalSupply = 1; + Assert.AreEqual(wantTotalSupply, result); + + _engine.Reset(); + result = _engine.ExecuteTestCaseStandard("properties", tokenid).Pop(); + Assert.AreEqual(properties, result); + + _engine.Reset(); + result = _engine.ExecuteTestCaseStandard("balanceOf", owner, Null.Null).Pop(); + var wantBalance = 100_000_000; + Assert.AreEqual(wantBalance, result); + + _engine.Reset(); + result = _engine.ExecuteTestCaseStandard("balanceOf", owner, tokenid).Pop(); + wantBalance = 100_000_000; + Assert.AreEqual(wantBalance, result); + + // Mint new NFT + _engine.Reset(); + var tokenid2 = System.Text.Encoding.Default.GetBytes("def"); + result = _engine.ExecuteTestCaseStandard("mintNFT", tokenid2, owner, properties).Pop(); + wantResult = true; + Assert.AreEqual(wantResult, result.ConvertTo(StackItemType.Boolean)); + + _engine.Reset(); + result = _engine.ExecuteTestCaseStandard("totalSupply").Pop(); + wantTotalSupply = 2; + Assert.AreEqual(wantTotalSupply, result); + + // balance of all + _engine.Reset(); + result = _engine.ExecuteTestCaseStandard("balanceOf", owner, Null.Null).Pop(); + wantBalance = 200_000_000; + Assert.AreEqual(wantBalance, result); + + // balance of token2 + _engine.Reset(); + result = _engine.ExecuteTestCaseStandard("balanceOf", owner, tokenid2).Pop(); + wantBalance = 100_000_000; + Assert.AreEqual(wantBalance, result); + + // Transfer + _engine.Reset(); + result = _engine.ExecuteTestCaseStandard("transfer", owner, to, 10_000_000, tokenid2).Pop(); + Assert.AreEqual(true, result.ConvertTo(StackItemType.Boolean)); + + _engine.Reset(); + result = _engine.ExecuteTestCaseStandard("balanceOf", owner, tokenid2).Pop(); + wantBalance = 90_000_000; + Assert.AreEqual(wantBalance, result); + + _engine.Reset(); + result = _engine.ExecuteTestCaseStandard("balanceOf", to, tokenid2).Pop(); + wantBalance = 10_000_000; + Assert.AreEqual(wantBalance, result); + } + } + + internal class TestScriptContainer : IVerifiable + { + public Witness[] Witnesses { get => throw new System.NotImplementedException(); set => throw new System.NotImplementedException(); } + + public int Size => throw new System.NotImplementedException(); + + public void Deserialize(BinaryReader reader) + { + throw new System.NotImplementedException(); + } + + public void DeserializeUnsigned(BinaryReader reader) + { + throw new System.NotImplementedException(); + } + + public UInt160[] GetScriptHashesForVerifying(StoreView snapshot) + { + var hash = Wallets.Helper.ToScriptHash("Nj9Epc1x2sDmd6yH5qJPYwXRqSRf5X6KHE"); + return new UInt160[] { hash }; + } + + public void Serialize(BinaryWriter writer) + { + throw new System.NotImplementedException(); + } + + public void SerializeUnsigned(BinaryWriter writer) + { + throw new System.NotImplementedException(); + } + } +} From 97a4e733dcfdea1cf579dc511892151a8f1ea541 Mon Sep 17 00:00:00 2001 From: Shawn Date: Fri, 8 May 2020 15:54:09 +0800 Subject: [PATCH 02/21] fix --- src/Neo.SmartContract.Framework/Helper.cs | 1 + templates/Template.NFT.CSharp/NFTContract.cs | 6 +++--- tests/Template.NEP5.UnitTests/UnitTest_NFT.cs | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Neo.SmartContract.Framework/Helper.cs b/src/Neo.SmartContract.Framework/Helper.cs index af0ceb39e..11477fc63 100644 --- a/src/Neo.SmartContract.Framework/Helper.cs +++ b/src/Neo.SmartContract.Framework/Helper.cs @@ -151,6 +151,7 @@ public static byte ToByte(this int source) public static bool Equals(this byte[] left, byte[] right) { if (left.Length != right.Length) return false; + if (left == null || right == null) return false; for (int i = 0; i < left.Length; i++) { if (left[i] != right[i]) return false; diff --git a/templates/Template.NFT.CSharp/NFTContract.cs b/templates/Template.NFT.CSharp/NFTContract.cs index 1d1824109..efc7c16ca 100644 --- a/templates/Template.NFT.CSharp/NFTContract.cs +++ b/templates/Template.NFT.CSharp/NFTContract.cs @@ -12,10 +12,10 @@ namespace NFTContract /// public class NFTContract : SmartContract { - [DisplayName("mintedToken")] + [DisplayName("MintedToken")] public static event Action MintedToken;//(byte[] to, byte[] tokenId, byte[] properties); - [DisplayName("transferred")] + [DisplayName("Transferred")] public static event Action Transferred; //(byte[] from , byte[] to, BigInteger amount, byte[] TokenId) //super admin address @@ -63,7 +63,7 @@ public static Enumerator OwnerOf(byte[] tokenid) public static Enumerator TokensOf(byte[] owner) { - if (owner.Length != 20) throw new FormatException("The parameter 'owner' should be 20-byte address."); ; + if (owner.Length != 20) throw new FormatException("The parameter 'owner' should be 20-byte address."); return Storage.Find(Context(), new byte[] { 0x10 }.Concat(owner)).Values; } diff --git a/tests/Template.NEP5.UnitTests/UnitTest_NFT.cs b/tests/Template.NEP5.UnitTests/UnitTest_NFT.cs index 1608e23cd..dd6698c9c 100644 --- a/tests/Template.NEP5.UnitTests/UnitTest_NFT.cs +++ b/tests/Template.NEP5.UnitTests/UnitTest_NFT.cs @@ -95,13 +95,13 @@ public void Test_MintNFT() wantTotalSupply = 2; Assert.AreEqual(wantTotalSupply, result); - // balance of all + // Balance of all _engine.Reset(); result = _engine.ExecuteTestCaseStandard("balanceOf", owner, Null.Null).Pop(); wantBalance = 200_000_000; Assert.AreEqual(wantBalance, result); - // balance of token2 + // Balance of token2 _engine.Reset(); result = _engine.ExecuteTestCaseStandard("balanceOf", owner, tokenid2).Pop(); wantBalance = 100_000_000; From 040525da6974c8dedea4a351b18087588c275a43 Mon Sep 17 00:00:00 2001 From: Shawn Date: Mon, 11 May 2020 16:06:42 +0800 Subject: [PATCH 03/21] add UT and fix NFT --- templates/Template.NFT.CSharp/NFTContract.cs | 7 +++++- tests/Template.NEP5.UnitTests/UnitTest_NFT.cs | 25 +++++++++++++++++-- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/templates/Template.NFT.CSharp/NFTContract.cs b/templates/Template.NFT.CSharp/NFTContract.cs index efc7c16ca..fdac83516 100644 --- a/templates/Template.NFT.CSharp/NFTContract.cs +++ b/templates/Template.NFT.CSharp/NFTContract.cs @@ -27,6 +27,7 @@ public class NFTContract : SmartContract private static byte[] TotalBalanceKey(byte[] owner) => new byte[] { 0x11 }.Concat(owner); private static byte[] TokenBalanceKey(byte[] owner, byte[] tokenId) => new byte[] { 0x12 }.Concat(owner).Concat(tokenId); private static byte[] PropertiesKey(byte[] tokenId) => new byte[] { 0x13 }.Concat(tokenId); + private static byte[] TokensOfKey(byte[] owner, byte[] tokenId) => new byte[] { 0x14 }.Concat(owner).Concat(tokenId); private const int TOKEN_DECIMALS = 8; private const int MAX_AMOUNT = 100_000_000; @@ -64,7 +65,7 @@ public static Enumerator OwnerOf(byte[] tokenid) public static Enumerator TokensOf(byte[] owner) { if (owner.Length != 20) throw new FormatException("The parameter 'owner' should be 20-byte address."); - return Storage.Find(Context(), new byte[] { 0x10 }.Concat(owner)).Values; + return Storage.Find(Context(), new byte[] { 0x14 }.Concat(owner)).Values; } public static byte[] Properties(byte[] tokenid) @@ -83,6 +84,7 @@ public static bool MintNFT(byte[] tokenId, byte[] owner, byte[] properties) Storage.Put(Context(), PropertiesKey(tokenId), properties); Storage.Put(Context(), TokenOwnerKey(tokenId, owner), owner); + Storage.Put(Context(), TokensOfKey(owner, tokenId), tokenId); var totalSupplyKey = TotalSupplyKey(); var totalSupply = Storage.Get(Context(), totalSupplyKey); @@ -135,6 +137,8 @@ public static bool Transfer(byte[] from, byte[] to, BigInteger amount, byte[] to if (fromNewBalance == 0) { Storage.Delete(Context(), TokenOwnerKey(tokenId, from)); + Storage.Delete(Context(), TokensOfKey(from, tokenId)); + } Storage.Put(Context(), TokenBalanceKey(from, tokenId), fromNewBalance); Storage.Put(Context(), TotalBalanceKey(from), fromTotalBalance.ToBigInteger() - amount); @@ -145,6 +149,7 @@ public static bool Transfer(byte[] from, byte[] to, BigInteger amount, byte[] to { Storage.Put(Context(), TokenOwnerKey(tokenId, to), to); Storage.Put(Context(), TokenBalanceKey(to, tokenId), amount); + Storage.Put(Context(), TokensOfKey(to, tokenId), tokenId); } else { diff --git a/tests/Template.NEP5.UnitTests/UnitTest_NFT.cs b/tests/Template.NEP5.UnitTests/UnitTest_NFT.cs index dd6698c9c..0876c78df 100644 --- a/tests/Template.NEP5.UnitTests/UnitTest_NFT.cs +++ b/tests/Template.NEP5.UnitTests/UnitTest_NFT.cs @@ -3,9 +3,8 @@ using Neo.IO; using Neo.Network.P2P.Payloads; using Neo.Persistence; +using Neo.SmartContract.Enumerators; using Neo.VM.Types; -using Neo.SmartContract.Iterators; -using System.Collections.Generic; using System.IO; using System.Linq; @@ -121,6 +120,28 @@ public void Test_MintNFT() result = _engine.ExecuteTestCaseStandard("balanceOf", to, tokenid2).Pop(); wantBalance = 10_000_000; Assert.AreEqual(wantBalance, result); + + // OwnerOf + _engine.Reset(); + var iterator = ((InteropInterface)_engine.ExecuteTestCaseStandard("ownerOf", tokenid2).Pop()).GetInterface(); + iterator.Next(); + var v1 = iterator.Value(); + Assert.AreEqual(new ByteString(to), v1); + + iterator.Next(); + var v2 = iterator.Value(); + Assert.AreEqual(new ByteString(owner), v2); + + // TokensOf + _engine.Reset(); + iterator = ((InteropInterface)_engine.ExecuteTestCaseStandard("tokensOf", owner).Pop()).GetInterface(); + iterator.Next(); + v1 = iterator.Value(); + Assert.AreEqual(new ByteString(tokenid), v1); + + iterator.Next(); + v2 = iterator.Value(); + Assert.AreEqual(new ByteString(tokenid2), v2); } } From 351562fc8efdccdf0e473313fdbb4fa8b1ce13b9 Mon Sep 17 00:00:00 2001 From: Shawn Date: Tue, 12 May 2020 14:32:03 +0800 Subject: [PATCH 04/21] modify UT for NFT --- neo-devpack-dotnet.sln | 13 ++++++++++++ .../Neo.Compiler.MSIL.csproj | 1 + .../Neo.SmartContract.Framework.csproj | 5 +++++ templates/Template.NFT.CSharp/NFTContract.cs | 1 + tests/Template.NEP5.UnitTests/UnitTest_NFT.cs | 21 ++++++++++--------- 5 files changed, 31 insertions(+), 10 deletions(-) diff --git a/neo-devpack-dotnet.sln b/neo-devpack-dotnet.sln index d9367d9d2..a0fd3ab42 100644 --- a/neo-devpack-dotnet.sln +++ b/neo-devpack-dotnet.sln @@ -26,6 +26,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Template.NEP5.CSharp", "tem EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Template.NEP5.UnitTests", "tests\Template.NEP5.UnitTests\Template.NEP5.UnitTests.csproj", "{780141EE-D6E9-4591-8470-8F91B12027CA}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Template.NFT.CSharp", "templates\Template.NFT.CSharp\Template.NFT.CSharp.csproj", "{C3A7FF3C-3B54-49E1-A56A-84AD4BFAE0E8}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "neo", "..\..\..\NEO\master\src\neo\neo.csproj", "{BB607536-C191-4A9E-AA97-3D5EB5BFD321}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -68,6 +72,14 @@ Global {780141EE-D6E9-4591-8470-8F91B12027CA}.Debug|Any CPU.Build.0 = Debug|Any CPU {780141EE-D6E9-4591-8470-8F91B12027CA}.Release|Any CPU.ActiveCfg = Release|Any CPU {780141EE-D6E9-4591-8470-8F91B12027CA}.Release|Any CPU.Build.0 = Release|Any CPU + {C3A7FF3C-3B54-49E1-A56A-84AD4BFAE0E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C3A7FF3C-3B54-49E1-A56A-84AD4BFAE0E8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C3A7FF3C-3B54-49E1-A56A-84AD4BFAE0E8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C3A7FF3C-3B54-49E1-A56A-84AD4BFAE0E8}.Release|Any CPU.Build.0 = Release|Any CPU + {BB607536-C191-4A9E-AA97-3D5EB5BFD321}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BB607536-C191-4A9E-AA97-3D5EB5BFD321}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BB607536-C191-4A9E-AA97-3D5EB5BFD321}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BB607536-C191-4A9E-AA97-3D5EB5BFD321}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -82,6 +94,7 @@ Global {93BEC5CC-BAFF-4389-89E7-84AAFF5D495D} = {D5266066-0AFD-44D5-A83E-2F73668A63C8} {ADD05222-DC45-4FDC-A41A-30A97BACC95F} = {7042EF23-826B-45A1-A905-59AD678C31B7} {780141EE-D6E9-4591-8470-8F91B12027CA} = {D5266066-0AFD-44D5-A83E-2F73668A63C8} + {C3A7FF3C-3B54-49E1-A56A-84AD4BFAE0E8} = {7042EF23-826B-45A1-A905-59AD678C31B7} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {6DA935E1-C674-4364-B087-F1B511B79215} diff --git a/src/Neo.Compiler.MSIL/Neo.Compiler.MSIL.csproj b/src/Neo.Compiler.MSIL/Neo.Compiler.MSIL.csproj index 3eafb1805..7a315ac11 100644 --- a/src/Neo.Compiler.MSIL/Neo.Compiler.MSIL.csproj +++ b/src/Neo.Compiler.MSIL/Neo.Compiler.MSIL.csproj @@ -30,6 +30,7 @@ + diff --git a/src/Neo.SmartContract.Framework/Neo.SmartContract.Framework.csproj b/src/Neo.SmartContract.Framework/Neo.SmartContract.Framework.csproj index 0ec57013f..ba33378ee 100644 --- a/src/Neo.SmartContract.Framework/Neo.SmartContract.Framework.csproj +++ b/src/Neo.SmartContract.Framework/Neo.SmartContract.Framework.csproj @@ -18,4 +18,9 @@ Neo.SmartContract.Framework + + + + + diff --git a/templates/Template.NFT.CSharp/NFTContract.cs b/templates/Template.NFT.CSharp/NFTContract.cs index fdac83516..8c1b6dd37 100644 --- a/templates/Template.NFT.CSharp/NFTContract.cs +++ b/templates/Template.NFT.CSharp/NFTContract.cs @@ -1,3 +1,4 @@ +using Neo; using Neo.SmartContract.Framework; using Neo.SmartContract.Framework.Services.Neo; using System; diff --git a/tests/Template.NEP5.UnitTests/UnitTest_NFT.cs b/tests/Template.NEP5.UnitTests/UnitTest_NFT.cs index 0876c78df..c496cb461 100644 --- a/tests/Template.NEP5.UnitTests/UnitTest_NFT.cs +++ b/tests/Template.NEP5.UnitTests/UnitTest_NFT.cs @@ -4,6 +4,7 @@ using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.SmartContract.Enumerators; +using Neo.SmartContract.Iterators; using Neo.VM.Types; using System.IO; using System.Linq; @@ -123,24 +124,24 @@ public void Test_MintNFT() // OwnerOf _engine.Reset(); - var iterator = ((InteropInterface)_engine.ExecuteTestCaseStandard("ownerOf", tokenid2).Pop()).GetInterface(); - iterator.Next(); - var v1 = iterator.Value(); + var enumerator = ((InteropInterface)_engine.ExecuteTestCaseStandard("ownerOf", tokenid2).Pop()).GetInterface(); + enumerator.Next(); + var v1 = enumerator.Value(); Assert.AreEqual(new ByteString(to), v1); - iterator.Next(); - var v2 = iterator.Value(); + enumerator.Next(); + var v2 = enumerator.Value(); Assert.AreEqual(new ByteString(owner), v2); // TokensOf _engine.Reset(); - iterator = ((InteropInterface)_engine.ExecuteTestCaseStandard("tokensOf", owner).Pop()).GetInterface(); - iterator.Next(); - v1 = iterator.Value(); + enumerator = ((InteropInterface)_engine.ExecuteTestCaseStandard("tokensOf", owner).Pop()).GetInterface(); + enumerator.Next(); + v1 = enumerator.Value(); Assert.AreEqual(new ByteString(tokenid), v1); - iterator.Next(); - v2 = iterator.Value(); + enumerator.Next(); + v2 = enumerator.Value(); Assert.AreEqual(new ByteString(tokenid2), v2); } } From fef6d9f2af9aab820b98eaef0d38839f93de7168 Mon Sep 17 00:00:00 2001 From: Shawn Date: Tue, 12 May 2020 14:45:02 +0800 Subject: [PATCH 05/21] fix --- neo-devpack-dotnet.sln | 7 ------- src/Neo.Compiler.MSIL/Neo.Compiler.MSIL.csproj | 1 - .../Neo.SmartContract.Framework.csproj | 5 ----- 3 files changed, 13 deletions(-) diff --git a/neo-devpack-dotnet.sln b/neo-devpack-dotnet.sln index a0fd3ab42..a6180a9e8 100644 --- a/neo-devpack-dotnet.sln +++ b/neo-devpack-dotnet.sln @@ -28,8 +28,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Template.NEP5.UnitTests", " EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Template.NFT.CSharp", "templates\Template.NFT.CSharp\Template.NFT.CSharp.csproj", "{C3A7FF3C-3B54-49E1-A56A-84AD4BFAE0E8}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "neo", "..\..\..\NEO\master\src\neo\neo.csproj", "{BB607536-C191-4A9E-AA97-3D5EB5BFD321}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -76,10 +74,6 @@ Global {C3A7FF3C-3B54-49E1-A56A-84AD4BFAE0E8}.Debug|Any CPU.Build.0 = Debug|Any CPU {C3A7FF3C-3B54-49E1-A56A-84AD4BFAE0E8}.Release|Any CPU.ActiveCfg = Release|Any CPU {C3A7FF3C-3B54-49E1-A56A-84AD4BFAE0E8}.Release|Any CPU.Build.0 = Release|Any CPU - {BB607536-C191-4A9E-AA97-3D5EB5BFD321}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BB607536-C191-4A9E-AA97-3D5EB5BFD321}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BB607536-C191-4A9E-AA97-3D5EB5BFD321}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BB607536-C191-4A9E-AA97-3D5EB5BFD321}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -94,7 +88,6 @@ Global {93BEC5CC-BAFF-4389-89E7-84AAFF5D495D} = {D5266066-0AFD-44D5-A83E-2F73668A63C8} {ADD05222-DC45-4FDC-A41A-30A97BACC95F} = {7042EF23-826B-45A1-A905-59AD678C31B7} {780141EE-D6E9-4591-8470-8F91B12027CA} = {D5266066-0AFD-44D5-A83E-2F73668A63C8} - {C3A7FF3C-3B54-49E1-A56A-84AD4BFAE0E8} = {7042EF23-826B-45A1-A905-59AD678C31B7} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {6DA935E1-C674-4364-B087-F1B511B79215} diff --git a/src/Neo.Compiler.MSIL/Neo.Compiler.MSIL.csproj b/src/Neo.Compiler.MSIL/Neo.Compiler.MSIL.csproj index 7a315ac11..3eafb1805 100644 --- a/src/Neo.Compiler.MSIL/Neo.Compiler.MSIL.csproj +++ b/src/Neo.Compiler.MSIL/Neo.Compiler.MSIL.csproj @@ -30,7 +30,6 @@ - diff --git a/src/Neo.SmartContract.Framework/Neo.SmartContract.Framework.csproj b/src/Neo.SmartContract.Framework/Neo.SmartContract.Framework.csproj index ba33378ee..0ec57013f 100644 --- a/src/Neo.SmartContract.Framework/Neo.SmartContract.Framework.csproj +++ b/src/Neo.SmartContract.Framework/Neo.SmartContract.Framework.csproj @@ -18,9 +18,4 @@ Neo.SmartContract.Framework - - - - - From ab06bd44dd04a5f96134ec19d34a180a47dc7e38 Mon Sep 17 00:00:00 2001 From: Shawn Date: Mon, 18 May 2020 10:09:35 +0800 Subject: [PATCH 06/21] update neo --- src/Neo.Compiler.MSIL/Neo.Compiler.MSIL.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Neo.Compiler.MSIL/Neo.Compiler.MSIL.csproj b/src/Neo.Compiler.MSIL/Neo.Compiler.MSIL.csproj index 3eafb1805..d75a9acda 100644 --- a/src/Neo.Compiler.MSIL/Neo.Compiler.MSIL.csproj +++ b/src/Neo.Compiler.MSIL/Neo.Compiler.MSIL.csproj @@ -26,7 +26,7 @@ - + From d917d16552d143fd44bff8c00ed2f313e1631ece Mon Sep 17 00:00:00 2001 From: Shawn Date: Tue, 19 May 2020 15:36:26 +0800 Subject: [PATCH 07/21] del cosigner --- .../Services/Neo/CryptoTest.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/Neo.SmartContract.Framework.UnitTests/Services/Neo/CryptoTest.cs b/tests/Neo.SmartContract.Framework.UnitTests/Services/Neo/CryptoTest.cs index f9ebccd6f..2ecc88edf 100644 --- a/tests/Neo.SmartContract.Framework.UnitTests/Services/Neo/CryptoTest.cs +++ b/tests/Neo.SmartContract.Framework.UnitTests/Services/Neo/CryptoTest.cs @@ -23,7 +23,6 @@ public void Init() _engine = new TestEngine(TriggerType.Application, new Transaction() { Attributes = new TransactionAttribute[0], - Cosigners = new Cosigner[0], Script = new byte[0], Sender = UInt160.Zero, Witnesses = new Witness[0], @@ -58,7 +57,7 @@ public void Test_SHA256() var item = result.Pop(); Assert.IsInstanceOfType(item, typeof(ByteString)); - Assert.AreEqual("8d7f537c8c8c4236e99de1a3323c06e7a6b22b7e802a210096520c6a0dc037df", item.GetSpan().ToArray().ToHexString()); + Assert.AreEqual("293ba9cd0c05e23da15e39d29bcb8edfa5b2eeb29163a325c3229e81feed3d11", item.GetSpan().ToArray().ToHexString()); } [TestMethod] From 73b358aa1d52e3a63721384677139bd5f9188c9e Mon Sep 17 00:00:00 2001 From: ShawnYun <42930111+ShawnYun@users.noreply.github.com> Date: Tue, 19 May 2020 15:40:24 +0800 Subject: [PATCH 08/21] fix --- neo-devpack-dotnet.sln | 4 ---- 1 file changed, 4 deletions(-) diff --git a/neo-devpack-dotnet.sln b/neo-devpack-dotnet.sln index a6180a9e8..941028ee3 100644 --- a/neo-devpack-dotnet.sln +++ b/neo-devpack-dotnet.sln @@ -70,10 +70,6 @@ Global {780141EE-D6E9-4591-8470-8F91B12027CA}.Debug|Any CPU.Build.0 = Debug|Any CPU {780141EE-D6E9-4591-8470-8F91B12027CA}.Release|Any CPU.ActiveCfg = Release|Any CPU {780141EE-D6E9-4591-8470-8F91B12027CA}.Release|Any CPU.Build.0 = Release|Any CPU - {C3A7FF3C-3B54-49E1-A56A-84AD4BFAE0E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C3A7FF3C-3B54-49E1-A56A-84AD4BFAE0E8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C3A7FF3C-3B54-49E1-A56A-84AD4BFAE0E8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C3A7FF3C-3B54-49E1-A56A-84AD4BFAE0E8}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 9197fdb036aaba73f417e9a40d9c287a3d2447c1 Mon Sep 17 00:00:00 2001 From: Shawn Date: Wed, 20 May 2020 10:12:28 +0800 Subject: [PATCH 09/21] fix equals --- src/Neo.SmartContract.Framework/Helper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Neo.SmartContract.Framework/Helper.cs b/src/Neo.SmartContract.Framework/Helper.cs index 11477fc63..a73add8f3 100644 --- a/src/Neo.SmartContract.Framework/Helper.cs +++ b/src/Neo.SmartContract.Framework/Helper.cs @@ -150,8 +150,8 @@ public static byte ToByte(this int source) public static bool Equals(this byte[] left, byte[] right) { - if (left.Length != right.Length) return false; if (left == null || right == null) return false; + if (left.Length != right.Length) return false; for (int i = 0; i < left.Length; i++) { if (left[i] != right[i]) return false; From af0ca3a1a02301b27701cc2d413133c1834c3c62 Mon Sep 17 00:00:00 2001 From: Shawn Date: Wed, 20 May 2020 10:18:39 +0800 Subject: [PATCH 10/21] format --- src/Neo.SmartContract.Framework/Helper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Neo.SmartContract.Framework/Helper.cs b/src/Neo.SmartContract.Framework/Helper.cs index a73add8f3..5408f4662 100644 --- a/src/Neo.SmartContract.Framework/Helper.cs +++ b/src/Neo.SmartContract.Framework/Helper.cs @@ -151,7 +151,7 @@ public static byte ToByte(this int source) public static bool Equals(this byte[] left, byte[] right) { if (left == null || right == null) return false; - if (left.Length != right.Length) return false; + if (left.Length != right.Length) return false; for (int i = 0; i < left.Length; i++) { if (left[i] != right[i]) return false; From 55a63d942c13bc9429b6bd4812ee9aea90095cbd Mon Sep 17 00:00:00 2001 From: Shawn Date: Thu, 21 May 2020 11:40:05 +0800 Subject: [PATCH 11/21] use storagemap --- templates/Template.NFT.CSharp/NFTContract.cs | 76 +++++++++++--------- 1 file changed, 41 insertions(+), 35 deletions(-) diff --git a/templates/Template.NFT.CSharp/NFTContract.cs b/templates/Template.NFT.CSharp/NFTContract.cs index 8c1b6dd37..33abc41fc 100644 --- a/templates/Template.NFT.CSharp/NFTContract.cs +++ b/templates/Template.NFT.CSharp/NFTContract.cs @@ -14,21 +14,20 @@ namespace NFTContract public class NFTContract : SmartContract { [DisplayName("MintedToken")] - public static event Action MintedToken;//(byte[] to, byte[] tokenId, byte[] properties); + public static event Action MintedToken; [DisplayName("Transferred")] - public static event Action Transferred; //(byte[] from , byte[] to, BigInteger amount, byte[] TokenId) + public static event Action Transferred; - //super admin address private static readonly byte[] superAdmin = Helper.ToScriptHash("Nj9Epc1x2sDmd6yH5qJPYwXRqSRf5X6KHE"); private static string TotalSupplyKey() => "totalSupply"; private static StorageContext Context() => Storage.CurrentContext; - private static byte[] TokenOwnerKey(byte[] tokenId, byte[] owner) => new byte[] { 0x10 }.Concat(tokenId).Concat(owner); - private static byte[] TotalBalanceKey(byte[] owner) => new byte[] { 0x11 }.Concat(owner); - private static byte[] TokenBalanceKey(byte[] owner, byte[] tokenId) => new byte[] { 0x12 }.Concat(owner).Concat(tokenId); - private static byte[] PropertiesKey(byte[] tokenId) => new byte[] { 0x13 }.Concat(tokenId); - private static byte[] TokensOfKey(byte[] owner, byte[] tokenId) => new byte[] { 0x14 }.Concat(owner).Concat(tokenId); + private static byte[] StorageProfixTokenOwner(byte[] tokenId) => new byte[] { 0x10 }.Concat(tokenId); + private static byte[] StorageProfixTotalBalance(byte[] owner) => new byte[] { 0x11 }.Concat(owner); + private static byte[] StorageProfixTokenBalance(byte[] owner) => new byte[] { 0x12 }.Concat(owner); + private static byte[] StorageProfixProperties(byte[] tokenId) => new byte[] { 0x13 }.Concat(tokenId); + private static byte[] StorageProfixTokensOf(byte[] owner) => new byte[] { 0x14 }.Concat(owner); private const int TOKEN_DECIMALS = 8; private const int MAX_AMOUNT = 100_000_000; @@ -45,7 +44,7 @@ public static string Symbol() public static string SupportedStandards() { - return "{\"NEP-10\", \"NEP-11\"}"; + return "NEP-10, NEP-11"; } public static BigInteger TotalSupply() @@ -71,21 +70,23 @@ public static Enumerator TokensOf(byte[] owner) public static byte[] Properties(byte[] tokenid) { - return Storage.Get(Context(), PropertiesKey(tokenid)); + return Storage.Get(Context(), StorageProfixProperties(tokenid)); } public static bool MintNFT(byte[] tokenId, byte[] owner, byte[] properties) { if (!Runtime.CheckWitness(superAdmin)) return false; - if (owner.Length != 20) throw new FormatException("The parameter 'owner' should be 20-byte address."); ; - if (properties.Length > 2048) throw new FormatException("The length of 'properties' should be less than 2048."); ; + if (owner.Length != 20) throw new FormatException("The parameter 'owner' should be 20-byte address."); + if (properties.Length > 2048) throw new FormatException("The length of 'properties' should be less than 2048."); - if (Storage.Get(Context(), TokenOwnerKey(tokenId, owner)) != null) return false; + StorageMap tokenOwnerMap = Storage.CurrentContext.CreateMap(StorageProfixTokenOwner(tokenId)); + if (tokenOwnerMap.Get(owner) != null) return false; - Storage.Put(Context(), PropertiesKey(tokenId), properties); - Storage.Put(Context(), TokenOwnerKey(tokenId, owner), owner); - Storage.Put(Context(), TokensOfKey(owner, tokenId), tokenId); + StorageMap tokenOfMap = Storage.CurrentContext.CreateMap(StorageProfixTokensOf(owner)); + Storage.Put(Context(), StorageProfixProperties(tokenId), properties); + tokenOwnerMap.Put(owner, owner); + tokenOfMap.Put(tokenId, tokenId); var totalSupplyKey = TotalSupplyKey(); var totalSupply = Storage.Get(Context(), totalSupplyKey); @@ -94,10 +95,10 @@ public static bool MintNFT(byte[] tokenId, byte[] owner, byte[] properties) else Storage.Put(Context(), totalSupplyKey, totalSupply.ToBigInteger() + 1); - var tokenBalanceKey = TokenBalanceKey(owner, tokenId); - Storage.Put(Context(), tokenBalanceKey, MAX_AMOUNT); + StorageMap tokenBalanceMap = Storage.CurrentContext.CreateMap(StorageProfixTokenBalance(owner)); + tokenBalanceMap.Put(tokenId, MAX_AMOUNT); - var totalBalanceKey = TotalBalanceKey(owner); + var totalBalanceKey = StorageProfixTotalBalance(owner); var totalBalance = Storage.Get(Context(), totalBalanceKey); if (totalBalance is null) Storage.Put(Context(), totalBalanceKey, MAX_AMOUNT); @@ -113,9 +114,9 @@ public static BigInteger BalanceOf(byte[] owner, byte[] tokenid) { if (owner.Length != 20) throw new FormatException("The parameter 'owner' should be 20-byte address."); if (tokenid is null) - return Storage.Get(Context(), TotalBalanceKey(owner)).ToBigInteger(); + return Storage.Get(Context(), StorageProfixTotalBalance(owner)).ToBigInteger(); else - return Storage.Get(Context(), TokenBalanceKey(owner, tokenid)).ToBigInteger(); + return Storage.CurrentContext.CreateMap(StorageProfixTokenBalance(owner)).Get(tokenid).ToBigInteger(); } public static bool Transfer(byte[] from, byte[] to, BigInteger amount, byte[] tokenId) @@ -131,31 +132,36 @@ public static bool Transfer(byte[] from, byte[] to, BigInteger amount, byte[] to return true; } - var fromTokenBalance = Storage.Get(Context(), TokenBalanceKey(from, tokenId)); - var fromTotalBalance = Storage.Get(Context(), TotalBalanceKey(from)); + StorageMap fromTokenBalanceMap = Storage.CurrentContext.CreateMap(StorageProfixTokenBalance(from)); + StorageMap toTokenBalanceMap = Storage.CurrentContext.CreateMap(StorageProfixTokenBalance(to)); + StorageMap tokenOwnerMap = Storage.CurrentContext.CreateMap(StorageProfixTokenOwner(tokenId)); + StorageMap fromTokensOfMap = Storage.CurrentContext.CreateMap(StorageProfixTokensOf(from)); + StorageMap toTokensOfMap = Storage.CurrentContext.CreateMap(StorageProfixTokensOf(to)); + + var fromTokenBalance = fromTokenBalanceMap.Get(tokenId); + var fromTotalBalance = Storage.Get(Context(), StorageProfixTotalBalance(from)); if (fromTokenBalance == null || fromTokenBalance.ToBigInteger() < amount) return false; var fromNewBalance = fromTokenBalance.ToBigInteger() - amount; if (fromNewBalance == 0) { - Storage.Delete(Context(), TokenOwnerKey(tokenId, from)); - Storage.Delete(Context(), TokensOfKey(from, tokenId)); - + tokenOwnerMap.Delete(from); + fromTokensOfMap.Delete(tokenId); } - Storage.Put(Context(), TokenBalanceKey(from, tokenId), fromNewBalance); - Storage.Put(Context(), TotalBalanceKey(from), fromTotalBalance.ToBigInteger() - amount); + fromTokenBalanceMap.Put(tokenId, fromNewBalance); + Storage.Put(Context(), StorageProfixTotalBalance(from), fromTotalBalance.ToBigInteger() - amount); - var toTokenBalance = Storage.Get(Context(), TokenBalanceKey(to, tokenId)); - var toTotalBalance = Storage.Get(Context(), TotalBalanceKey(to)); + var toTokenBalance = toTokenBalanceMap.Get(tokenId); + var toTotalBalance = Storage.Get(Context(), StorageProfixTotalBalance(to)); if (toTokenBalance is null && amount > 0) { - Storage.Put(Context(), TokenOwnerKey(tokenId, to), to); - Storage.Put(Context(), TokenBalanceKey(to, tokenId), amount); - Storage.Put(Context(), TokensOfKey(to, tokenId), tokenId); + tokenOwnerMap.Put(to, to); + toTokenBalanceMap.Put(tokenId, amount); + toTokensOfMap.Put(tokenId, tokenId); } else { - Storage.Put(Context(), TokenBalanceKey(to, tokenId), toTokenBalance.ToBigInteger() + amount); - Storage.Put(Context(), TotalBalanceKey(to), toTotalBalance.ToBigInteger() + amount); + toTokenBalanceMap.Put(tokenId, toTokenBalance.ToBigInteger() + amount); + Storage.Put(Context(), StorageProfixTotalBalance(to), toTotalBalance.ToBigInteger() + amount); } //notify From a9522a842024f48acd03ed9833ec0955c46eb83f Mon Sep 17 00:00:00 2001 From: Shawn Date: Thu, 21 May 2020 11:42:28 +0800 Subject: [PATCH 12/21] format --- templates/Template.NFT.CSharp/NFTContract.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/templates/Template.NFT.CSharp/NFTContract.cs b/templates/Template.NFT.CSharp/NFTContract.cs index 33abc41fc..594745aba 100644 --- a/templates/Template.NFT.CSharp/NFTContract.cs +++ b/templates/Template.NFT.CSharp/NFTContract.cs @@ -17,7 +17,7 @@ public class NFTContract : SmartContract public static event Action MintedToken; [DisplayName("Transferred")] - public static event Action Transferred; + public static event Action Transferred; private static readonly byte[] superAdmin = Helper.ToScriptHash("Nj9Epc1x2sDmd6yH5qJPYwXRqSRf5X6KHE"); private static string TotalSupplyKey() => "totalSupply"; @@ -123,7 +123,6 @@ public static bool Transfer(byte[] from, byte[] to, BigInteger amount, byte[] to { if (from.Length != 20 || to.Length != 20) throw new FormatException("The parameters 'from' and 'to' should be 20-byte addresses."); if (amount < 0 || amount > MAX_AMOUNT) throw new FormatException("The parameters 'amount' is out of range."); - if (!Runtime.CheckWitness(from)) return false; if (from.Equals(to)) From d351d263935e65c8955714113539aeef9e8bb71b Mon Sep 17 00:00:00 2001 From: Shawn Date: Mon, 25 May 2020 15:20:25 +0800 Subject: [PATCH 13/21] fix --- templates/Template.NFT.CSharp/NFTContract.cs | 45 ++++++++++---------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/templates/Template.NFT.CSharp/NFTContract.cs b/templates/Template.NFT.CSharp/NFTContract.cs index 594745aba..28704ae6d 100644 --- a/templates/Template.NFT.CSharp/NFTContract.cs +++ b/templates/Template.NFT.CSharp/NFTContract.cs @@ -1,4 +1,3 @@ -using Neo; using Neo.SmartContract.Framework; using Neo.SmartContract.Framework.Services.Neo; using System; @@ -23,11 +22,11 @@ public class NFTContract : SmartContract private static string TotalSupplyKey() => "totalSupply"; private static StorageContext Context() => Storage.CurrentContext; - private static byte[] StorageProfixTokenOwner(byte[] tokenId) => new byte[] { 0x10 }.Concat(tokenId); - private static byte[] StorageProfixTotalBalance(byte[] owner) => new byte[] { 0x11 }.Concat(owner); - private static byte[] StorageProfixTokenBalance(byte[] owner) => new byte[] { 0x12 }.Concat(owner); - private static byte[] StorageProfixProperties(byte[] tokenId) => new byte[] { 0x13 }.Concat(tokenId); - private static byte[] StorageProfixTokensOf(byte[] owner) => new byte[] { 0x14 }.Concat(owner); + private static byte[] StoragePrefixTokenOwner(byte[] tokenId) => new byte[] { 0x10 }.Concat(tokenId); + private static byte[] StoragePrefixTotalBalance(byte[] owner) => new byte[] { 0x11 }.Concat(owner); + private static byte[] StoragePrefixTokenBalance(byte[] owner) => new byte[] { 0x12 }.Concat(owner); + private static byte[] StoragePrefixProperties(byte[] tokenId) => new byte[] { 0x13 }.Concat(tokenId); + private static byte[] StoragePrefixTokensOf(byte[] owner) => new byte[] { 0x14 }.Concat(owner); private const int TOKEN_DECIMALS = 8; private const int MAX_AMOUNT = 100_000_000; @@ -70,7 +69,7 @@ public static Enumerator TokensOf(byte[] owner) public static byte[] Properties(byte[] tokenid) { - return Storage.Get(Context(), StorageProfixProperties(tokenid)); + return Storage.Get(Context(), StoragePrefixProperties(tokenid)); } public static bool MintNFT(byte[] tokenId, byte[] owner, byte[] properties) @@ -80,11 +79,11 @@ public static bool MintNFT(byte[] tokenId, byte[] owner, byte[] properties) if (owner.Length != 20) throw new FormatException("The parameter 'owner' should be 20-byte address."); if (properties.Length > 2048) throw new FormatException("The length of 'properties' should be less than 2048."); - StorageMap tokenOwnerMap = Storage.CurrentContext.CreateMap(StorageProfixTokenOwner(tokenId)); + StorageMap tokenOwnerMap = Storage.CurrentContext.CreateMap(StoragePrefixTokenOwner(tokenId)); if (tokenOwnerMap.Get(owner) != null) return false; - StorageMap tokenOfMap = Storage.CurrentContext.CreateMap(StorageProfixTokensOf(owner)); - Storage.Put(Context(), StorageProfixProperties(tokenId), properties); + StorageMap tokenOfMap = Storage.CurrentContext.CreateMap(StoragePrefixTokensOf(owner)); + Storage.Put(Context(), StoragePrefixProperties(tokenId), properties); tokenOwnerMap.Put(owner, owner); tokenOfMap.Put(tokenId, tokenId); @@ -95,10 +94,10 @@ public static bool MintNFT(byte[] tokenId, byte[] owner, byte[] properties) else Storage.Put(Context(), totalSupplyKey, totalSupply.ToBigInteger() + 1); - StorageMap tokenBalanceMap = Storage.CurrentContext.CreateMap(StorageProfixTokenBalance(owner)); + StorageMap tokenBalanceMap = Storage.CurrentContext.CreateMap(StoragePrefixTokenBalance(owner)); tokenBalanceMap.Put(tokenId, MAX_AMOUNT); - var totalBalanceKey = StorageProfixTotalBalance(owner); + var totalBalanceKey = StoragePrefixTotalBalance(owner); var totalBalance = Storage.Get(Context(), totalBalanceKey); if (totalBalance is null) Storage.Put(Context(), totalBalanceKey, MAX_AMOUNT); @@ -114,9 +113,9 @@ public static BigInteger BalanceOf(byte[] owner, byte[] tokenid) { if (owner.Length != 20) throw new FormatException("The parameter 'owner' should be 20-byte address."); if (tokenid is null) - return Storage.Get(Context(), StorageProfixTotalBalance(owner)).ToBigInteger(); + return Storage.Get(Context(), StoragePrefixTotalBalance(owner)).ToBigInteger(); else - return Storage.CurrentContext.CreateMap(StorageProfixTokenBalance(owner)).Get(tokenid).ToBigInteger(); + return Storage.CurrentContext.CreateMap(StoragePrefixTokenBalance(owner)).Get(tokenid).ToBigInteger(); } public static bool Transfer(byte[] from, byte[] to, BigInteger amount, byte[] tokenId) @@ -131,14 +130,14 @@ public static bool Transfer(byte[] from, byte[] to, BigInteger amount, byte[] to return true; } - StorageMap fromTokenBalanceMap = Storage.CurrentContext.CreateMap(StorageProfixTokenBalance(from)); - StorageMap toTokenBalanceMap = Storage.CurrentContext.CreateMap(StorageProfixTokenBalance(to)); - StorageMap tokenOwnerMap = Storage.CurrentContext.CreateMap(StorageProfixTokenOwner(tokenId)); - StorageMap fromTokensOfMap = Storage.CurrentContext.CreateMap(StorageProfixTokensOf(from)); - StorageMap toTokensOfMap = Storage.CurrentContext.CreateMap(StorageProfixTokensOf(to)); + StorageMap fromTokenBalanceMap = Storage.CurrentContext.CreateMap(StoragePrefixTokenBalance(from)); + StorageMap toTokenBalanceMap = Storage.CurrentContext.CreateMap(StoragePrefixTokenBalance(to)); + StorageMap tokenOwnerMap = Storage.CurrentContext.CreateMap(StoragePrefixTokenOwner(tokenId)); + StorageMap fromTokensOfMap = Storage.CurrentContext.CreateMap(StoragePrefixTokensOf(from)); + StorageMap toTokensOfMap = Storage.CurrentContext.CreateMap(StoragePrefixTokensOf(to)); var fromTokenBalance = fromTokenBalanceMap.Get(tokenId); - var fromTotalBalance = Storage.Get(Context(), StorageProfixTotalBalance(from)); + var fromTotalBalance = Storage.Get(Context(), StoragePrefixTotalBalance(from)); if (fromTokenBalance == null || fromTokenBalance.ToBigInteger() < amount) return false; var fromNewBalance = fromTokenBalance.ToBigInteger() - amount; if (fromNewBalance == 0) @@ -147,10 +146,10 @@ public static bool Transfer(byte[] from, byte[] to, BigInteger amount, byte[] to fromTokensOfMap.Delete(tokenId); } fromTokenBalanceMap.Put(tokenId, fromNewBalance); - Storage.Put(Context(), StorageProfixTotalBalance(from), fromTotalBalance.ToBigInteger() - amount); + Storage.Put(Context(), StoragePrefixTotalBalance(from), fromTotalBalance.ToBigInteger() - amount); var toTokenBalance = toTokenBalanceMap.Get(tokenId); - var toTotalBalance = Storage.Get(Context(), StorageProfixTotalBalance(to)); + var toTotalBalance = Storage.Get(Context(), StoragePrefixTotalBalance(to)); if (toTokenBalance is null && amount > 0) { tokenOwnerMap.Put(to, to); @@ -160,7 +159,7 @@ public static bool Transfer(byte[] from, byte[] to, BigInteger amount, byte[] to else { toTokenBalanceMap.Put(tokenId, toTokenBalance.ToBigInteger() + amount); - Storage.Put(Context(), StorageProfixTotalBalance(to), toTotalBalance.ToBigInteger() + amount); + Storage.Put(Context(), StoragePrefixTotalBalance(to), toTotalBalance.ToBigInteger() + amount); } //notify From e7971c79d3a44c104ae804d2445a6ea9dbee2499 Mon Sep 17 00:00:00 2001 From: Shawn Date: Wed, 3 Jun 2020 18:21:08 +0800 Subject: [PATCH 14/21] modify nft --- templates/Template.NFT.CSharp/NFTContract.cs | 105 +++++++++++------- tests/Template.NEP5.UnitTests/UnitTest_NFT.cs | 19 ++-- 2 files changed, 74 insertions(+), 50 deletions(-) diff --git a/templates/Template.NFT.CSharp/NFTContract.cs b/templates/Template.NFT.CSharp/NFTContract.cs index 28704ae6d..714a28fa0 100644 --- a/templates/Template.NFT.CSharp/NFTContract.cs +++ b/templates/Template.NFT.CSharp/NFTContract.cs @@ -19,17 +19,16 @@ public class NFTContract : SmartContract public static event Action Transferred; private static readonly byte[] superAdmin = Helper.ToScriptHash("Nj9Epc1x2sDmd6yH5qJPYwXRqSRf5X6KHE"); - private static string TotalSupplyKey() => "totalSupply"; private static StorageContext Context() => Storage.CurrentContext; - private static byte[] StoragePrefixTokenOwner(byte[] tokenId) => new byte[] { 0x10 }.Concat(tokenId); - private static byte[] StoragePrefixTotalBalance(byte[] owner) => new byte[] { 0x11 }.Concat(owner); - private static byte[] StoragePrefixTokenBalance(byte[] owner) => new byte[] { 0x12 }.Concat(owner); - private static byte[] StoragePrefixProperties(byte[] tokenId) => new byte[] { 0x13 }.Concat(tokenId); - private static byte[] StoragePrefixTokensOf(byte[] owner) => new byte[] { 0x14 }.Concat(owner); + private static byte[] Prefix_TotalSupply => new byte[] { 0x10 }; + private static byte[] Prefix_TokenOwner => new byte[] { 0x11 }; + private static byte[] Prefix_TokenBalance => new byte[] { 0x12 }; + private static byte[] Prefix_Properties => new byte[] { 0x13 }; + private static byte[] Prefix_TokensOf => new byte[] { 0x14 }; private const int TOKEN_DECIMALS = 8; - private const int MAX_AMOUNT = 100_000_000; + private const int FACTOR = 100_000_000; public static string Name() { @@ -46,9 +45,14 @@ public static string SupportedStandards() return "NEP-10, NEP-11"; } + public static byte[] CreateStorageKey(byte[] prefix, byte[] key) + { + return prefix.Concat(key); + } + public static BigInteger TotalSupply() { - return Storage.Get(Context(), TotalSupplyKey()).ToBigInteger(); + return Storage.Get(Context(), Prefix_TotalSupply).ToBigInteger(); } public static int Decimals() @@ -58,51 +62,43 @@ public static int Decimals() public static Enumerator OwnerOf(byte[] tokenid) { - return Storage.Find(Context(), new byte[] { 0x10 }.Concat(tokenid)).Values; + return Storage.Find(Context(), CreateStorageKey(Prefix_TokenOwner, tokenid)).Values; } public static Enumerator TokensOf(byte[] owner) { if (owner.Length != 20) throw new FormatException("The parameter 'owner' should be 20-byte address."); - return Storage.Find(Context(), new byte[] { 0x14 }.Concat(owner)).Values; + return Storage.Find(Context(), CreateStorageKey(Prefix_TokensOf, owner)).Values; } - public static byte[] Properties(byte[] tokenid) + public static string Properties(byte[] tokenid) { - return Storage.Get(Context(), StoragePrefixProperties(tokenid)); + return Storage.Get(Context(), CreateStorageKey(Prefix_Properties, tokenid)).AsString(); } - public static bool MintNFT(byte[] tokenId, byte[] owner, byte[] properties) + public static bool Mint(byte[] tokenId, byte[] owner, byte[] properties) { if (!Runtime.CheckWitness(superAdmin)) return false; if (owner.Length != 20) throw new FormatException("The parameter 'owner' should be 20-byte address."); if (properties.Length > 2048) throw new FormatException("The length of 'properties' should be less than 2048."); - StorageMap tokenOwnerMap = Storage.CurrentContext.CreateMap(StoragePrefixTokenOwner(tokenId)); + StorageMap tokenOwnerMap = Storage.CurrentContext.CreateMap(CreateStorageKey(Prefix_TokenOwner, tokenId)); if (tokenOwnerMap.Get(owner) != null) return false; - StorageMap tokenOfMap = Storage.CurrentContext.CreateMap(StoragePrefixTokensOf(owner)); - Storage.Put(Context(), StoragePrefixProperties(tokenId), properties); + StorageMap tokenOfMap = Storage.CurrentContext.CreateMap(CreateStorageKey(Prefix_TokensOf, owner)); + Storage.Put(Context(), CreateStorageKey(Prefix_Properties ,tokenId), properties); tokenOwnerMap.Put(owner, owner); tokenOfMap.Put(tokenId, tokenId); - var totalSupplyKey = TotalSupplyKey(); - var totalSupply = Storage.Get(Context(), totalSupplyKey); + var totalSupply = Storage.Get(Context(), Prefix_TotalSupply); if (totalSupply is null) - Storage.Put(Context(), totalSupplyKey, 1); + Storage.Put(Context(), Prefix_TotalSupply, 1); else - Storage.Put(Context(), totalSupplyKey, totalSupply.ToBigInteger() + 1); + Storage.Put(Context(), Prefix_TotalSupply, totalSupply.ToBigInteger() + 1); - StorageMap tokenBalanceMap = Storage.CurrentContext.CreateMap(StoragePrefixTokenBalance(owner)); - tokenBalanceMap.Put(tokenId, MAX_AMOUNT); - - var totalBalanceKey = StoragePrefixTotalBalance(owner); - var totalBalance = Storage.Get(Context(), totalBalanceKey); - if (totalBalance is null) - Storage.Put(Context(), totalBalanceKey, MAX_AMOUNT); - else - Storage.Put(Context(), totalBalanceKey, totalBalance.ToBigInteger() + MAX_AMOUNT); + StorageMap tokenBalanceMap = Storage.CurrentContext.CreateMap(CreateStorageKey(Prefix_TokenBalance, owner)); + tokenBalanceMap.Put(tokenId, FACTOR); //notify MintedToken(owner, tokenId, properties); @@ -113,15 +109,21 @@ public static BigInteger BalanceOf(byte[] owner, byte[] tokenid) { if (owner.Length != 20) throw new FormatException("The parameter 'owner' should be 20-byte address."); if (tokenid is null) - return Storage.Get(Context(), StoragePrefixTotalBalance(owner)).ToBigInteger(); + { + var iterator = Storage.Find(Context(), CreateStorageKey(Prefix_TokenBalance, owner)); + BigInteger result = 0; + while(iterator.Next()) + result += iterator.Value.ToBigInteger(); + return result; + } else - return Storage.CurrentContext.CreateMap(StoragePrefixTokenBalance(owner)).Get(tokenid).ToBigInteger(); + return Storage.CurrentContext.CreateMap(CreateStorageKey(Prefix_TokenBalance, owner)).Get(tokenid).ToBigInteger(); } public static bool Transfer(byte[] from, byte[] to, BigInteger amount, byte[] tokenId) { if (from.Length != 20 || to.Length != 20) throw new FormatException("The parameters 'from' and 'to' should be 20-byte addresses."); - if (amount < 0 || amount > MAX_AMOUNT) throw new FormatException("The parameters 'amount' is out of range."); + if (amount < 0 || amount > FACTOR) throw new FormatException("The parameters 'amount' is out of range."); if (!Runtime.CheckWitness(from)) return false; if (from.Equals(to)) @@ -130,14 +132,13 @@ public static bool Transfer(byte[] from, byte[] to, BigInteger amount, byte[] to return true; } - StorageMap fromTokenBalanceMap = Storage.CurrentContext.CreateMap(StoragePrefixTokenBalance(from)); - StorageMap toTokenBalanceMap = Storage.CurrentContext.CreateMap(StoragePrefixTokenBalance(to)); - StorageMap tokenOwnerMap = Storage.CurrentContext.CreateMap(StoragePrefixTokenOwner(tokenId)); - StorageMap fromTokensOfMap = Storage.CurrentContext.CreateMap(StoragePrefixTokensOf(from)); - StorageMap toTokensOfMap = Storage.CurrentContext.CreateMap(StoragePrefixTokensOf(to)); + StorageMap fromTokenBalanceMap = Storage.CurrentContext.CreateMap(CreateStorageKey(Prefix_TokenBalance, from)); + StorageMap toTokenBalanceMap = Storage.CurrentContext.CreateMap(CreateStorageKey(Prefix_TokenBalance, to)); + StorageMap tokenOwnerMap = Storage.CurrentContext.CreateMap(CreateStorageKey(Prefix_TokenOwner, tokenId)); + StorageMap fromTokensOfMap = Storage.CurrentContext.CreateMap(CreateStorageKey(Prefix_TokensOf, from)); + StorageMap toTokensOfMap = Storage.CurrentContext.CreateMap(CreateStorageKey(Prefix_TokensOf, to)); var fromTokenBalance = fromTokenBalanceMap.Get(tokenId); - var fromTotalBalance = Storage.Get(Context(), StoragePrefixTotalBalance(from)); if (fromTokenBalance == null || fromTokenBalance.ToBigInteger() < amount) return false; var fromNewBalance = fromTokenBalance.ToBigInteger() - amount; if (fromNewBalance == 0) @@ -146,10 +147,8 @@ public static bool Transfer(byte[] from, byte[] to, BigInteger amount, byte[] to fromTokensOfMap.Delete(tokenId); } fromTokenBalanceMap.Put(tokenId, fromNewBalance); - Storage.Put(Context(), StoragePrefixTotalBalance(from), fromTotalBalance.ToBigInteger() - amount); var toTokenBalance = toTokenBalanceMap.Get(tokenId); - var toTotalBalance = Storage.Get(Context(), StoragePrefixTotalBalance(to)); if (toTokenBalance is null && amount > 0) { tokenOwnerMap.Put(to, to); @@ -159,12 +158,36 @@ public static bool Transfer(byte[] from, byte[] to, BigInteger amount, byte[] to else { toTokenBalanceMap.Put(tokenId, toTokenBalance.ToBigInteger() + amount); - Storage.Put(Context(), StoragePrefixTotalBalance(to), toTotalBalance.ToBigInteger() + amount); } //notify Transferred(from, to, amount, tokenId); return true; } + + public static bool Migrate(byte[] script, string manifest) + { + if (!Runtime.CheckWitness(superAdmin)) + { + return false; + } + if (script.Length == 0 || manifest.Length == 0) + { + return false; + } + Contract.Update(script, manifest); + return true; + } + + public static bool Destroy() + { + if (!Runtime.CheckWitness(superAdmin)) + { + return false; + } + + Contract.Destroy(); + return true; + } } } diff --git a/tests/Template.NEP5.UnitTests/UnitTest_NFT.cs b/tests/Template.NEP5.UnitTests/UnitTest_NFT.cs index c496cb461..b1da3e91c 100644 --- a/tests/Template.NEP5.UnitTests/UnitTest_NFT.cs +++ b/tests/Template.NEP5.UnitTests/UnitTest_NFT.cs @@ -8,6 +8,7 @@ using Neo.VM.Types; using System.IO; using System.Linq; +using System.Numerics; namespace Neo.SmartContract.Framework.UnitTests { @@ -60,7 +61,7 @@ public void Test_MintNFT() var properties = "NFT properties"; // Mint NFT - var result = _engine.ExecuteTestCaseStandard("mintNFT", tokenid, owner, properties).Pop(); + var result = _engine.ExecuteTestCaseStandard("mint", tokenid, owner, properties).Pop(); var wantResult = true; Assert.AreEqual(wantResult, result.ConvertTo(StackItemType.Boolean)); @@ -75,18 +76,18 @@ public void Test_MintNFT() _engine.Reset(); result = _engine.ExecuteTestCaseStandard("balanceOf", owner, Null.Null).Pop(); - var wantBalance = 100_000_000; + var wantBalance = new Integer(BigInteger.Pow(10, 8)); Assert.AreEqual(wantBalance, result); _engine.Reset(); result = _engine.ExecuteTestCaseStandard("balanceOf", owner, tokenid).Pop(); - wantBalance = 100_000_000; + wantBalance = BigInteger.Pow(10, 8); Assert.AreEqual(wantBalance, result); // Mint new NFT _engine.Reset(); var tokenid2 = System.Text.Encoding.Default.GetBytes("def"); - result = _engine.ExecuteTestCaseStandard("mintNFT", tokenid2, owner, properties).Pop(); + result = _engine.ExecuteTestCaseStandard("mint", tokenid2, owner, properties).Pop(); wantResult = true; Assert.AreEqual(wantResult, result.ConvertTo(StackItemType.Boolean)); @@ -98,28 +99,28 @@ public void Test_MintNFT() // Balance of all _engine.Reset(); result = _engine.ExecuteTestCaseStandard("balanceOf", owner, Null.Null).Pop(); - wantBalance = 200_000_000; + wantBalance = 2 * BigInteger.Pow(10, 8); Assert.AreEqual(wantBalance, result); // Balance of token2 _engine.Reset(); result = _engine.ExecuteTestCaseStandard("balanceOf", owner, tokenid2).Pop(); - wantBalance = 100_000_000; + wantBalance = BigInteger.Pow(10, 8); Assert.AreEqual(wantBalance, result); // Transfer _engine.Reset(); - result = _engine.ExecuteTestCaseStandard("transfer", owner, to, 10_000_000, tokenid2).Pop(); + result = _engine.ExecuteTestCaseStandard("transfer", owner, to, BigInteger.Pow(10, 7), tokenid2).Pop(); Assert.AreEqual(true, result.ConvertTo(StackItemType.Boolean)); _engine.Reset(); result = _engine.ExecuteTestCaseStandard("balanceOf", owner, tokenid2).Pop(); - wantBalance = 90_000_000; + wantBalance = 9 * BigInteger.Pow(10, 7); Assert.AreEqual(wantBalance, result); _engine.Reset(); result = _engine.ExecuteTestCaseStandard("balanceOf", to, tokenid2).Pop(); - wantBalance = 10_000_000; + wantBalance = BigInteger.Pow(10, 7); Assert.AreEqual(wantBalance, result); // OwnerOf From d2b3265ec657b84682374c16f7d12a87a1b6f246 Mon Sep 17 00:00:00 2001 From: Shawn Date: Wed, 3 Jun 2020 18:26:33 +0800 Subject: [PATCH 15/21] format --- templates/Template.NFT.CSharp/NFTContract.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/Template.NFT.CSharp/NFTContract.cs b/templates/Template.NFT.CSharp/NFTContract.cs index 714a28fa0..b5f15d77e 100644 --- a/templates/Template.NFT.CSharp/NFTContract.cs +++ b/templates/Template.NFT.CSharp/NFTContract.cs @@ -87,7 +87,7 @@ public static bool Mint(byte[] tokenId, byte[] owner, byte[] properties) if (tokenOwnerMap.Get(owner) != null) return false; StorageMap tokenOfMap = Storage.CurrentContext.CreateMap(CreateStorageKey(Prefix_TokensOf, owner)); - Storage.Put(Context(), CreateStorageKey(Prefix_Properties ,tokenId), properties); + Storage.Put(Context(), CreateStorageKey(Prefix_Properties, tokenId), properties); tokenOwnerMap.Put(owner, owner); tokenOfMap.Put(tokenId, tokenId); @@ -112,7 +112,7 @@ public static BigInteger BalanceOf(byte[] owner, byte[] tokenid) { var iterator = Storage.Find(Context(), CreateStorageKey(Prefix_TokenBalance, owner)); BigInteger result = 0; - while(iterator.Next()) + while (iterator.Next()) result += iterator.Value.ToBigInteger(); return result; } From cdc5920129ad3a908794744ea9ef53c445f1f2f2 Mon Sep 17 00:00:00 2001 From: Shawn Date: Thu, 4 Jun 2020 14:50:53 +0800 Subject: [PATCH 16/21] fix prefix byte[] to byte --- templates/Template.NFT.CSharp/NFTContract.cs | 51 ++++++++++---------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/templates/Template.NFT.CSharp/NFTContract.cs b/templates/Template.NFT.CSharp/NFTContract.cs index b5f15d77e..638edb613 100644 --- a/templates/Template.NFT.CSharp/NFTContract.cs +++ b/templates/Template.NFT.CSharp/NFTContract.cs @@ -21,11 +21,12 @@ public class NFTContract : SmartContract private static readonly byte[] superAdmin = Helper.ToScriptHash("Nj9Epc1x2sDmd6yH5qJPYwXRqSRf5X6KHE"); private static StorageContext Context() => Storage.CurrentContext; - private static byte[] Prefix_TotalSupply => new byte[] { 0x10 }; - private static byte[] Prefix_TokenOwner => new byte[] { 0x11 }; - private static byte[] Prefix_TokenBalance => new byte[] { 0x12 }; - private static byte[] Prefix_Properties => new byte[] { 0x13 }; - private static byte[] Prefix_TokensOf => new byte[] { 0x14 }; + + private const byte Prefix_TotalSupply = 10; + private const byte Prefix_TokenOwner = 11; + private const byte Prefix_TokenBalance = 12; + private const byte Prefix_Properties = 13; + private const byte Prefix_TokensOf = 14; private const int TOKEN_DECIMALS = 8; private const int FACTOR = 100_000_000; @@ -52,7 +53,7 @@ public static byte[] CreateStorageKey(byte[] prefix, byte[] key) public static BigInteger TotalSupply() { - return Storage.Get(Context(), Prefix_TotalSupply).ToBigInteger(); + return Storage.Get(Context(), Prefix_TotalSupply.ToByteArray()).ToBigInteger(); } public static int Decimals() @@ -62,18 +63,18 @@ public static int Decimals() public static Enumerator OwnerOf(byte[] tokenid) { - return Storage.Find(Context(), CreateStorageKey(Prefix_TokenOwner, tokenid)).Values; + return Storage.Find(Context(), CreateStorageKey(Prefix_TokenOwner.ToByteArray(), tokenid)).Values; } public static Enumerator TokensOf(byte[] owner) { if (owner.Length != 20) throw new FormatException("The parameter 'owner' should be 20-byte address."); - return Storage.Find(Context(), CreateStorageKey(Prefix_TokensOf, owner)).Values; + return Storage.Find(Context(), CreateStorageKey(Prefix_TokensOf.ToByteArray(), owner)).Values; } public static string Properties(byte[] tokenid) { - return Storage.Get(Context(), CreateStorageKey(Prefix_Properties, tokenid)).AsString(); + return Storage.Get(Context(), CreateStorageKey(Prefix_Properties.ToByteArray(), tokenid)).AsString(); } public static bool Mint(byte[] tokenId, byte[] owner, byte[] properties) @@ -83,24 +84,24 @@ public static bool Mint(byte[] tokenId, byte[] owner, byte[] properties) if (owner.Length != 20) throw new FormatException("The parameter 'owner' should be 20-byte address."); if (properties.Length > 2048) throw new FormatException("The length of 'properties' should be less than 2048."); - StorageMap tokenOwnerMap = Storage.CurrentContext.CreateMap(CreateStorageKey(Prefix_TokenOwner, tokenId)); + StorageMap tokenOwnerMap = Storage.CurrentContext.CreateMap(CreateStorageKey(Prefix_TokenOwner.ToByteArray(), tokenId)); if (tokenOwnerMap.Get(owner) != null) return false; - StorageMap tokenOfMap = Storage.CurrentContext.CreateMap(CreateStorageKey(Prefix_TokensOf, owner)); - Storage.Put(Context(), CreateStorageKey(Prefix_Properties, tokenId), properties); + StorageMap tokenOfMap = Storage.CurrentContext.CreateMap(CreateStorageKey(Prefix_TokensOf.ToByteArray(), owner)); + Storage.Put(Context(), CreateStorageKey(Prefix_Properties.ToByteArray(), tokenId), properties); tokenOwnerMap.Put(owner, owner); tokenOfMap.Put(tokenId, tokenId); - var totalSupply = Storage.Get(Context(), Prefix_TotalSupply); + var totalSupply = Storage.Get(Context(), Prefix_TotalSupply.ToByteArray()); if (totalSupply is null) - Storage.Put(Context(), Prefix_TotalSupply, 1); + Storage.Put(Context(), Prefix_TotalSupply.ToByteArray(), 1); else - Storage.Put(Context(), Prefix_TotalSupply, totalSupply.ToBigInteger() + 1); + Storage.Put(Context(), Prefix_TotalSupply.ToByteArray(), totalSupply.ToBigInteger() + 1); - StorageMap tokenBalanceMap = Storage.CurrentContext.CreateMap(CreateStorageKey(Prefix_TokenBalance, owner)); + StorageMap tokenBalanceMap = Storage.CurrentContext.CreateMap(CreateStorageKey(Prefix_TokenBalance.ToByteArray(), owner)); tokenBalanceMap.Put(tokenId, FACTOR); - //notify + // notify MintedToken(owner, tokenId, properties); return true; } @@ -110,14 +111,14 @@ public static BigInteger BalanceOf(byte[] owner, byte[] tokenid) if (owner.Length != 20) throw new FormatException("The parameter 'owner' should be 20-byte address."); if (tokenid is null) { - var iterator = Storage.Find(Context(), CreateStorageKey(Prefix_TokenBalance, owner)); + var iterator = Storage.Find(Context(), CreateStorageKey(Prefix_TokenBalance.ToByteArray(), owner)); BigInteger result = 0; while (iterator.Next()) result += iterator.Value.ToBigInteger(); return result; } else - return Storage.CurrentContext.CreateMap(CreateStorageKey(Prefix_TokenBalance, owner)).Get(tokenid).ToBigInteger(); + return Storage.CurrentContext.CreateMap(CreateStorageKey(Prefix_TokenBalance.ToByteArray(), owner)).Get(tokenid).ToBigInteger(); } public static bool Transfer(byte[] from, byte[] to, BigInteger amount, byte[] tokenId) @@ -132,11 +133,11 @@ public static bool Transfer(byte[] from, byte[] to, BigInteger amount, byte[] to return true; } - StorageMap fromTokenBalanceMap = Storage.CurrentContext.CreateMap(CreateStorageKey(Prefix_TokenBalance, from)); - StorageMap toTokenBalanceMap = Storage.CurrentContext.CreateMap(CreateStorageKey(Prefix_TokenBalance, to)); - StorageMap tokenOwnerMap = Storage.CurrentContext.CreateMap(CreateStorageKey(Prefix_TokenOwner, tokenId)); - StorageMap fromTokensOfMap = Storage.CurrentContext.CreateMap(CreateStorageKey(Prefix_TokensOf, from)); - StorageMap toTokensOfMap = Storage.CurrentContext.CreateMap(CreateStorageKey(Prefix_TokensOf, to)); + StorageMap fromTokenBalanceMap = Storage.CurrentContext.CreateMap(CreateStorageKey(Prefix_TokenBalance.ToByteArray(), from)); + StorageMap toTokenBalanceMap = Storage.CurrentContext.CreateMap(CreateStorageKey(Prefix_TokenBalance.ToByteArray(), to)); + StorageMap tokenOwnerMap = Storage.CurrentContext.CreateMap(CreateStorageKey(Prefix_TokenOwner.ToByteArray(), tokenId)); + StorageMap fromTokensOfMap = Storage.CurrentContext.CreateMap(CreateStorageKey(Prefix_TokensOf.ToByteArray(), from)); + StorageMap toTokensOfMap = Storage.CurrentContext.CreateMap(CreateStorageKey(Prefix_TokensOf.ToByteArray(), to)); var fromTokenBalance = fromTokenBalanceMap.Get(tokenId); if (fromTokenBalance == null || fromTokenBalance.ToBigInteger() < amount) return false; @@ -160,7 +161,7 @@ public static bool Transfer(byte[] from, byte[] to, BigInteger amount, byte[] to toTokenBalanceMap.Put(tokenId, toTokenBalance.ToBigInteger() + amount); } - //notify + // notify Transferred(from, to, amount, tokenId); return true; } From 35e1da38783dbafde95c1723b3a2048ced82a8f1 Mon Sep 17 00:00:00 2001 From: Shawn Date: Thu, 4 Jun 2020 17:13:32 +0800 Subject: [PATCH 17/21] fix supportStandards --- templates/Template.NFT.CSharp/NFTContract.cs | 4 ++-- tests/Template.NEP5.UnitTests/UnitTest_NFT.cs | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/templates/Template.NFT.CSharp/NFTContract.cs b/templates/Template.NFT.CSharp/NFTContract.cs index 638edb613..e30888fb0 100644 --- a/templates/Template.NFT.CSharp/NFTContract.cs +++ b/templates/Template.NFT.CSharp/NFTContract.cs @@ -41,9 +41,9 @@ public static string Symbol() return "MNFT"; } - public static string SupportedStandards() + public static string[] SupportedStandards() { - return "NEP-10, NEP-11"; + return new string[] { "NEP-10", "NEP-11" }; } public static byte[] CreateStorageKey(byte[] prefix, byte[] key) diff --git a/tests/Template.NEP5.UnitTests/UnitTest_NFT.cs b/tests/Template.NEP5.UnitTests/UnitTest_NFT.cs index b1da3e91c..81df2f41a 100644 --- a/tests/Template.NEP5.UnitTests/UnitTest_NFT.cs +++ b/tests/Template.NEP5.UnitTests/UnitTest_NFT.cs @@ -23,18 +23,34 @@ public void Init() _engine = new TestEngine(TriggerType.Application, new TestScriptContainer(), null); _engine.AddEntryScript("../../../../../templates/Template.NFT.CSharp/NFTContract.cs"); } + [TestMethod] public void Test_GetName() { + _engine.Reset(); var result = _engine.ExecuteTestCaseStandard("name").Pop(); StackItem wantResult = "MyNFT"; Assert.AreEqual(wantResult.ConvertTo(StackItemType.ByteString), result.ConvertTo(StackItemType.ByteString)); } + [TestMethod] + public void Test_GetStandards() + { + _engine.Reset(); + var result = (Array)_engine.ExecuteTestCaseStandard("supportedStandards").Pop(); + + Assert.AreEqual(result.Count, 2); + StackItem wantResult1 = "NEP-10"; + StackItem wantResult2 = "NEP-11"; + Assert.AreEqual(wantResult1.ConvertTo(StackItemType.ByteString), result[0]); + Assert.AreEqual(wantResult2.ConvertTo(StackItemType.ByteString), result[1]); + } + [TestMethod] public void Test_GetDecimals() { + _engine.Reset(); var result = _engine.ExecuteTestCaseStandard("decimals").Pop(); var wantResult = 8; From 2517b812cd18149471da14b61126c4f879733f3f Mon Sep 17 00:00:00 2001 From: Shawn Date: Thu, 4 Jun 2020 18:19:30 +0800 Subject: [PATCH 18/21] modify notify --- templates/Template.NFT.CSharp/NFTContract.cs | 18 +++++++++--------- tests/Template.NEP5.UnitTests/UnitTest_NFT.cs | 3 +-- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/templates/Template.NFT.CSharp/NFTContract.cs b/templates/Template.NFT.CSharp/NFTContract.cs index e30888fb0..18f8eb831 100644 --- a/templates/Template.NFT.CSharp/NFTContract.cs +++ b/templates/Template.NFT.CSharp/NFTContract.cs @@ -12,11 +12,11 @@ namespace NFTContract /// public class NFTContract : SmartContract { - [DisplayName("MintedToken")] - public static event Action MintedToken; + [DisplayName("MintToken")] + public static event Action MintTokenNotify; - [DisplayName("Transferred")] - public static event Action Transferred; + [DisplayName("Transfer")] + public static event Action TransferNotify; private static readonly byte[] superAdmin = Helper.ToScriptHash("Nj9Epc1x2sDmd6yH5qJPYwXRqSRf5X6KHE"); @@ -101,8 +101,8 @@ public static bool Mint(byte[] tokenId, byte[] owner, byte[] properties) StorageMap tokenBalanceMap = Storage.CurrentContext.CreateMap(CreateStorageKey(Prefix_TokenBalance.ToByteArray(), owner)); tokenBalanceMap.Put(tokenId, FACTOR); - // notify - MintedToken(owner, tokenId, properties); + // Notify + MintTokenNotify(owner, tokenId, properties); return true; } @@ -129,7 +129,7 @@ public static bool Transfer(byte[] from, byte[] to, BigInteger amount, byte[] to if (from.Equals(to)) { - Transferred(from, to, amount, tokenId); + Transfer(from, to, amount, tokenId); return true; } @@ -161,8 +161,8 @@ public static bool Transfer(byte[] from, byte[] to, BigInteger amount, byte[] to toTokenBalanceMap.Put(tokenId, toTokenBalance.ToBigInteger() + amount); } - // notify - Transferred(from, to, amount, tokenId); + // Notify + TransferNotify(from, to, amount, tokenId); return true; } diff --git a/tests/Template.NEP5.UnitTests/UnitTest_NFT.cs b/tests/Template.NEP5.UnitTests/UnitTest_NFT.cs index 81df2f41a..89446a0b4 100644 --- a/tests/Template.NEP5.UnitTests/UnitTest_NFT.cs +++ b/tests/Template.NEP5.UnitTests/UnitTest_NFT.cs @@ -4,7 +4,6 @@ using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.SmartContract.Enumerators; -using Neo.SmartContract.Iterators; using Neo.VM.Types; using System.IO; using System.Linq; @@ -40,7 +39,7 @@ public void Test_GetStandards() _engine.Reset(); var result = (Array)_engine.ExecuteTestCaseStandard("supportedStandards").Pop(); - Assert.AreEqual(result.Count, 2); + Assert.AreEqual(2, result.Count); StackItem wantResult1 = "NEP-10"; StackItem wantResult2 = "NEP-11"; Assert.AreEqual(wantResult1.ConvertTo(StackItemType.ByteString), result[0]); From 8c679ab3e9c36195fc28b7914acfca435c9b459c Mon Sep 17 00:00:00 2001 From: Shawn Date: Thu, 4 Jun 2020 18:20:53 +0800 Subject: [PATCH 19/21] modify notify --- templates/Template.NFT.CSharp/NFTContract.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/Template.NFT.CSharp/NFTContract.cs b/templates/Template.NFT.CSharp/NFTContract.cs index 18f8eb831..1c45a5998 100644 --- a/templates/Template.NFT.CSharp/NFTContract.cs +++ b/templates/Template.NFT.CSharp/NFTContract.cs @@ -129,7 +129,7 @@ public static bool Transfer(byte[] from, byte[] to, BigInteger amount, byte[] to if (from.Equals(to)) { - Transfer(from, to, amount, tokenId); + TransferNotify(from, to, amount, tokenId); return true; } From c92aa7f247e97f58794e51b95490d076aaeeec86 Mon Sep 17 00:00:00 2001 From: ShawnYun <42930111+ShawnYun@users.noreply.github.com> Date: Fri, 5 Jun 2020 10:07:46 +0800 Subject: [PATCH 20/21] Update templates/Template.NFT.CSharp/NFTContract.cs Co-authored-by: Shargon --- templates/Template.NFT.CSharp/NFTContract.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/Template.NFT.CSharp/NFTContract.cs b/templates/Template.NFT.CSharp/NFTContract.cs index 1c45a5998..b15e140df 100644 --- a/templates/Template.NFT.CSharp/NFTContract.cs +++ b/templates/Template.NFT.CSharp/NFTContract.cs @@ -33,7 +33,7 @@ public class NFTContract : SmartContract public static string Name() { - return "MyNFT"; + return "NFT"; } public static string Symbol() From f21664256674270b9e28eb7be088c4355321a6b1 Mon Sep 17 00:00:00 2001 From: Shawn Date: Tue, 16 Jun 2020 16:15:32 +0800 Subject: [PATCH 21/21] add burn and fix --- templates/Template.NFT.CSharp/NFTContract.cs | 31 +++++++++++++++++-- tests/Template.NEP5.UnitTests/UnitTest_NFT.cs | 21 +++++++++++-- 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/templates/Template.NFT.CSharp/NFTContract.cs b/templates/Template.NFT.CSharp/NFTContract.cs index b15e140df..8558c2512 100644 --- a/templates/Template.NFT.CSharp/NFTContract.cs +++ b/templates/Template.NFT.CSharp/NFTContract.cs @@ -15,6 +15,9 @@ public class NFTContract : SmartContract [DisplayName("MintToken")] public static event Action MintTokenNotify; + [DisplayName("BurnToken")] + public static event Action BurnTokenNotify; + [DisplayName("Transfer")] public static event Action TransferNotify; @@ -38,7 +41,7 @@ public static string Name() public static string Symbol() { - return "MNFT"; + return "NFT"; } public static string[] SupportedStandards() @@ -94,9 +97,9 @@ public static bool Mint(byte[] tokenId, byte[] owner, byte[] properties) var totalSupply = Storage.Get(Context(), Prefix_TotalSupply.ToByteArray()); if (totalSupply is null) - Storage.Put(Context(), Prefix_TotalSupply.ToByteArray(), 1); + Storage.Put(Context(), Prefix_TotalSupply.ToByteArray(), FACTOR); else - Storage.Put(Context(), Prefix_TotalSupply.ToByteArray(), totalSupply.ToBigInteger() + 1); + Storage.Put(Context(), Prefix_TotalSupply.ToByteArray(), totalSupply.ToBigInteger() + FACTOR); StorageMap tokenBalanceMap = Storage.CurrentContext.CreateMap(CreateStorageKey(Prefix_TokenBalance.ToByteArray(), owner)); tokenBalanceMap.Put(tokenId, FACTOR); @@ -106,6 +109,28 @@ public static bool Mint(byte[] tokenId, byte[] owner, byte[] properties) return true; } + public static bool Burn(byte[] tokenId, byte[] owner, BigInteger amount) + { + if (owner.Length != 20) throw new FormatException("The parameters 'owner' should be 20-byte addresses."); + if (amount < 0 || amount > FACTOR) throw new FormatException("The parameters 'amount' is out of range."); + if (amount == 0) return true; + if (!Runtime.CheckWitness(owner)) return false; + + var tokenBalanceMap = Storage.CurrentContext.CreateMap(CreateStorageKey(Prefix_TokenBalance.ToByteArray(), owner)); + var balance = tokenBalanceMap.Get(tokenId).ToBigInteger(); + if (balance < amount) return false; + + var totalSupply = Storage.Get(Context(), Prefix_TotalSupply.ToByteArray()).ToBigInteger(); + balance -= amount; + totalSupply -= amount; + tokenBalanceMap.Put(tokenId, balance); + Storage.Put(Context(), Prefix_TotalSupply.ToByteArray(), totalSupply); + + // Notify + BurnTokenNotify(owner, tokenId, amount); + return true; + } + public static BigInteger BalanceOf(byte[] owner, byte[] tokenid) { if (owner.Length != 20) throw new FormatException("The parameter 'owner' should be 20-byte address."); diff --git a/tests/Template.NEP5.UnitTests/UnitTest_NFT.cs b/tests/Template.NEP5.UnitTests/UnitTest_NFT.cs index 89446a0b4..e8371f514 100644 --- a/tests/Template.NEP5.UnitTests/UnitTest_NFT.cs +++ b/tests/Template.NEP5.UnitTests/UnitTest_NFT.cs @@ -29,7 +29,7 @@ public void Test_GetName() _engine.Reset(); var result = _engine.ExecuteTestCaseStandard("name").Pop(); - StackItem wantResult = "MyNFT"; + StackItem wantResult = "NFT"; Assert.AreEqual(wantResult.ConvertTo(StackItemType.ByteString), result.ConvertTo(StackItemType.ByteString)); } @@ -82,7 +82,7 @@ public void Test_MintNFT() _engine.Reset(); result = _engine.ExecuteTestCaseStandard("totalSupply").Pop(); - var wantTotalSupply = 1; + var wantTotalSupply = BigInteger.Pow(10, 8); Assert.AreEqual(wantTotalSupply, result); _engine.Reset(); @@ -108,7 +108,7 @@ public void Test_MintNFT() _engine.Reset(); result = _engine.ExecuteTestCaseStandard("totalSupply").Pop(); - wantTotalSupply = 2; + wantTotalSupply = 2 * BigInteger.Pow(10, 8); Assert.AreEqual(wantTotalSupply, result); // Balance of all @@ -159,6 +159,21 @@ public void Test_MintNFT() enumerator.Next(); v2 = enumerator.Value(); Assert.AreEqual(new ByteString(tokenid2), v2); + + // BurnToken + _engine.Reset(); + result = _engine.ExecuteTestCaseStandard("burn", tokenid2, owner, BigInteger.Pow(10, 7)).Pop(); + Assert.AreEqual(true, result.ConvertTo(StackItemType.Boolean)); + + _engine.Reset(); + result = _engine.ExecuteTestCaseStandard("balanceOf", owner, tokenid2).Pop(); + wantBalance = 8 * BigInteger.Pow(10, 7); + Assert.AreEqual(wantBalance, result); + + _engine.Reset(); + result = _engine.ExecuteTestCaseStandard("totalSupply").Pop(); + wantBalance = 19 * BigInteger.Pow(10, 7); + Assert.AreEqual(wantBalance, result); } }