Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement NEP-17 #2024

Merged
merged 41 commits into from
Nov 18, 2020
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
c937be2
If exists
shargon Oct 26, 2020
a374286
Call onPayment if to it's a smart contract
shargon Oct 27, 2020
427a595
Increase cost in transfer
shargon Oct 27, 2020
1da0df6
Merge branch 'master' into allow-call-if-exists
shargon Oct 27, 2020
153c250
Remove Mint check
shargon Oct 27, 2020
8179ae3
Merge remote-tracking branch 'origin/allow-call-if-exists' into allow…
shargon Oct 27, 2020
2d909a0
Merge branch 'master' into allow-call-if-exists
shargon Oct 28, 2020
bbffc39
return
shargon Oct 28, 2020
772b566
Remove extra args
shargon Oct 28, 2020
ec0264a
Drop result
shargon Oct 29, 2020
7b59c50
Merge branch 'master' into allow-call-if-exists
shargon Oct 29, 2020
a87d7f3
Clean code
shargon Oct 29, 2020
ec77bc9
Merge remote-tracking branch 'origin/allow-call-if-exists' into allow…
shargon Oct 29, 2020
5074b27
Merge branch 'master' into allow-call-if-exists
shargon Nov 4, 2020
fde07e8
Method.Exists
shargon Nov 4, 2020
849ce9a
Merge remote-tracking branch 'origin/allow-call-if-exists' into allow…
shargon Nov 4, 2020
2f4ae86
Rename
erikzhang Nov 5, 2020
9bb5e85
protected
erikzhang Nov 5, 2020
fb0128e
Update ApplicationEngine.Contract.cs
erikzhang Nov 5, 2020
a4e9e5b
Merge branch 'master' into allow-call-if-exists
shargon Nov 10, 2020
259be1e
Fix merge
shargon Nov 10, 2020
7aef12a
Merge branch 'master' into allow-call-if-exists
shargon Nov 12, 2020
41a0871
Add Name in Extra
shargon Nov 12, 2020
93c4cfd
Name in manifest
shargon Nov 12, 2020
2d6d40a
Merge remote-tracking branch 'neo-project/master' into allow-call-if-…
shargon Nov 12, 2020
af57798
Fix UT
shargon Nov 12, 2020
6a03132
dotnet format
shargon Nov 12, 2020
dfb2f06
Remove Method.Exists
shargon Nov 13, 2020
202c005
Clean code
shargon Nov 13, 2020
de0fabb
Move filed `Name`
erikzhang Nov 15, 2020
d43e7ee
Rename
erikzhang Nov 15, 2020
ce9876b
Update null checks
erikzhang Nov 15, 2020
5f7630d
Fix CallFromNativeContract parameters
erikzhang Nov 15, 2020
b774259
Update AssetDescriptor.cs
erikzhang Nov 15, 2020
a75d5f0
Fix UT
erikzhang Nov 15, 2020
6734e60
format
erikzhang Nov 15, 2020
06517a5
Shargon's suggestion
erikzhang Nov 17, 2020
d33ff2f
Update src/neo/SmartContract/Native/Tokens/Nep17Token.cs
erikzhang Nov 18, 2020
93f6f5c
Merge branch 'master' into allow-call-if-exists
erikzhang Nov 18, 2020
134e024
Fix
erikzhang Nov 18, 2020
fff5001
Merge branch 'master' into allow-call-if-exists
erikzhang Nov 18, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/neo/SmartContract/Manifest/ContractManifest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ public int Size
/// </summary>
public UInt160 Hash => Abi.Hash;

/// <summary>
/// Contract name
/// </summary>
public string Name { get; set; }

