-
Notifications
You must be signed in to change notification settings - Fork 104
Expand file tree
/
Copy pathLoot.cs
More file actions
213 lines (190 loc) · 9.15 KB
/
Loot.cs
File metadata and controls
213 lines (190 loc) · 9.15 KB
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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
// Copyright (C) 2015-2026 The Neo Project.
//
// Loot.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;
using Neo.SmartContract.Framework.Attributes;
using Neo.SmartContract.Framework.Native;
using Neo.SmartContract.Framework.Services;
using System;
using System.ComponentModel;
using System.Numerics;
using System.Runtime.CompilerServices;
namespace NFT
{
[DisplayName("SampleLootNFT")]
[ContractAuthor("core-dev", "dev@neo.org")]
[ContractDescription("This is a text Example.SmartContract.NFT")]
[SupportedStandards(NepStandard.Nep11)]
[ContractPermission(Permission.Any, Method.OnNEP11Payment)]
[ContractSourceCode("https://github.com/neo-project/neo-devpack-dotnet/tree/master/examples/")]
public partial class Loot : Nep11Token<TokenState>
{
public override string Symbol { [Safe] get => "sLoot"; }
private static readonly LocalStorageMap TokenIndexMap = new((byte)StoragePrefix.Token);
private static readonly LocalStorageMap TokenMap = new((byte)StoragePrefix.Token);
public static event Action<string> EventMsg;
[Safe]
public override Map<string, object> Properties(ByteString tokenId)
{
ExecutionEngine.Assert(Runtime.EntryScriptHash == Runtime.CallingScriptHash);
LocalStorageMap tokenMap = new((byte)StoragePrefix.Token);
var token = (TokenState)StdLib.Deserialize(tokenMap[tokenId]);
Map<string, object> map = new()
{
["name"] = token.Name,
["owner"] = token.Owner,
["tokenID"] = token.TokenId,
["credential"] = token.Credential
};
return map;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private TokenState GetToken(BigInteger tokenId)
{
var token = (TokenState)StdLib.Deserialize(TokenMap[tokenId.ToString()]);
ExecutionEngine.Assert(token is not null, "Token not exists");
return token;
}
[Safe]
public BigInteger GetCredential(BigInteger tokenId) => GetToken(tokenId).Credential;
[Safe]
public string GetWeapon(BigInteger credential) => Pluck(credential, 0xd7a595, _weapons);
[Safe]
public string GetChest(BigInteger credential) => Pluck(credential, 0x5a7e36, _chestArmor);
[Safe]
public string GetHead(BigInteger credential) => Pluck(credential, 0x0cdfbb, _headArmor);
[Safe]
public string GetWaist(BigInteger credential) => Pluck(credential, 0x7dcd1b, _waistArmor);
[Safe]
public string GetFoot(BigInteger credential) => Pluck(credential, 0x92877a, _footArmor);
[Safe]
public string GetHand(BigInteger credential) => Pluck(credential, 0x323282, _handArmor);
[Safe]
public string GetNeck(BigInteger credential) => Pluck(credential, 0x0d59c2, _necklaces);
[Safe]
public string GetRing(BigInteger credential) => Pluck(credential, 0xfae431, _rings);
[Safe]
private string Pluck(BigInteger credential, BigInteger keyPrefix, string[] sourceArray)
{
var rand = credential ^ keyPrefix;
var value = rand % sourceArray.Length;
var output = sourceArray[(int)rand % sourceArray.Length];
var greatness = rand % 21;
value = rand % _suffixes.Length;
if (greatness > 14) output = $"{output} {_suffixes[(int)value]}";
if (greatness < 19) return output;
value = rand % _namePrefixes.Length;
var name0 = _namePrefixes[(int)value];
value = rand % _nameSuffixes.Length;
var name1 = _nameSuffixes[(int)value];
output = greatness == 19 ? $"\"{name0} {name1}\" {output}" : $"\"{name0} {name1}\" {output} +1";
return output;
}
[Safe]
public string tokenURI(BigInteger tokenId)
{
var token = GetToken(tokenId);
var parts = new string[19];
parts[0] = "<svg xmlns=\"http://www.w3.org/2000/svg\" preserveAspectRatio=\"xMinYMin meet\" viewBox=\"0 0 350 350\">" +
"<style>.base { fill: white; font-family: serif; font-size: 14px; }</style>" +
"<rect width=\"100%\" height=\"100%\" fill=\"black\" />" +
"<text x=\"10\" y=\"20\" class=\"base\">";
parts[1] = $"{token.Name}";
parts[2] = "</text><text x=\"10\" y=\"40\" class=\"base\">";
parts[3] = GetWeapon(token.Credential);
parts[4] = "</text><text x=\"10\" y=\"60\" class=\"base\">";
parts[5] = GetChest(token.Credential);
parts[6] = "</text><text x=\"10\" y=\"80\" class=\"base\">";
parts[7] = GetHead(token.Credential);
parts[8] = "</text><text x=\"10\" y=\"100\" class=\"base\">";
parts[9] = GetWaist(token.Credential);
parts[10] = "</text><text x=\"10\" y=\"120\" class=\"base\">";
parts[11] = GetFoot(token.Credential);
parts[12] = "</text><text x=\"10\" y=\"140\" class=\"base\">";
parts[13] = GetHand(token.Credential);
parts[14] = "</text><text x=\"10\" y=\"160\" class=\"base\">";
parts[15] = GetNeck(token.Credential);
parts[16] = "</text><text x=\"10\" y=\"180\" class=\"base\">";
parts[17] = GetRing(token.Credential);
parts[18] = "</text></svg>";
string output = $"{parts[0]} {parts[1]} {parts[2]} {parts[3]} {parts[4]} {parts[5]} {parts[6]} {parts[7]} {parts[8]}";
output = $"{output} {parts[9]} {parts[10]} {parts[11]} {parts[12]} {parts[13]} {parts[14]} {parts[15]} {parts[16]} {parts[17]} {parts[18]}";
//string json = StdLib.Base64Encode($"{{\"name\": \"Bag # {tokenId.ToString()}\", \"description\": \"Loot is randomized adventurer gear generated and stored on chain.Stats, images, and other functionality are intentionally omitted for others to interpret.Feel free to use Loot in any way you want.\", \"image\": \"data:image / svg + xml; base64, { StdLib.Base64Encode(output)} \"}}");
//output = $"data:application/json;base64, {json}";
return output;
}
/// <summary>
/// Security Requirements:
/// [0] Has to check the validity of the token Id
/// both the upper and lower bound
/// [1] shall not be called from a contract
/// [3] tx shall fault if token already taken
/// [4] update the token map.
/// </summary>
/// <param name="tokenId"></param>
public void Claim(BigInteger tokenId)
{
// 222 reserved to the developer
ExecutionEngine.Assert(!tokenId.IsZero && tokenId < 7778, "Token ID invalid");
ExecutionEngine.Assert(Runtime.EntryScriptHash == Runtime.CallingScriptHash, "Contract calls are not allowed");
Transaction tx = Runtime.Transaction;
MintToken(tokenId, tx.Sender);
EventMsg("Player mints success");
}
/// <summary>
/// Security Requirements:
/// [0] only the owner can call this function
/// [1] the range of the tokenid is to be in (7777, 8001)
/// </summary>
/// <param name="tokenId"></param>
public void OwnerClaim(BigInteger tokenId)
{
OwnerOnly();
ExecutionEngine.Assert(tokenId > 7777 && tokenId < 8001, "Token ID invalid");
var sender = GetOwner();
MintToken(tokenId, sender);
EventMsg("Owner mints success");
}
/// <summary>
/// Security Requirements:
/// [0] the transaction should `FAULT` if the token already taken
/// [1] has to update the taken map if a new token is mint.
/// </summary>
/// <param name="tokenId"></param>
/// <param name="sender"></param>
private void MintToken(BigInteger tokenId, UInt160 sender)
{
var credential = CheckClaim(tokenId);
var token = TokenState.MintLoot(sender, tokenId, credential);
Mint(tokenId.ToString(), token);
TokenIndexMap.Put(tokenId.ToString(), "taken");
}
/// <summary>
/// Security requirements:
/// [0] throw exception if token already taken
/// [1] should get a random number as credential that
/// is not predictable and not linked to the tokenId
/// </summary>
/// <param name="tokenId"></param>
/// <returns></returns>
private BigInteger CheckClaim(BigInteger tokenId)
{
// <0> -- confirmed
ExecutionEngine.Assert(TokenIndexMap.Get(tokenId.ToString()) != "taken", "Token already claimed.");
// <1> -- confirmed
return Runtime.GetRandom();
}
}
internal enum StoragePrefix
{
Owner = 0x15,
Token = 0x16,
}
}