This repository was archived by the owner on Mar 28, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 55
/
Copy pathNEP5.py
executable file
·225 lines (156 loc) · 6.23 KB
/
NEP5.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
"""
NEP5 Token
===================================
.. moduleauthor:: Thomas Saunders <[email protected]>, Joe Stewart <[email protected]>
This file, when compiled to .avm format, would comply with the current NEP5 token standard on the NEO blockchain
Token standard is available in proposal form here:
`NEP5 Token Standard Proposal <https://github.com/neo-project/proposals/blob/master/nep-5.mediawiki>`_
Compilation can be achieved as such
>>> from boa.compiler import Compiler
>>> Compiler.load_and_save('./boa/tests/src/NEP5.py')
Or, from within the neo-python shell
``sc build_run path/to/NEP5.py True False True 0710 05 name []``
Below is the current implementation in Python
"""
from boa.interop.Neo.Runtime import GetTrigger, CheckWitness
from boa.interop.Neo.Action import RegisterAction
from boa.interop.Neo.TriggerType import Application, Verification
from boa.interop.Neo.Storage import GetContext, Get, Put, Delete
from boa.interop.System.ExecutionEngine import GetCallingScriptHash
from boa.interop.Neo.Blockchain import GetContract
from boa.builtins import concat
# -------------------------------------------
# TOKEN SETTINGS
# -------------------------------------------
OWNER = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
# Script hash of the contract owner
# Name of the Token
TOKEN_NAME = 'NEP5 Standard'
# Symbol of the Token
TOKEN_SYMBOL = 'NEP5'
# Number of decimal places
TOKEN_DECIMALS = 8
# Total Supply of tokens in the system
TOKEN_TOTAL_SUPPLY = 10000000 * 100000000 # 10m total supply * 10^8 ( decimals)
ctx = GetContext()
# -------------------------------------------
# Events
# -------------------------------------------
OnTransfer = RegisterAction('transfer', 'addr_from', 'addr_to', 'amount')
OnApprove = RegisterAction('approve', 'addr_from', 'addr_to', 'amount')
OnError = RegisterAction('error', 'message')
def Main(operation, args):
"""
This is the main entry point for the Smart Contract
:param operation: the operation to be performed ( eg `balanceOf`, `transfer`, etc)
:type operation: str
:param args: a list of arguments ( which may be empty, but not absent )
:type args: list
:return: indicating the successful execution of the smart contract
:rtype: bool
"""
# 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
assert CheckWitness(OWNER), 'unauthorized'
return True
# 'Application' mode is the main body of the smart contract
elif trigger == Application():
if operation == 'name':
return TOKEN_NAME
elif operation == 'decimals':
return TOKEN_DECIMALS
elif operation == 'symbol':
return TOKEN_SYMBOL
elif operation == 'totalSupply':
return TOKEN_TOTAL_SUPPLY
elif operation == 'balanceOf':
assert len(args) == 1, 'incorrect arg length'
account = args[0]
return do_balance_of(ctx, account)
elif operation == 'transfer':
assert len(args) == 3, 'incorrect arg length'
t_from = args[0]
t_to = args[1]
t_amount = args[2]
return do_transfer(ctx, t_from, t_to, t_amount, GetCallingScriptHash())
AssertionError('unknown operation')
def do_balance_of(ctx, account):
"""
Method to return the current balance of an address
:param account: the account address to retrieve the balance for
:type account: bytearray
:return: the current balance of an address
:rtype: int
"""
assert len(account) == 20, "invalid address"
return Get(ctx, account)
def do_transfer(ctx, t_from, t_to, amount, caller):
"""
Method to transfer NEP5 tokens of a specified amount from one account to another
:param t_from: the address to transfer from
:type t_from: bytearray
:param t_to: the address to transfer to
:type t_to: bytearray
:param amount: the amount of NEP5 tokens to transfer
:type amount: int
:param caller: the scripthash of the calling script
:type caller: bytearray
:return: whether the transfer was successful
:rtype: bool
"""
assert amount > 0, "invalid amount"
assert len(t_from) == 20, "invalid from address"
assert len(t_to) == 20, "invalid to address"
assert CheckWitnessOrCaller(t_from, caller), "transfer not authorized"
if t_from == t_to:
print("transfer to self!")
return True
from_val = Get(ctx, t_from)
assert from_val >= amount, "insufficient funds"
if from_val == amount:
Delete(ctx, t_from)
else:
difference = from_val - amount
Put(ctx, t_from, difference)
to_value = Get(ctx, t_to)
to_total = to_value + amount
Put(ctx, t_to, to_total)
OnTransfer(t_from, t_to, amount)
return True
def CheckWitnessOrCaller(scripthash, caller):
"""
Method to check if the transaction is signed by a private key
or is the scripthash of a contract that is authorized to perform
the requested function for its own address only
:param scripthash: the scripthash to be checked
:type scripthash: bytearray
:param caller: the scripthash of the calling script
:type caller: bytearray
:return: whether the scripthash is authorized
:rtype: bool
"""
if GetContract(scripthash):
if scripthash == caller:
return True # a contract can spend its own funds
else:
# deny third-party contracts from transferring
# tokens of a user even with the user signature
# (this will break ability of some DEX to list the token)
return False
return CheckWitness(scripthash)
def AssertionError(msg):
"""
Method to throw an exception (required by assert)
- will log a notification to the neo-cli ApplicationLog
to aid in post-transaction troubleshooting and analysis
:param msg: error message
:type msg: string
"""
OnError(msg)
raise Exception(msg)