-
Notifications
You must be signed in to change notification settings - Fork 102
/
Copy pathNep11Token.cs
149 lines (133 loc) · 5.52 KB
/
Nep11Token.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
// Copyright (C) 2015-2024 The Neo Project.
//
// Nep11Token.cs file belongs to the neo project and is free
// software distributed under the MIT software license, see the
// accompanying file LICENSE in the main directory of the
// repository or http://www.opensource.org/licenses/mit-license.php
// for more details.
//
// Redistribution and use in source and binary forms with or without
// modifications are permitted.
using Neo.SmartContract.Framework.Attributes;
using Neo.SmartContract.Framework.Native;
using Neo.SmartContract.Framework.Services;
using System;
using System.ComponentModel;
using System.Numerics;
namespace Neo.SmartContract.Framework
{
[SupportedStandards(NepStandard.Nep11)]
[ContractPermission(Permission.Any, Method.OnNEP11Payment)]
public abstract class Nep11Token<TokenState> : TokenContract
where TokenState : Nep11TokenState
{
public delegate void OnTransferDelegate(UInt160 from, UInt160 to, BigInteger amount, ByteString tokenId);
[DisplayName("Transfer")]
public static event OnTransferDelegate OnTransfer;
protected const byte Prefix_TokenId = 0x02;
protected const byte Prefix_Token = 0x03;
protected const byte Prefix_AccountToken = 0x04;
public sealed override byte Decimals
{
[Safe]
get => 0;
}
[Safe]
public static UInt160 OwnerOf(ByteString tokenId)
{
if (tokenId.Length > 64) throw new Exception("The argument \"tokenId\" should be 64 or less bytes long.");
var tokenMap = new StorageMap(Prefix_Token);
var tokenKey = tokenMap[tokenId] ?? throw new Exception("The token with given \"tokenId\" does not exist.");
TokenState token = (TokenState)StdLib.Deserialize(tokenKey);
return token.Owner;
}
[Safe]
public virtual Map<string, object> Properties(ByteString tokenId)
{
var tokenMap = new StorageMap(Prefix_Token);
TokenState token = (TokenState)StdLib.Deserialize(tokenMap[tokenId]);
return new Map<string, object>()
{
["name"] = token.Name
};
}
[Safe]
public static Iterator Tokens()
{
var tokenMap = new StorageMap(Prefix_Token);
return tokenMap.Find(FindOptions.KeysOnly | FindOptions.RemovePrefix);
}
[Safe]
public static Iterator TokensOf(UInt160 owner)
{
if (!owner.IsValid)
throw new Exception("The argument \"owner\" is invalid");
var accountMap = new StorageMap(Prefix_AccountToken);
return accountMap.Find(owner, FindOptions.KeysOnly | FindOptions.RemovePrefix);
}
public static bool Transfer(UInt160 to, ByteString tokenId, object data)
{
if (to is null || !to.IsValid)
throw new Exception("The argument \"to\" is invalid.");
var tokenMap = new StorageMap(Prefix_Token);
TokenState token = (TokenState)StdLib.Deserialize(tokenMap[tokenId]);
UInt160 from = token.Owner;
if (!Runtime.CheckWitness(from)) return false;
if (from != to)
{
token.Owner = to;
tokenMap[tokenId] = StdLib.Serialize(token);
UpdateBalance(from, tokenId, -1);
UpdateBalance(to, tokenId, +1);
}
PostTransfer(from, to, tokenId, data);
return true;
}
protected static ByteString NewTokenId()
{
return NewTokenId(Runtime.ExecutingScriptHash);
}
protected static ByteString NewTokenId(ByteString salt)
{
StorageContext context = Storage.CurrentContext;
byte[] key = new byte[] { Prefix_TokenId };
ByteString id = Storage.Get(context, key);
Storage.Put(context, key, (BigInteger)id + 1);
if (id is not null) salt += id;
return CryptoLib.Sha256(salt);
}
protected static void Mint(ByteString tokenId, TokenState token)
{
StorageMap tokenMap = new(Storage.CurrentContext, Prefix_Token);
tokenMap[tokenId] = StdLib.Serialize(token);
UpdateBalance(token.Owner, tokenId, +1);
TotalSupply++;
PostTransfer(null, token.Owner, tokenId, null);
}
protected static void Burn(ByteString tokenId)
{
StorageMap tokenMap = new(Storage.CurrentContext, Prefix_Token);
TokenState token = (TokenState)StdLib.Deserialize(tokenMap[tokenId]);
tokenMap.Delete(tokenId);
UpdateBalance(token.Owner, tokenId, -1);
TotalSupply--;
PostTransfer(token.Owner, null, tokenId, null);
}
protected static void UpdateBalance(UInt160 owner, ByteString tokenId, int increment)
{
UpdateBalance(owner, increment);
StorageMap accountMap = new(Storage.CurrentContext, Prefix_AccountToken);
ByteString key = owner + tokenId;
if (increment > 0)
accountMap.Put(key, 0);
else
accountMap.Delete(key);
}
protected static void PostTransfer(UInt160 from, UInt160 to, ByteString tokenId, object data)
{
OnTransfer(from, to, 1, tokenId);
if (to is not null && ContractManagement.GetContract(to) is not null)
Contract.Call(to, "onNEP11Payment", CallFlags.All, from, 1, tokenId, data);
}
}
}