]> git.somenet.org - pub/jan/neo-infcoin.git/blob - InfCoin.py
(FS)InfCoin contract
[pub/jan/neo-infcoin.git] / InfCoin.py
1 #!/bin/false
2 # not executable.
3
4 """
5 Informatics-Coin NEP5
6
7 DOGE war gestern. BUY FSINF! FOMO! ALL-IN!
8 """
9
10 from boa.interop.Neo.Runtime import GetTrigger, CheckWitness
11 from boa.interop.Neo.Action import RegisterAction
12 from boa.interop.Neo.TriggerType import Application, Verification
13 from boa.interop.Neo.Storage import GetContext, Get, Put, Delete, Find
14 from boa.interop.System.ExecutionEngine import GetCallingScriptHash
15 from boa.interop.Neo.Blockchain import GetContract
16 from boa.interop.Neo.Contract import Migrate, Destroy
17 from boa.interop.Neo.Iterator import IterNext, IterKey, IterValue
18 from boa.builtins import concat
19
20 # -------------------------------------------
21 # TOKEN SETTINGS
22 # -------------------------------------------
23
24 # Script hash of the contract owner
25 OWNER = b'\x0e\x42\x28\xf9\x68\x68\x07\x9d\xaa\xd2\xa5\xa8\x3e\x93\xab\xbe\x5c\xaf\x22\x1d'
26
27 # Name of the Token
28 TOKEN_NAME = 'Informatics token v2'
29
30 # Symbol of the Token
31 TOKEN_SYMBOL = 'Inf'
32
33 # Number of decimal places
34 TOKEN_DECIMALS = 2
35
36
37 # -------------------------------------------
38 # Events
39 # -------------------------------------------
40
41 OnTransfer = RegisterAction('transfer', 'addr_from', 'addr_to', 'amount')
42 OnError = RegisterAction('error', 'message')
43
44
45 def Main(operation, args):
46     """
47     This is the main entry point for the Smart Contract
48
49     :param operation: the operation to be performed ( eg `balanceOf`, `transfer`, etc)
50     :type operation: str
51     :param args: a list of arguments ( which may be empty, but not absent )
52     :type args: list
53     :return: indicating the successful execution of the smart contract
54     :rtype: bool
55     """
56
57     # The trigger determines whether this smart contract is being
58     # run in 'verification' mode or 'application'
59     trigger = GetTrigger()
60
61     # 'Verification' mode is used when trying to spend assets ( eg NEO, Gas)
62     # on behalf of this contract's address
63     if trigger == Verification():
64
65         # if the script that sent this is the owner
66         # we allow the spend
67         assert CheckWitness(OWNER), 'Unauthorized'
68         return True
69
70
71     # 'Application' mode is the main body of the smart contract
72     elif trigger == Application():
73
74         if operation == 'name':
75             return TOKEN_NAME
76
77         elif operation == 'decimals':
78             return TOKEN_DECIMALS
79
80         elif operation == 'symbol':
81             return TOKEN_SYMBOL
82
83         elif operation == 'totalSupply':
84             return do_total_supply(ctx=GetContext())
85
86         elif operation == 'balanceOf':
87             assert len(args) == 1, 'Incorrect argument count'
88             return do_balance_of(ctx=GetContext(), _account=args[0])
89
90         elif operation == 'transfer':
91             assert len(args) == 3, 'Incorrect argument count'
92             return do_transfer(ctx=GetContext(), _from=args[0], _to=args[1], _amount=args[2], caller=GetCallingScriptHash())
93
94         # the following are administrative methods
95
96         elif operation == 'owner_mint':
97             assert len(args) == 2, 'Incorrect argument count'
98             return owner_mint(ctx=GetContext(), _to=args[0], _amount=args[1])
99
100         elif operation == 'owner_burn':
101             assert len(args) == 1, 'Incorrect argument count'
102             return owner_burn(ctx=GetContext(), _amount=args[0])
103
104         elif operation == 'owner_migrate_contract':
105             assert len(args) == 9, 'Incorrect argument count'
106             return owner_migrate(ctx=GetContext(), _script=args[0], _param_list=args[1], _return_type=args[2], _properties=args[3], _name=args[4], _version=args[5], _author=args[6], _email=args[7], _description=args[8], caller=GetCallingScriptHash())
107
108         elif operation == 'owner_destroy_contract':
109             assert CheckWitness(OWNER), 'Unauthorized'
110             return Destroy()
111
112         elif operation == 'owner_dump':
113             return owner_dump(ctx=GetContext())
114
115     assert False, 'Unknown operation'
116
117
118
119 def do_total_supply(ctx):
120     """
121     Method to return the total supply of this coin
122
123     :return: the total supply of this coin
124     :rtype: int
125
126     """
127
128     res = Get(ctx, "totalSupply")
129     return res
130
131
132
133 def do_balance_of(ctx, _account):
134     """
135     Method to return the current balance of an address
136
137     :param _account: the account address to retrieve the balance for
138     :type _account: bytearray
139
140     :return: the current balance of an address
141     :rtype: int
142
143     """
144
145     assert len(_account) == 20, "Invalid address"
146     return Get(ctx, _account)
147
148
149
150 def do_transfer(ctx, _from, _to, _amount, caller):
151     """
152     Method to transfer NEP5 tokens of a specified _amount from one account to another
153
154     :param _from: the address to transfer from
155     :type _from: bytearray
156     :param _to: the address to transfer to
157     :type _to: bytearray
158     :param _amount: the _amount of NEP5 tokens to transfer
159     :type _amount: int
160     :param caller: the scripthash of the calling script
161     :type caller: bytearray
162
163     :return: whether the transfer was successful
164     :rtype: bool
165
166     """
167
168     assert _amount > 0, "Invalid amount"
169     assert len(_from) == 20, "Invalid from address"
170     assert len(_to) == 20, "Invalid to address"
171     assert CheckWitnessOrCaller(_from, caller), "Transfer unauthorized"
172
173     if _from == _to:
174         print("Transfer to self")
175         return True
176
177     from_balance = Get(ctx, _from)
178     assert from_balance >= _amount, "Insufficient funds"
179
180     if from_balance == _amount:
181         print("Removing all funds!")
182         Delete(ctx, _from)
183
184     else:
185         difference = from_balance - _amount
186         Put(ctx, _from, difference)
187
188     to_value = Get(ctx, _to)
189     to_total = to_value + _amount
190     Put(ctx, _to, to_total)
191
192     OnTransfer(_from, _to, _amount)
193     return True
194
195
196
197 def owner_mint(ctx, _to, _amount):
198     """
199     Method to mint tokens
200
201     :return: whether the token minting was successful
202     :rtype: bool
203
204     """
205
206     assert CheckWitness(OWNER), 'Unauthorized'
207     assert len(_to) == 20, "Invalid to address"
208     assert _amount > 0, "Invalid amount"
209
210     balance = Get(ctx, _to)
211 # to discuss for neo3.
212 #    if balance == 0:
213 #        Put(ctx, concat(_to,b'--OM'), 1)
214     new_total = balance + _amount
215     Put(ctx, _to, new_total)
216
217     total_supply = Get(ctx, 'totalSupply')
218     new_total_supply = total_supply + _amount
219     Put(ctx, 'totalSupply', new_total_supply)
220
221     OnTransfer(None, _to, _amount)
222     return True
223
224
225
226 def owner_burn(ctx, _amount):
227     """
228     Method to burn tokens
229
230     :param _amount: the _amount of NEP5 tokens to transfer
231     :type _amount: int
232
233     :return: whether the token burning was successful
234     :rtype: bool
235
236     """
237
238     assert CheckWitness(OWNER), 'Unauthorized'
239     assert _amount > 0, "Invalid amount"
240
241     from_val = Get(ctx, OWNER)
242     assert from_val >= _amount, "Insufficient funds"
243
244     if from_val == _amount:
245         print("Removing all funds!")
246         Delete(ctx, OWNER)
247
248     else:
249         difference = from_val - _amount
250         Put(ctx, OWNER, difference)
251
252     total_supply = Get(ctx, 'totalSupply')
253     new_total_supply = total_supply - _amount
254     Put(ctx, 'totalSupply', new_total_supply)
255
256
257     OnTransfer(OWNER, None, _amount)
258     return True
259
260
261
262 def owner_migrate(ctx, _script, _param_list, _return_type, _properties, _name, _version, _author, _email, _description, caller):
263     """
264     Migrate this contract to a new version
265
266     :param args: list of arguments containing migrating contract info
267     :return: Contract
268     """
269     assert CheckWitness(OWNER), 'Unauthorized'
270
271     # once migrated, funds held at the contract address are no
272     # longer retrievable.  make sure to transfer all funds
273     # before doing anything.
274     current_contract_balance = Get(ctx, caller)
275     if current_contract_balance > 0:
276         print("Cannot migrate yet. Please transfer all neo/gas and tokens from contract address")
277         return False
278
279     Migrate(_script, _param_list, _return_type, _properties, _name, _version, _author, _email, _description)
280     return True
281
282
283
284 def owner_dump(ctx):
285     """
286     Dump contract store
287
288     :param args: list of arguments containing migrating contract info
289     :return: Contract
290     """
291     assert CheckWitness(OWNER), 'Unauthorized'
292
293     items = []
294     result_iter = Find(ctx, "")
295     while result_iter.IterNext():
296         items.append([result_iter.IterKey(), result_iter.IterValue()])
297     return items
298
299
300
301 def CheckWitnessOrCaller(scripthash, caller):
302     """
303     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
304
305     :param scripthash: the scripthash to be checked
306     :type scripthash: bytearray
307     :param caller: the scripthash of the calling script
308     :type caller: bytearray
309
310     :return: whether the scripthash is authorized
311     :rtype: bool
312
313     """
314
315     if GetContract(scripthash):
316         if scripthash == caller:
317             return True  # a contract can spend its own funds
318         else:
319             # deny third-party contracts from transferring
320             # tokens of a user even with the user signature
321             # (this will break ability of some DEX to list the token)
322             return False
323
324     return CheckWitness(scripthash)
325
326
327
328 def AssertionError(msg):
329     """
330     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
331
332     :param msg: error message
333     :type msg: string
334     """
335
336     OnError(msg)
337     raise Exception(msg)