7 DOGE war gestern. BUY FSINF! FOMO! ALL-IN!
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
20 # -------------------------------------------
22 # -------------------------------------------
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'
28 TOKEN_NAME = 'Informatics token v2'
33 # Number of decimal places
37 # -------------------------------------------
39 # -------------------------------------------
41 OnTransfer = RegisterAction('transfer', 'addr_from', 'addr_to', 'amount')
42 OnError = RegisterAction('error', 'message')
45 def Main(operation, args):
47 This is the main entry point for the Smart Contract
49 :param operation: the operation to be performed ( eg `balanceOf`, `transfer`, etc)
51 :param args: a list of arguments ( which may be empty, but not absent )
53 :return: indicating the successful execution of the smart contract
57 # The trigger determines whether this smart contract is being
58 # run in 'verification' mode or 'application'
59 trigger = GetTrigger()
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():
65 # if the script that sent this is the owner
67 assert CheckWitness(OWNER), 'Unauthorized'
71 # 'Application' mode is the main body of the smart contract
72 elif trigger == Application():
74 if operation == 'name':
77 elif operation == 'decimals':
80 elif operation == 'symbol':
83 elif operation == 'totalSupply':
84 return do_total_supply(ctx=GetContext())
86 elif operation == 'balanceOf':
87 assert len(args) == 1, 'Incorrect argument count'
88 return do_balance_of(ctx=GetContext(), _account=args[0])
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())
94 # the following are administrative methods
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])
100 elif operation == 'owner_burn':
101 assert len(args) == 1, 'Incorrect argument count'
102 return owner_burn(ctx=GetContext(), _amount=args[0])
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())
108 elif operation == 'owner_destroy_contract':
109 assert CheckWitness(OWNER), 'Unauthorized'
112 elif operation == 'owner_dump':
113 return owner_dump(ctx=GetContext())
115 assert False, 'Unknown operation'
119 def do_total_supply(ctx):
121 Method to return the total supply of this coin
123 :return: the total supply of this coin
128 res = Get(ctx, "totalSupply")
133 def do_balance_of(ctx, _account):
135 Method to return the current balance of an address
137 :param _account: the account address to retrieve the balance for
138 :type _account: bytearray
140 :return: the current balance of an address
145 assert len(_account) == 20, "Invalid address"
146 return Get(ctx, _account)
150 def do_transfer(ctx, _from, _to, _amount, caller):
152 Method to transfer NEP5 tokens of a specified _amount from one account to another
154 :param _from: the address to transfer from
155 :type _from: bytearray
156 :param _to: the address to transfer to
158 :param _amount: the _amount of NEP5 tokens to transfer
160 :param caller: the scripthash of the calling script
161 :type caller: bytearray
163 :return: whether the transfer was successful
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"
174 print("Transfer to self")
177 from_balance = Get(ctx, _from)
178 assert from_balance >= _amount, "Insufficient funds"
180 if from_balance == _amount:
181 print("Removing all funds!")
185 difference = from_balance - _amount
186 Put(ctx, _from, difference)
188 to_value = Get(ctx, _to)
189 to_total = to_value + _amount
190 Put(ctx, _to, to_total)
192 OnTransfer(_from, _to, _amount)
197 def owner_mint(ctx, _to, _amount):
199 Method to mint tokens
201 :return: whether the token minting was successful
206 assert CheckWitness(OWNER), 'Unauthorized'
207 assert len(_to) == 20, "Invalid to address"
208 assert _amount > 0, "Invalid amount"
210 balance = Get(ctx, _to)
211 # to discuss for neo3.
213 # Put(ctx, concat(_to,b'--OM'), 1)
214 new_total = balance + _amount
215 Put(ctx, _to, new_total)
217 total_supply = Get(ctx, 'totalSupply')
218 new_total_supply = total_supply + _amount
219 Put(ctx, 'totalSupply', new_total_supply)
221 OnTransfer(None, _to, _amount)
226 def owner_burn(ctx, _amount):
228 Method to burn tokens
230 :param _amount: the _amount of NEP5 tokens to transfer
233 :return: whether the token burning was successful
238 assert CheckWitness(OWNER), 'Unauthorized'
239 assert _amount > 0, "Invalid amount"
241 from_val = Get(ctx, OWNER)
242 assert from_val >= _amount, "Insufficient funds"
244 if from_val == _amount:
245 print("Removing all funds!")
249 difference = from_val - _amount
250 Put(ctx, OWNER, difference)
252 total_supply = Get(ctx, 'totalSupply')
253 new_total_supply = total_supply - _amount
254 Put(ctx, 'totalSupply', new_total_supply)
257 OnTransfer(OWNER, None, _amount)
262 def owner_migrate(ctx, _script, _param_list, _return_type, _properties, _name, _version, _author, _email, _description, caller):
264 Migrate this contract to a new version
266 :param args: list of arguments containing migrating contract info
269 assert CheckWitness(OWNER), 'Unauthorized'
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")
279 Migrate(_script, _param_list, _return_type, _properties, _name, _version, _author, _email, _description)
288 :param args: list of arguments containing migrating contract info
291 assert CheckWitness(OWNER), 'Unauthorized'
294 result_iter = Find(ctx, "")
295 while result_iter.IterNext():
296 items.append([result_iter.IterKey(), result_iter.IterValue()])
301 def CheckWitnessOrCaller(scripthash, caller):
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
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
310 :return: whether the scripthash is authorized
315 if GetContract(scripthash):
316 if scripthash == caller:
317 return True # a contract can spend its own funds
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)
324 return CheckWitness(scripthash)
328 def AssertionError(msg):
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
332 :param msg: error message