/// <summary>
/// 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.
/// </summary>
Expand Down Expand Up @@ -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(),
Expand All @@ -128,6 +134,7 @@ public ContractManifest Clone()
{
return new ContractManifest
{
Name = Name,
Groups = Groups.Select(p => p.Clone()).ToArray(),
SupportedStandards = SupportedStandards[..],
Abi = Abi.Clone(),
Expand Down Expand Up @@ -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"]);
Expand Down
1 change: 1 addition & 0 deletions src/neo/SmartContract/Native/NativeContract.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ protected NativeContract()
}
this.Manifest = new ContractManifest
{
Name = Name,
Groups = System.Array.Empty<ContractGroup>(),
SupportedStandards = new string[0],
Abi = new ContractAbi()
Expand Down
2 changes: 1 addition & 1 deletion src/neo/SmartContract/Native/Tokens/GasToken.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace Neo.SmartContract.Native.Tokens
{
public sealed class GasToken : Nep5Token<AccountState>
public sealed class GasToken : Nep17Token<AccountState>
{
public override int Id => -2;
public override string Name => "GAS";
erikzhang marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
2 changes: 1 addition & 1 deletion src/neo/SmartContract/Native/Tokens/NeoToken.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

namespace Neo.SmartContract.Native.Tokens
{
public sealed class NeoToken : Nep5Token<NeoToken.NeoAccountState>
public sealed class NeoToken : Nep17Token<NeoToken.NeoAccountState>
{
public override int Id => -1;
public override string Name => "NEO";
erikzhang marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

namespace Neo.SmartContract.Native.Tokens
{
public abstract class Nep5Token<TState> : NativeContract
public abstract class Nep17Token<TState> : NativeContract
where TState : AccountState, new()
{
[ContractMethod(0, CallFlags.None)]
Expand All @@ -22,11 +22,11 @@ public abstract class Nep5Token<TState> : 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" };
erikzhang marked this conversation as resolved.
Show resolved Hide resolved

var events = new List<ContractEventDescriptor>(Manifest.Abi.Events)
{
Expand Down Expand Up @@ -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)
Expand All @@ -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)]
Expand All @@ -104,7 +104,7 @@ public virtual BigInteger BalanceOf(StoreView snapshot, UInt160 account)
return storage.GetInteroperable<TState>().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));
Expand Down Expand Up @@ -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", amount);
erikzhang marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
11 changes: 8 additions & 3 deletions src/neo/Wallets/AssetDescriptor.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using Neo.Ledger;
using Neo.Persistence;
using Neo.SmartContract;
using Neo.VM;
using System;
Expand All @@ -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: 3_000_000);
erikzhang marked this conversation as resolved.
Show resolved Hide resolved
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();
}

Expand Down
2 changes: 1 addition & 1 deletion tests/neo.UnitTests/Ledger/UT_ContractState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public void TestDeserialize()
public void TestGetSize()
{
ISerializable newContract = contract;
newContract.Size.Should().Be(218);
newContract.Size.Should().Be(240);
}

[TestMethod]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);

Expand All @@ -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);

Expand All @@ -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);

Expand All @@ -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);

Expand All @@ -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);
Expand Down Expand Up @@ -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]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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()
Expand Down Expand Up @@ -71,11 +77,11 @@ public StorageKey CreateStorageKey(byte prefix, byte[] key = null)
}
}

public class TestNep5Token : Nep5Token<NeoToken.NeoAccountState>
public class TestNep17Token : Nep17Token<NeoToken.NeoAccountState>
{
public override int Id => 0x10000005;

public override string Name => "testNep5Token";
public override string Name => "testNep17Token";

public override string Symbol => throw new NotImplementedException();

Expand Down
1 change: 1 addition & 0 deletions tests/neo.UnitTests/TestUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public static ContractManifest CreateDefaultManifest(UInt160 hash)
{
return new ContractManifest()
{
Name = "testManifest",
Groups = new ContractGroup[0],
SupportedStandards = Array.Empty<string>(),
Abi = new ContractAbi()
Expand Down
1 change: 0 additions & 1 deletion tests/neo.UnitTests/Wallets/UT_AssetDescriptor.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Neo.Persistence;
using Neo.SmartContract.Native;
using System;

Expand Down