-
Notifications
You must be signed in to change notification settings - Fork 22
/
nft_template.py
752 lines (596 loc) · 25.2 KB
/
nft_template.py
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
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
"""NEO Non-Fungible Token Smart Contract Template
Authors: Joe Stewart, Jonathan Winter
Email: [email protected], [email protected]
Version: 2.0
Date: 15 March 2019
License: MIT
Based on NEP5 template by Tom Saunders
Example test using neo-local:
neo> sc build_run /smart-contracts/nft_template.py test 0710 05 True False False name []
Compile and import with neo-python using neo-local:
neo> sc build /smart-contracts/nft_template.py
neo> sc deploy /smart-contracts/nft_template.avm 0710 05 True False False
Example invocation
neo> testinvoke {this_contract_hash} tokensOfOwner [{your_wallet_address}, 1]
"""
from boa.builtins import concat
from boa.interop.Neo.Action import RegisterAction
from boa.interop.Neo.App import DynamicAppCall
from boa.interop.Neo.Blockchain import GetContract
from boa.interop.Neo.Iterator import Iterator
from boa.interop.Neo.Runtime import (CheckWitness, GetTrigger, Log,
Notify, Serialize)
from boa.interop.Neo.Storage import GetContext, Get, Put, Delete, Find
from boa.interop.Neo.TriggerType import Application, Verification
from boa.interop.System.ExecutionEngine import (GetCallingScriptHash,
GetEntryScriptHash,
GetExecutingScriptHash)
# This is the script hash of the address for the owner of the contract
# This can be found in ``neo-python`` with the wallet open,
# use ``wallet`` command
TOKEN_CONTRACT_OWNER = b'\x1c\xc9\xc0\\\xef\xff\xe6\xcd\xd7\xb1\x82\x81j\x91R\xec!\x8d.\xc0'
DAPP_ADMIN = b'\x08t/\\P5\xac-\x0b\x1c\xb4\x94tIyBu\x7f1*' # can set RW properties
TOKEN_NAME = 'Non-Fungible Token Template'
TOKEN_SYMBOL = 'NFT'
TOKEN_DECIMALS = 0
TOKEN_CIRC_KEY = b'in_circulation'
# Smart Contract Event Notifications
OnApprove = RegisterAction('approve', 'addr_from', 'addr_to', 'amount')
OnNFTApprove = RegisterAction('NFTapprove', 'addr_from', 'addr_to', 'tokenid')
OnTransfer = RegisterAction('transfer', 'addr_from', 'addr_to', 'amount')
OnNFTTransfer = RegisterAction('NFTtransfer', 'addr_from', 'addr_to', 'tokenid')
OnMint = RegisterAction('mint', 'addr_to', 'amount')
OnNFTMint = RegisterAction('NFTmint', 'addr_to', 'tokenid')
OnError = RegisterAction('error', 'message')
# common errors
ARG_ERROR = 'incorrect arg length'
INVALID_ADDRESS_ERROR = 'invalid address'
PERMISSION_ERROR = 'incorrect permission'
TOKEN_DNE_ERROR = 'token does not exist'
def Main(operation, args):
"""Entry point to the program
:param str operation: The name of the operation to perform
:param list args: A list of arguments along with the operation
:return: The result of the operation
:rtype: bytearray
Token operations:
- allowance(token_id): returns approved third-party spender of a
token
- approve(token_receiver, token_id, revoke): approve third party
to spend a token
- balanceOf(owner): returns owner's current total tokens owned
- name(): returns name of token
- decimals(): returns token decimal precision
- ownerOf(token_id): returns the owner of the specified token.
- properties(token_id): returns a token's read-only data
- rwProperties(token_id): returns a token's read/write data
- supportedStandards(): returns a list of supported standards
{"NEP-10"}
- symbol(): returns token symbol
- token(token_id): returns a dictionary where token, property,
and uri keys map to the corresponding data for the given
`token_id`
- tokensOfOwner(owner, starting_index): returns a dictionary that
contains less than or equal to ten of the tokens owned by
the specified address starting at the `starting_index`.
- totalSupply(): Returns the total token supply deployed in the
system.
- transfer(to, token_id, extra_arg): transfers a token
- transferFrom(spender, from, to, token_id): allows a third party
to execute a pre-approved transfer
- uri(token_id): Returns a distinct Uniform Resource Identifier
(URI) for a given asset.
The URI data of a token supplies a reference to get more
information about a specific token or its data.
TOKEN_CONTRACT_OWNER operations:
- mintToken(owner, properties, URI, extra_arg): create a new
NFT token with the specified properties and URI and send it
to the specified owner
- modifyURI(token_id, token_data): modify specified token's
URI data
setters:
- setName(name): sets the name of the token
- setSymbol(symbol): sets the token's symbol
- setSupportedStandards(supported_standards): sets the
supported standards, 'NEP-10' is always the first element
in the array
"""
# The trigger determines whether this smart contract is being run
# in 'verification' mode or 'application'
trigger = GetTrigger()
# 'Verification' mode is used when trying to spend assets
# (eg NEO, Gas) on behalf of this contract's address
if trigger == Verification():
# if the script that sent this is the owner, we allow the spend
if CheckWitness(TOKEN_CONTRACT_OWNER):
return True
elif trigger == Application():
# Need to get this at the top level
caller = GetCallingScriptHash()
ctx = GetContext()
if operation == 'name':
name = Get(ctx, 'name')
if name:
return name
else:
return TOKEN_NAME
elif operation == 'symbol':
symbol = Get(ctx, 'symbol')
if symbol:
return symbol
else:
return TOKEN_SYMBOL
elif operation == 'supportedStandards':
supported_standards = Get(ctx, 'supportedStandards')
if supported_standards:
return supported_standards
else:
return Serialize(['NEP-10'])
elif operation == 'totalSupply':
return Get(ctx, TOKEN_CIRC_KEY)
elif operation == 'allowance':
assert len(args) == 1, ARG_ERROR
ownership = safe_deserialize(Get(ctx, concat('ownership/', args[0])))
assert ownership, TOKEN_DNE_ERROR
# don't fault here in case a calling contract is just checking allowance value
if not has_key(ownership, 'approved'): return False
if len(ownership['approved']) != 40: return False
return ownership['approved']
elif operation == 'balanceOf':
assert len(args) == 1, ARG_ERROR
assert len(args[0]) == 20, INVALID_ADDRESS_ERROR
token_iter = Find(ctx, args[0])
count = 0
while token_iter.next():
count += 1
return count
elif operation == 'ownerOf':
assert len(args) == 1, ARG_ERROR
ownership = safe_deserialize(Get(ctx, concat('ownership/', args[0])))
assert ownership, TOKEN_DNE_ERROR
assert has_key(ownership, 'owner'), TOKEN_DNE_ERROR
assert len(ownership['owner']) == 20, TOKEN_DNE_ERROR
return ownership['owner']
elif operation == 'properties':
assert len(args) == 1, ARG_ERROR
return get_properties(ctx, args[0])
elif operation == 'rwProperties':
assert len(args) == 1, ARG_ERROR
return get_rw_properties(ctx, args[0])
elif operation == 'token':
assert len(args) == 1, ARG_ERROR
token = Get(ctx, concat('token/', args[0]))
assert token, TOKEN_DNE_ERROR
return token
elif operation == 'tokensOfOwner':
assert len(args) == 2, ARG_ERROR
tokens_of_owner = do_tokens_of_owner(ctx, args[0], args[1])
assert tokens_of_owner, 'address has no tokens'
return Serialize(tokens_of_owner)
elif operation == 'uri':
assert len(args) == 1, ARG_ERROR
token = safe_deserialize(Get(ctx, concat('token/', args[0])))
assert token, TOKEN_DNE_ERROR
assert has_key(token, 'uri'), TOKEN_DNE_ERROR
return token['uri']
elif operation == 'decimals':
return TOKEN_DECIMALS
#
# User RW operations
#
if operation == 'approve':
# args: from, spender, id, revoke
# (NFT needs a fourth argument to revoke approval)
assert len(args) > 2, ARG_ERROR
assert args[2], TOKEN_DNE_ERROR
return do_approve(ctx, caller, args)
elif operation == 'transfer':
assert len(args) > 1, ARG_ERROR
return do_transfer(ctx, caller, args)
elif operation == 'transferFrom':
assert len(args) > 2, ARG_ERROR
if len(args) == 3:
# Nash-style (from, to, amount/id) transferFrom that can
# be invoked only by whitelisted DEX to initiate a
# pre-approved transfer
return nash_do_transfer_from(ctx, caller, args)
else:
# Moonlight-style (spender, from, to, amount/id)
# transfer where an authenticated spender/originator is
# the only one who can initiate a transfer but can send
# to an arbitrary third party (or themselves)
return do_transfer_from(ctx, caller, args)
#
# dApp operations
#
if operation == 'setRWProperties':
# args: token id, rwdata
assert CheckWitness(DAPP_ADMIN), PERMISSION_ERROR
assert len(args) == 2, ARG_ERROR
return set_rw_properties(ctx, args[0], args[1])
# Administrative operations
if CheckWitness(TOKEN_CONTRACT_OWNER):
if operation == 'mintToken':
assert len(args) > 3, ARG_ERROR
return do_mint_token(ctx, args)
elif operation == 'modifyURI':
assert len(args) == 2, ARG_ERROR
return do_modify_uri(ctx, args)
elif operation == 'setName':
assert len(args) == 1, ARG_ERROR
return do_set_config(ctx, 'name', args[0])
elif operation == 'setSymbol':
assert len(args) == 1, ARG_ERROR
return do_set_config(ctx, 'symbol', args[0])
elif operation == 'setSupportedStandards':
assert len(args) >= 1, ARG_ERROR
supported_standards = ['NEP-10']
for arg in args:
supported_standards.append(arg)
return do_set_config(ctx, 'supportedStandards', Serialize(supported_standards))
AssertionError('unknown operation')
return False
def do_approve(ctx, Caller, args):
"""Approve a token to be transferred to a third party by an approved spender
:param StorageContext ctx: current store context
:param bytearray t_owner: current owner of the token
:param bytearray t_spender: spender to approve
:param int t_id: int: token id
:param bool revoke: set to True to revoke previous approval
:return: approval success
:rtype: bool
"""
t_owner = args[0]
t_spender = args[1]
t_id = args[2]
revoke = False
if len(args) > 3:
revoke = args[3]
if Caller != GetEntryScriptHash() and not is_whitelisted_dex(ctx, Caller):
# non-whitelisted contracts can only approve their own funds for transfer,
# even if they have the signature of the owner on the invocation
t_owner = Caller
assert len(t_owner) == 20, INVALID_ADDRESS_ERROR
assert len(t_spender) == 20, INVALID_ADDRESS_ERROR
assert t_id, TOKEN_DNE_ERROR
ownership_key = concat('ownership/', t_id)
ownership = safe_deserialize(Get(ctx, ownership_key))
assert ownership, TOKEN_DNE_ERROR
assert has_key(ownership, 'owner'), TOKEN_DNE_ERROR
assert t_owner == ownership['owner'], PERMISSION_ERROR
assert t_owner != t_spender, 'same owner and spender'
assert authenticate(t_owner, Caller), PERMISSION_ERROR
# revoke previous approval if revoke is True
if revoke:
if has_key(ownership, 'approved'):
ownership.remove('approved')
Put(ctx, ownership_key, Serialize(ownership))
# log the revoking of previous approvals
OnApprove(t_owner, t_spender, 0)
OnNFTApprove(t_owner, '', t_id)
return True
ownership['approved'] = concat(t_owner, t_spender)
# approve this transfer
Put(ctx, ownership_key, Serialize(ownership))
# Log this approval event
OnApprove(t_owner, t_spender, 1)
OnNFTApprove(t_owner, t_spender, t_id)
return True
def do_mint_token(ctx, args):
"""Mints a new NFT token; stores it's properties, URI info, and
owner on the blockchain; updates the totalSupply
:param StorageContext ctx: current store context
:param list args:
0: bytearray t_owner: token owner
1: int t_id: token id (must not already exist)
2: str t_properties: token's read only data
3: str t_uri: token's uri
4: str t_rw_properties: token's read/write data (optional)
:return: mint success
:rtype: bool
"""
t_circ = Get(ctx, TOKEN_CIRC_KEY)
t_circ += 1
assert len(args[0]) == 20, INVALID_ADDRESS_ERROR
assert args[1], 'missing token id'
assert args[2], 'missing properties'
assert args[3], 'missing uri'
t_id = args[1]
token = safe_deserialize(Get(ctx, concat('token/', t_id)))
assert not token, 'token already exists'
# basically a token 'object' containing the token's
# id, uri, and properties
token = {}
ownership = {} # information about the token's owner
token['id'] = t_id
token['uri'] = args[3]
token['properties'] = args[2] # this can never change
if len(args) > 4:
token['rwproperties'] = args[4]
else:
token['rwproperties'] = ''
ownership['owner'] = args[0]
Put(ctx, concat('token/', t_id), Serialize(token))
# update token's owner
Put(ctx, concat('ownership/', t_id), Serialize(ownership))
res = add_token_to_owners_list(ctx, ownership['owner'], t_id)
Put(ctx, TOKEN_CIRC_KEY, t_circ) # update total supply
# Log this minting event
OnTransfer('', ownership['owner'], 1)
OnNFTTransfer('', ownership['owner'], t_id)
OnMint(ownership['owner'], 1)
OnNFTMint(ownership['owner'], t_id)
return True
def do_modify_uri(ctx, args):
"""Modifies token URI
:param StorageContext ctx: current store context
:param int t_id: token id
:param str t_uri: token uri
:return: URI modification success
:rtype: bool
"""
t_id = args[0]
t_uri = args[1]
token_key = concat('token/', t_id)
token = safe_deserialize(Get(ctx, token_key))
assert token, TOKEN_DNE_ERROR
token['uri'] = t_uri
Put(ctx, token_key, Serialize(token))
return True
def do_tokens_of_owner(ctx, t_owner, start_id):
"""This method returns ten of the owner's tokens starting at the
given index. The index is used for paginating through the results.
Pagination is needed for the situation where the owner's dict of
tokens could be quite large.
:param StorageContext ctx: current store context
:param bytearray t_owner: token owner
:param int start_id: the id to start searching through the
owner's tokens
:return: dictionary of id, properties, and uri keys mapped to their
corresponding token's data
:rtype: bool or dict
"""
assert len(t_owner) == 20, INVALID_ADDRESS_ERROR
if start_id == 0:
start_id = 1 # token id's cannot go below 1
start_key = concat(t_owner, start_id)
count = 0
token_dict = {}
token_iter = Find(ctx, t_owner)
# while loop explained: keep looping through the owner's list
# of tokens until 10 have been found beginning at the starting
# index.
# if statement explained: once a key has been found matching
# my search key (or of greater value),
# update the dictionary, increment the counter,
# and disregard trying to find a matching key thereafter.
# (once a key has been found matching my search key
# (or greater), just get everything afterward while count < 10)
while token_iter.next() and (count < 10):
if (token_iter.Key >= start_key) or (count > 0):
token_key = concat('token/', token_iter.Value)
token = safe_deserialize(Get(ctx, token_key))
if token:
token_dict[token_key] = token
count += 1
return token_dict
def do_transfer(ctx, Caller, args):
"""Transfers a token at the specified id from the t_owner address
to the t_to address
:param StorageContext ctx: current store context
:param list args:
0: bytearray t_to: transfer to address
1: int t_id: token id
:return: transfer success
:rtype: bool
"""
# we don't need the t_from because the token data stores the owner
t_from = ""
t_to = args[0]
t_id = args[1]
if len(args) == 3: # use traditional from, to, id format if they want to send it
t_from = args[0]
t_to = args[1]
t_id = args[2]
if Caller != GetEntryScriptHash() and not is_whitelisted_dex(ctx, Caller):
# non-whitelisted contracts can only approve their own funds for transfer,
# even if they have the signature of the owner on the invocation
t_from = Caller
assert len(t_to) == 20, INVALID_ADDRESS_ERROR
ownership = safe_deserialize(Get(ctx, concat('ownership/', t_id)))
assert ownership, TOKEN_DNE_ERROR
assert has_key(ownership, 'owner'), TOKEN_DNE_ERROR
assert len(ownership['owner']) == 20, TOKEN_DNE_ERROR
t_owner = ownership['owner']
if t_owner == t_to:
print('transfer to self')
return True
assert authenticate(t_owner, Caller), PERMISSION_ERROR
res = remove_token_from_owners_list(ctx, t_owner, t_id)
assert res, 'unable to remove token from owner list'
ownership['owner'] = t_to # update token's owner
# remove any existing approvals for this token
if has_key(ownership, 'approved'):
ownership.remove('approved')
Put(ctx, concat('ownership/', t_id), Serialize(ownership))
res = add_token_to_owners_list(ctx, t_to, t_id)
# log this transfer event
OnTransfer(t_owner, t_to, 1)
OnNFTTransfer(t_owner, t_to, t_id)
return True
def do_transfer_from(ctx, Caller, args):
"""Transfers the approved token at the specified id from the
t_from address to the t_to address
Only the approved spender OR a whitelisted DEX can invoke this function
and a whitelisted DEX will still need to pass the authentication of the spender
:param StorageContext ctx: current store context
:param list args:
0: bytearray t_spender: approved spender address
1: bytearray t_from: transfer from address (token owner)
2: bytearray t_to: transfer to address (token receiver)
3: int t_id: token id
:return: transferFrom success
:rtype: bool
"""
t_spender = args[0]
t_from = args[1]
t_to = args[2]
t_id = args[3]
if Caller != GetEntryScriptHash() and not is_whitelisted_dex(ctx, Caller):
# non-whitelisted contracts can only approve their own funds for transfer,
# even if they have the signature of the owner on the invocation
t_from = Caller
assert len(t_spender) == 20, INVALID_ADDRESS_ERROR
assert len(t_from) == 20, INVALID_ADDRESS_ERROR
assert len(t_to) == 20, INVALID_ADDRESS_ERROR
assert authenticate(t_spender, Caller), PERMISSION_ERROR
if t_from == t_to:
print('transfer to self')
return True
ownership = safe_deserialize(Get(ctx, concat('ownership/', t_id)))
assert ownership, TOKEN_DNE_ERROR
assert has_key(ownership, 'owner'), TOKEN_DNE_ERROR
assert has_key(ownership, 'approved'), 'no approval exists for this token'
assert len(ownership['owner']) == 20, TOKEN_DNE_ERROR
t_owner = ownership['owner']
assert t_from == t_owner, 'from address is not the owner of this token'
assert len(ownership['approved']) == 40, 'malformed approval key for this token'
# Finally check to see if the owner approved this spender
assert ownership['approved'] == concat(t_from, t_spender), PERMISSION_ERROR
res = remove_token_from_owners_list(ctx, t_from, t_id)
assert res, 'unable to remove token from owner list'
ownership['owner'] = t_to
ownership.remove('approved') # remove previous approval
Put(ctx, concat('ownership/', t_id), Serialize(ownership))
res = add_token_to_owners_list(ctx, t_to, t_id)
# log this transfer event
OnTransfer(t_from, t_to, 1)
OnNFTTransfer(t_from, t_to, t_id)
return True
def nash_do_transfer_from(ctx, Caller, args):
"""Transfers the approved token at the specified id from the
t_from address to the t_to address
Only a whitelisted DEX can invoke this function
:param StorageContext ctx: current store context
:param list args:
0: bytearray t_from: transfer from address (token owner)
1: bytearray t_to: transfer to address (token receiver)
2: int t_id: token id
:return: transferFrom success
:rtype: bool
"""
t_from = args[0]
t_to = args[1]
t_id = args[2]
assert is_whitelisted_dex(ctx, Caller), PERMISSION_ERROR
assert len(t_from) == 20, INVALID_ADDRESS_ERROR
assert len(t_to) == 20, INVALID_ADDRESS_ERROR
if t_from == t_to:
print('transfer to self')
return True
ownership = safe_deserialize(Get(ctx, concat('ownership/', t_id)))
assert ownership, TOKEN_DNE_ERROR
assert has_key(ownership, 'owner'), TOKEN_DNE_ERROR
assert has_key(ownership, 'approved'), 'no approval exists for this token'
assert len(ownership['owner']) == 20, TOKEN_DNE_ERROR
t_owner = ownership['owner']
assert t_from == t_owner, 'from address is not the owner of this token'
assert len(ownership['approved']) == 40, 'malformed approval key for this token'
assert ownership['approved'] == concat(t_from, t_to), PERMISSION_ERROR
res = remove_token_from_owners_list(ctx, t_from, t_id)
assert res, 'unable to remove token from owner list'
ownership['owner'] = t_to
ownership.remove('approved') # remove previous approval
Put(ctx, concat('ownership/', t_id), Serialize(ownership))
res = add_token_to_owners_list(ctx, t_to, t_id)
# log this transfer event
OnTransfer(t_from, t_to, 1)
OnNFTTransfer(t_from, t_to, t_id)
return True
# helper methods
def do_set_config(ctx, key, value):
"""Sets or deletes a config key
:param StorageContext ctx: current store context
:param str key: key
:param any value: value
:return: config success
:rtype: bool
"""
if len(value) > 0:
Put(ctx, key, value)
print('config key set')
else:
Delete(ctx, key)
print('config key deleted')
return True
def safe_deserialize(data):
"""Checks to see if the data exists before attempting to
deserialize it
:param list or dict data: data to deserialize
:return: deserialized data or False
:rtype: bool or list or dict
"""
if data:
obj = Deserialize(data)
return obj
return False
def add_token_to_owners_list(ctx, t_owner, t_id):
"""Adds a token to the owner's list of tokens
:param StorageContext ctx: current store context
:param bytearray t_owner: token owner (could be either a smart
contract or a wallet address)
:param int t_id: token ID
:return: none
"""
Put(ctx, concat(t_owner, t_id), t_id) # store owner's new token
return True
def remove_token_from_owners_list(ctx, t_owner, t_id):
"""Removes a token from owner's list of tokens
:param StorageContext ctx: current store context
:param bytearray t_owner: token owner
:param int t_id: token id
:return: successfully removed token from owner's list
:rtype: bool
"""
ckey = concat(t_owner, t_id)
if Get(ctx, ckey):
Delete(ctx, ckey)
return True
print("token not found in owner's list")
return False
def get_properties(ctx, id):
token = safe_deserialize(Get(ctx, concat('token/', id)))
if not token:
print(TOKEN_DNE_ERROR)
return False
if not has_key(token, 'properties'):
return False
return token['properties']
def get_rw_properties(ctx, id):
token = safe_deserialize(Get(ctx, concat('token/', id)))
if not token:
print(TOKEN_DNE_ERROR)
return False
if not has_key(token, 'rwproperties'):
return False
return token['rwproperties']
def set_rw_properties(ctx, id, data):
token_key = concat('token/', id)
token = safe_deserialize(Get(ctx, token_key ))
if not token:
print(TOKEN_DNE_ERROR)
return False
token['rwproperties'] = data
Put(ctx, token_key, Serialize(token))
return True
def authenticate(scripthash, Caller):
if CheckWitness(scripthash): return True
if GetContract(scripthash) and scripthash == Caller: return True
return False
def do_whitelist_dex(ctx, args):
return do_set_config(ctx, concat('exchange/', args[0]), args[1])
def is_whitelisted_dex(ctx, scripthash):
return Get(ctx, concat('exchange/', scripthash))
def AssertionError(msg):
OnError(msg) # for neo-cli ApplicationLog
raise Exception(msg)