diff --git a/src/neo/SmartContract/Manifest/ContractManifest.cs b/src/neo/SmartContract/Manifest/ContractManifest.cs
index ff57c1e3c4..b88260272d 100644
--- a/src/neo/SmartContract/Manifest/ContractManifest.cs
+++ b/src/neo/SmartContract/Manifest/ContractManifest.cs
@@ -34,6 +34,11 @@ public int Size
///
public UInt160 Hash => Abi.Hash;
+ ///
+ /// Contract name
+ ///
+ public string Name { get; set; }
+
///
/// A group represents a set of mutually trusted contracts. A contract will trust and allow any contract in the same group to invoke it, and the user interface will not give any warnings.
///
@@ -110,6 +115,7 @@ public JObject ToJson()
{
return new JObject
{
+ ["name"] = Name,
["groups"] = Groups.Select(u => u.ToJson()).ToArray(),
["supportedstandards"] = SupportedStandards.Select(u => new JString(u)).ToArray(),
["abi"] = Abi.ToJson(),
@@ -128,6 +134,7 @@ public ContractManifest Clone()
{
return new ContractManifest
{
+ Name = Name,
Groups = Groups.Select(p => p.Clone()).ToArray(),
SupportedStandards = SupportedStandards[..],
Abi = Abi.Clone(),
@@ -156,6 +163,7 @@ public void Deserialize(BinaryReader reader)
private void DeserializeFromJson(JObject json)
{
+ Name = json["name"].AsString();
Groups = ((JArray)json["groups"]).Select(u => ContractGroup.FromJson(u)).ToArray();
SupportedStandards = ((JArray)json["supportedstandards"]).Select(u => u.AsString()).ToArray();
Abi = ContractAbi.FromJson(json["abi"]);
diff --git a/src/neo/SmartContract/Native/NativeContract.cs b/src/neo/SmartContract/Native/NativeContract.cs
index ae670d74a2..e08b8a6954 100644
--- a/src/neo/SmartContract/Native/NativeContract.cs
+++ b/src/neo/SmartContract/Native/NativeContract.cs
@@ -62,6 +62,7 @@ protected NativeContract()
}
this.Manifest = new ContractManifest
{
+ Name = Name,
Groups = System.Array.Empty(),
SupportedStandards = new string[0],
Abi = new ContractAbi()
diff --git a/src/neo/SmartContract/Native/Tokens/GasToken.cs b/src/neo/SmartContract/Native/Tokens/GasToken.cs
index c3a6ed08f4..5bec05f471 100644
--- a/src/neo/SmartContract/Native/Tokens/GasToken.cs
+++ b/src/neo/SmartContract/Native/Tokens/GasToken.cs
@@ -4,7 +4,7 @@
namespace Neo.SmartContract.Native.Tokens
{
- public sealed class GasToken : Nep5Token
+ public sealed class GasToken : Nep17Token
{
public override int Id => -2;
public override string Name => "GAS";
diff --git a/src/neo/SmartContract/Native/Tokens/NeoToken.cs b/src/neo/SmartContract/Native/Tokens/NeoToken.cs
index 956f78d481..1204d39be6 100644
--- a/src/neo/SmartContract/Native/Tokens/NeoToken.cs
+++ b/src/neo/SmartContract/Native/Tokens/NeoToken.cs
@@ -15,7 +15,7 @@
namespace Neo.SmartContract.Native.Tokens
{
- public sealed class NeoToken : Nep5Token
+ public sealed class NeoToken : Nep17Token
{
public override int Id => -1;
public override string Name => "NEO";
diff --git a/src/neo/SmartContract/Native/Tokens/Nep5Token.cs b/src/neo/SmartContract/Native/Tokens/Nep17Token.cs
similarity index 86%
rename from src/neo/SmartContract/Native/Tokens/Nep5Token.cs
rename to src/neo/SmartContract/Native/Tokens/Nep17Token.cs
index a2337b1c25..c216908844 100644
--- a/src/neo/SmartContract/Native/Tokens/Nep5Token.cs
+++ b/src/neo/SmartContract/Native/Tokens/Nep17Token.cs
@@ -10,7 +10,7 @@
namespace Neo.SmartContract.Native.Tokens
{
- public abstract class Nep5Token : NativeContract
+ public abstract class Nep17Token : NativeContract
where TState : AccountState, new()
{
[ContractMethod(0, CallFlags.None)]
@@ -22,11 +22,11 @@ public abstract class Nep5Token : NativeContract
protected const byte Prefix_TotalSupply = 11;
protected const byte Prefix_Account = 20;
- protected Nep5Token()
+ protected Nep17Token()
{
this.Factor = BigInteger.Pow(10, Decimals);
- Manifest.SupportedStandards = new[] { "NEP-5" };
+ Manifest.SupportedStandards = new[] { "NEP-17" };
var events = new List(Manifest.Abi.Events)
{
@@ -67,7 +67,7 @@ internal protected virtual void Mint(ApplicationEngine engine, UInt160 account,
state.Balance += amount;
storage = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_TotalSupply), () => new StorageItem(BigInteger.Zero));
storage.Add(amount);
- engine.SendNotification(Hash, "Transfer", new Array { StackItem.Null, account.ToArray(), amount });
+ PostTransfer(engine, null, account, amount);
}
internal protected virtual void Burn(ApplicationEngine engine, UInt160 account, BigInteger amount)
@@ -85,7 +85,7 @@ internal protected virtual void Burn(ApplicationEngine engine, UInt160 account,
state.Balance -= amount;
storage = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_TotalSupply));
storage.Add(-amount);
- engine.SendNotification(Hash, "Transfer", new Array { account.ToArray(), StackItem.Null, amount });
+ PostTransfer(engine, account, null, amount);
}
[ContractMethod(0_01000000, CallFlags.AllowStates)]
@@ -104,7 +104,7 @@ public virtual BigInteger BalanceOf(StoreView snapshot, UInt160 account)
return storage.GetInteroperable().Balance;
}
- [ContractMethod(0_08000000, CallFlags.AllowModifyStates)]
+ [ContractMethod(0_09000000, CallFlags.AllowModifyStates)]
protected virtual bool Transfer(ApplicationEngine engine, UInt160 from, UInt160 to, BigInteger amount)
{
if (amount.Sign < 0) throw new ArgumentOutOfRangeException(nameof(amount));
@@ -143,12 +143,28 @@ protected virtual bool Transfer(ApplicationEngine engine, UInt160 from, UInt160
state_to.Balance += amount;
}
}
- engine.SendNotification(Hash, "Transfer", new Array { from.ToArray(), to.ToArray(), amount });
+ PostTransfer(engine, from, to, amount);
return true;
}
protected virtual void OnBalanceChanging(ApplicationEngine engine, UInt160 account, TState state, BigInteger amount)
{
}
+
+ private void PostTransfer(ApplicationEngine engine, UInt160 from, UInt160 to, BigInteger amount)
+ {
+ // Send notification
+
+ engine.SendNotification(Hash, "Transfer",
+ new Array { from?.ToArray() ?? StackItem.Null, to?.ToArray() ?? StackItem.Null, amount });
+
+ // Check if it's a wallet or smart contract
+
+ if (to is null || engine.Snapshot.Contracts.TryGet(to) is null) return;
+
+ // Call onPayment method if exists (NEP-17)
+
+ engine.CallFromNativeContract(() => { }, to, "onPayment", from.ToArray(), amount);
+ }
}
}
diff --git a/src/neo/Wallets/AssetDescriptor.cs b/src/neo/Wallets/AssetDescriptor.cs
index 9f6a4d4c18..35c80ea82c 100644
--- a/src/neo/Wallets/AssetDescriptor.cs
+++ b/src/neo/Wallets/AssetDescriptor.cs
@@ -1,3 +1,5 @@
+using Neo.Ledger;
+using Neo.Persistence;
using Neo.SmartContract;
using Neo.VM;
using System;
@@ -12,17 +14,20 @@ public class AssetDescriptor
public AssetDescriptor(UInt160 asset_id)
{
+ using SnapshotView snapshot = Blockchain.Singleton.GetSnapshot();
+ var contract = snapshot.Contracts.TryGet(asset_id);
+ if (contract is null) throw new ArgumentException();
+
byte[] script;
using (ScriptBuilder sb = new ScriptBuilder())
{
sb.EmitAppCall(asset_id, "decimals");
- sb.EmitAppCall(asset_id, "name");
script = sb.ToArray();
}
- using ApplicationEngine engine = ApplicationEngine.Run(script, gas: 3_000_000);
+ using ApplicationEngine engine = ApplicationEngine.Run(script, snapshot, gas: 0_02000000);
if (engine.State.HasFlag(VMState.FAULT)) throw new ArgumentException();
this.AssetId = asset_id;
- this.AssetName = engine.ResultStack.Pop().GetString();
+ this.AssetName = contract.Manifest.Name;
this.Decimals = (byte)engine.ResultStack.Pop().GetInteger();
}
diff --git a/tests/neo.UnitTests/Ledger/UT_ContractState.cs b/tests/neo.UnitTests/Ledger/UT_ContractState.cs
index 8fe1930adf..19bcda402e 100644
--- a/tests/neo.UnitTests/Ledger/UT_ContractState.cs
+++ b/tests/neo.UnitTests/Ledger/UT_ContractState.cs
@@ -72,7 +72,7 @@ public void TestDeserialize()
public void TestGetSize()
{
ISerializable newContract = contract;
- newContract.Size.Should().Be(218);
+ newContract.Size.Should().Be(240);
}
[TestMethod]
diff --git a/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs b/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs
index 44c621870f..c4b15df53d 100644
--- a/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs
+++ b/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs
@@ -11,7 +11,7 @@ public class UT_ContractManifest
[TestMethod]
public void ParseFromJson_Default()
{
- var json = @"{""groups"":[],""supportedstandards"":[],""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""safemethods"":[],""extra"":null}";
+ var json = @"{""name"":""testManifest"",""groups"":[],""supportedstandards"":[],""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""safemethods"":[],""extra"":null}";
var manifest = ContractManifest.Parse(json);
Assert.AreEqual(manifest.ToString(), json);
@@ -22,7 +22,7 @@ public void ParseFromJson_Default()
[TestMethod]
public void ParseFromJson_Permissions()
{
- var json = @"{""groups"":[],""supportedstandards"":[],""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""methods"":[],""events"":[]},""permissions"":[{""contract"":""0x0000000000000000000000000000000000000000"",""methods"":[""method1"",""method2""]}],""trusts"":[],""safemethods"":[],""extra"":null}";
+ var json = @"{""name"":""testManifest"",""groups"":[],""supportedstandards"":[],""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""methods"":[],""events"":[]},""permissions"":[{""contract"":""0x0000000000000000000000000000000000000000"",""methods"":[""method1"",""method2""]}],""trusts"":[],""safemethods"":[],""extra"":null}";
var manifest = ContractManifest.Parse(json);
Assert.AreEqual(manifest.ToString(), json);
@@ -41,7 +41,7 @@ public void ParseFromJson_Permissions()
[TestMethod]
public void ParseFromJson_SafeMethods()
{
- var json = @"{""groups"":[],""supportedstandards"":[],""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""safemethods"":[""balanceOf""],""extra"":null}";
+ var json = @"{""name"":""testManifest"",""groups"":[],""supportedstandards"":[],""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""safemethods"":[""balanceOf""],""extra"":null}";
var manifest = ContractManifest.Parse(json);
Assert.AreEqual(manifest.ToString(), json);
@@ -53,7 +53,7 @@ public void ParseFromJson_SafeMethods()
[TestMethod]
public void ParseFromJson_Trust()
{
- var json = @"{""groups"":[],""supportedstandards"":[],""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[""0x0000000000000000000000000000000000000001""],""safemethods"":[],""extra"":null}";
+ var json = @"{""name"":""testManifest"",""groups"":[],""supportedstandards"":[],""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[""0x0000000000000000000000000000000000000001""],""safemethods"":[],""extra"":null}";
var manifest = ContractManifest.Parse(json);
Assert.AreEqual(manifest.ToString(), json);
@@ -65,7 +65,7 @@ public void ParseFromJson_Trust()
[TestMethod]
public void ParseFromJson_Groups()
{
- var json = @"{""groups"":[{""pubkey"":""03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"",""signature"":""QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQQ==""}],""supportedstandards"":[],""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""safemethods"":[],""extra"":null}";
+ var json = @"{""name"":""testManifest"",""groups"":[{""pubkey"":""03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"",""signature"":""QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQQ==""}],""supportedstandards"":[],""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""safemethods"":[],""extra"":null}";
var manifest = ContractManifest.Parse(json);
Assert.AreEqual(manifest.ToString(), json);
@@ -77,7 +77,7 @@ public void ParseFromJson_Groups()
[TestMethod]
public void ParseFromJson_Extra()
{
- var json = @"{""groups"":[],""supportedstandards"":[],""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""safemethods"":[],""extra"":{""key"":""value""}}";
+ var json = @"{""name"":""testManifest"",""groups"":[],""supportedstandards"":[],""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""safemethods"":[],""extra"":{""key"":""value""}}";
var manifest = ContractManifest.Parse(json);
Assert.AreEqual(json, json);
Assert.AreEqual("value", manifest.Extra["key"].AsString(), false);
@@ -110,7 +110,7 @@ public void TestGetHash()
public void TestGetSize()
{
var temp = TestUtils.CreateDefaultManifest(UInt160.Zero);
- Assert.AreEqual(212, temp.Size);
+ Assert.AreEqual(234, temp.Size);
}
[TestMethod]
diff --git a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep17Token.cs
similarity index 84%
rename from tests/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs
rename to tests/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep17Token.cs
index 57b2a25491..0728f5d50a 100644
--- a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs
+++ b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep17Token.cs
@@ -9,7 +9,7 @@
namespace Neo.UnitTests.SmartContract.Native.Tokens
{
[TestClass]
- public class UT_Nep5Token : TestKit
+ public class UT_Nep17Token : TestKit
{
[TestInitialize]
public void TestSetup()
@@ -18,7 +18,13 @@ public void TestSetup()
}
protected const byte Prefix_TotalSupply = 11;
- private static readonly TestNep5Token test = new TestNep5Token();
+ private static readonly TestNep17Token test = new TestNep17Token();
+
+ [TestMethod]
+ public void TestName()
+ {
+ Assert.AreEqual(test.Name, test.Manifest.Name);
+ }
[TestMethod]
public void TestTotalSupply()
@@ -71,11 +77,11 @@ public StorageKey CreateStorageKey(byte prefix, byte[] key = null)
}
}
- public class TestNep5Token : Nep5Token
+ public class TestNep17Token : Nep17Token
{
public override int Id => 0x10000005;
- public override string Name => "testNep5Token";
+ public override string Name => "testNep17Token";
public override string Symbol => throw new NotImplementedException();
diff --git a/tests/neo.UnitTests/TestUtils.cs b/tests/neo.UnitTests/TestUtils.cs
index 2a4cb10cc3..e1d78738d3 100644
--- a/tests/neo.UnitTests/TestUtils.cs
+++ b/tests/neo.UnitTests/TestUtils.cs
@@ -22,6 +22,7 @@ public static ContractManifest CreateDefaultManifest(UInt160 hash)
{
return new ContractManifest()
{
+ Name = "testManifest",
Groups = new ContractGroup[0],
SupportedStandards = Array.Empty(),
Abi = new ContractAbi()
diff --git a/tests/neo.UnitTests/Wallets/UT_AssetDescriptor.cs b/tests/neo.UnitTests/Wallets/UT_AssetDescriptor.cs
index 937a0d89f7..bf9fbbae25 100644
--- a/tests/neo.UnitTests/Wallets/UT_AssetDescriptor.cs
+++ b/tests/neo.UnitTests/Wallets/UT_AssetDescriptor.cs
@@ -1,6 +1,5 @@
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Neo.Persistence;
using Neo.SmartContract.Native;
using System;