-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbond_poc.py
467 lines (410 loc) · 18.1 KB
/
bond_poc.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
"""
PLease set following environment variables for this script to work properly
export HOST=<ip address> default value=None
"""
import json
import subprocess
import time
import urllib3
from solcx import compile_files, install_solc
from web3 import Web3
from web3._utils.events import get_event_data
from web3.middleware import geth_poa_middleware
urllib3.disable_warnings()
with open('./resources/accounts.json') as json_file:
accounts = json.load(json_file)
exchange_addr = accounts["exchange"]
exchange_key = accounts["exchange_key"]
treasury_addr = accounts["treasury"]
treasury_key = accounts["treasury_key"]
mm_addr = accounts["mm"]
mm_key = accounts["mm_keys"]
def setup_w3(host):
"""
setup w3 http provider
"""
url = "http://" + host + ":8545"
global w3
w3 = Web3(Web3.HTTPProvider(url, request_kwargs={"verify": False}))
w3.middleware_onion.inject(geth_poa_middleware, layer=0)
def compile_exchange():
"""
compiling Exchange source file
"""
install_solc("0.8.9")
# global exchange_abi, exchange_bytecode
compiled_sol = compile_files(
["contracts/Exchange.sol"], solc_version='0.8.9', optimize=True)
exchange_bytecode = compiled_sol['contracts/Exchange.sol:Exchange']['bin']
exchange_abi = compiled_sol['contracts/Exchange.sol:Exchange']['abi']
return exchange_bytecode, exchange_abi
def tx_receipt_poll(construct_txn, acc_priv_key):
"""
function to validate tx hash and poll for receipt
Arguments:
construct_txn: transaction construct
acc_priv_key: private key of account
"""
signed_txn = w3.eth.account.sign_transaction(
construct_txn, acc_priv_key)
# Validating transaction hash
tx_hash_send = signed_txn.hash
tx_hash = w3.eth.send_raw_transaction(signed_txn.rawTransaction)
assert tx_hash_send == tx_hash, "tx hash mismatch"
tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
assert tx_receipt.status == 1, "transaction failed"
return tx_receipt
def deploy_exchange(pt_name, pt_symbol):
"""
deploy exchange contract and distribute tokens among all senders
Arguments:
pt_name: Payment token Name
pt_symbol: symbol for Payment token
"""
exchange_bytecode, exchange_abi = compile_exchange()
contract = w3.eth.contract(abi=exchange_abi, bytecode=exchange_bytecode)
# deploying contract
construct_txn = contract.constructor(pt_name, pt_symbol).buildTransaction(
{
"from": exchange_addr,
"gas": 2000000,
"gasPrice": 0,
"nonce": w3.eth.get_transaction_count(exchange_addr),
"chainId": 5000,
}
)
tx_receipt = tx_receipt_poll(construct_txn, exchange_key)
print("\nExchange smart contract deploy success, contract address: '{}'".format(
tx_receipt.contractAddress))
exchange_contract_address = tx_receipt.contractAddress
with open('./resources/contracts.json','r') as file_read:
data = json.load(file_read)
data["exchange_contract_address"] = exchange_contract_address
with open('./resources/contracts.json', 'r+') as file_write:
json.dump(data,file_write, indent=4)
def set_pt():
"""
get payment token address and add it to json
"""
exchange_contract, exchange_contract_address = get_exchange_contract()
exchange_bytecode, exchange_abi = compile_exchange()
exchange_contract = w3.eth.contract(address=exchange_contract_address, abi=exchange_abi)
pt_address = exchange_contract.caller({'from': exchange_addr}).getPtAddress()
print("PT address - {}\n".format(pt_address))
with open('./resources/contracts.json','r') as file_read:
data = json.load(file_read)
data["pt_address"] = pt_address
with open('./resources/contracts.json', 'r+') as file_write:
json.dump(data,file_write, indent=4)
def mint_pt(mm_num, pt_val):
"""
Mint payment token for a sender
Arguments:
mm_num: index for Market maker
pt_val: payment token amount to be minted
"""
with open('./resources/contracts.json','r') as file_read:
data = json.load(file_read)
exchange_contract_address = data["exchange_contract_address"]
exchange_bytecode, exchange_abi = compile_exchange()
exchange_contract = w3.eth.contract(address=exchange_contract_address, abi=exchange_abi)
construct_txn = exchange_contract.functions.mintAndTransferPT(pt_val, mm_addr[mm_num]).buildTransaction({
'from': exchange_addr,
'gas': 2000000,
'gasPrice': 0,
'nonce': w3.eth.get_transaction_count(exchange_addr),
'chainId': 5000
})
tx_receipt_poll(construct_txn, exchange_key)
balance = exchange_contract.caller({'from': exchange_addr}).get_balance_pt(mm_addr[mm_num])
print("MM{} ({}) got balance - {} PT".format(mm_num+1, mm_addr[mm_num], balance))
def mint_st():
"""
Mint security token
"""
exchange_contract,exchange_contract_address = get_exchange_contract()
with open('./resources/security-tokens.json') as json_file:
stks = json.load(json_file)["tokens"]
for stk in stks:
construct_txn = exchange_contract.functions.mintAndTransferST(stk["isin"], treasury_addr,
stk["interest_rate"], stk["cost_in_pt"],
stk["maturity_date"],
stk["bond_amount"]).buildTransaction({
'from': exchange_addr,
'gas': 2000000,
'gasPrice': 0,
'nonce': w3.eth.get_transaction_count(exchange_addr),
'chainId': 5000
})
tx_receipt_poll(construct_txn, exchange_key)
balance = exchange_contract.caller({'from': exchange_addr}).get_balance_st(treasury_addr, stk["isin"])
print("Treasury got balance - {} ST for ISIN - {}".format(balance, stk["isin"]))
def swap_token_start(mm_num, st_val, st_isin):
"""
call swapTokens method from exchange
Arguments:
mm_num: market maker number
st_val: security token amount
st_isin: ISIN of security token
"""
exchange_contract, exchange_contract_address = get_exchange_contract()
construct_txn = exchange_contract.functions.swapTokens(mm_addr[mm_num], treasury_addr, st_isin, st_val).buildTransaction({
'from': exchange_addr,
'gas': 2000000,
'gasPrice': 0,
'nonce': w3.eth.get_transaction_count(exchange_addr),
'chainId': 5000
})
tx_receipt_poll(construct_txn, exchange_key)
def increase_allowance_pt(mm_num, allowance_val):
"""
Increase allowance for Payment token for exchange
Arguments:
mm_num: Market maker index
allowance_val: allowance amount
"""
with open('./resources/contracts.json','r') as file_read:
data = json.load(file_read)
exchange_contract_address = data["exchange_contract_address"]
pt_address = data["pt_address"]
compiled_sol = compile_files(
["contracts/PaymentToken.sol"], solc_version='0.8.9', optimize=True)
pt_abi = compiled_sol['contracts/PaymentToken.sol:PaymentToken']['abi']
pt_contract = w3.eth.contract(address=pt_address, abi=pt_abi)
construct_txn = pt_contract.functions.increaseAllowance(exchange_contract_address, allowance_val).buildTransaction({
'from': mm_addr[mm_num],
'gas': 2000000,
'gasPrice': 0,
'nonce': w3.eth.get_transaction_count(mm_addr[mm_num]),
'chainId': 5000
})
tx_receipt_poll(construct_txn, mm_key[mm_num])
allowance = pt_contract.caller({'from': mm_addr[mm_num]}).allowance(mm_addr[mm_num], exchange_contract_address)
print("\nAllowance of Exchange is - {} PT".format(allowance))
def decrease_allowance_pt(mm_num, allowance_val):
"""
Decrease allowance for Payment token for exchange
Arguments:
mm_num: Market maker index
allowance_val: allowance amount
"""
with open('./resources/contracts.json','r') as file_read:
data = json.load(file_read)
exchange_contract_address = data["exchange_contract_address"]
pt_address = data["pt_address"]
compiled_sol = compile_files(
["contracts/PaymentToken.sol"], solc_version='0.8.9', optimize=True)
pt_abi = compiled_sol['contracts/PaymentToken.sol:PaymentToken']['abi']
pt_contract = w3.eth.contract(address=pt_address, abi=pt_abi)
construct_txn = pt_contract.functions.decreaseAllowance(exchange_contract_address, allowance_val).buildTransaction({
'from': mm_addr[mm_num],
'gas': 2000000,
'gasPrice': 0,
'nonce': w3.eth.get_transaction_count(mm_addr[mm_num]),
'chainId': 5000
})
tx_receipt_poll(construct_txn, mm_key[mm_num])
allowance = pt_contract.caller({'from': mm_addr[mm_num]}).allowance(mm_addr[mm_num], exchange_contract_address)
print("\nAllowance of Exchange is - {} PT".format(allowance))
def increase_allowance_st(st_isin, allowance_val):
"""
Increase allowance for Security token for exchange
Arguments:
st_isin: ISIN for security token
allowance_val: allowance amount
"""
exchange_contract, exchange_contract_address = get_exchange_contract()
st_address = exchange_contract.caller({'from': exchange_addr}).getStAddress(st_isin)
compiled_sol = compile_files(
["contracts/SecurityToken.sol"], solc_version='0.8.9', optimize=True)
st_abi = compiled_sol['contracts/SecurityToken.sol:SecurityToken']['abi']
st_contract = w3.eth.contract(address=st_address, abi=st_abi)
construct_txn = st_contract.functions.increaseAllowance(exchange_contract_address, allowance_val).buildTransaction({
'from': treasury_addr,
'gas': 2000000,
'gasPrice': 0,
'nonce': w3.eth.get_transaction_count(treasury_addr),
'chainId': 5000
})
tx_receipt_poll(construct_txn, treasury_key)
allowance = st_contract.caller({'from': treasury_addr}).allowance(treasury_addr, exchange_contract_address)
print("\nAllowance of Exchange is - {} ST ({})".format(allowance, st_isin))
def decrease_allowance_st(st_isin, allowance_val):
"""
Decrease allowance for Security token for exchange
Arguments:
st_isin: ISIN for security token
allowance_val: allowance amount
"""
exchange_contract, exchange_contract_address = get_exchange_contract()
st_address = exchange_contract.caller({'from': exchange_addr}).getStAddress(st_isin)
compiled_sol = compile_files(
["contracts/SecurityToken.sol"], solc_version='0.8.9', optimize=True)
st_abi = compiled_sol['contracts/SecurityToken.sol:SecurityToken']['abi']
st_contract = w3.eth.contract(address=st_address, abi=st_abi)
construct_txn = st_contract.functions.decreaseAllowance(exchange_contract_address, allowance_val).buildTransaction({
'from': treasury_addr,
'gas': 2000000,
'gasPrice': 0,
'nonce': w3.eth.get_transaction_count(treasury_addr),
'chainId': 5000
})
tx_receipt_poll(construct_txn, treasury_key)
allowance = st_contract.caller({'from': treasury_addr}).allowance(treasury_addr, exchange_contract_address)
print("\nAllowance of Exchange is - {} ST ({})".format(allowance, st_isin))
def swap_token_complete(transaction_num, st_isin, mm_num):
"""
call _swapTokens to complete swap
Arguments:
transaction_num: transaction number
st_isin: ISIN for security Token
mm_num: market maker index
"""
exchange_contract, exchange_contract_address = get_exchange_contract()
construct_txn = exchange_contract.functions._swapTokens(transaction_num).buildTransaction({
'from': exchange_addr,
'gas': 2000000,
'gasPrice': 0,
'nonce': w3.eth.get_transaction_count(exchange_addr),
'chainId': 5000
})
tx_receipt_poll(construct_txn, exchange_key)
balancePT = exchange_contract.caller({'from': exchange_addr}).get_balance_pt(treasury_addr)
print("\nTreasury got balance - {} PT".format(balancePT))
balanceST = exchange_contract.caller({'from': exchange_addr}).get_balance_st(mm_addr[mm_num], st_isin)
print("MM{} ({}) got balance - {} ST ({})".format(mm_num+1, mm_addr[mm_num], balanceST, st_isin))
def close_issuance():
"""
close issuance and print all balances
burn the ST remaining with treasury at the end issuance
"""
json_file = open('./resources/security-tokens.json')
stks = json.load(json_file)["tokens"]
print("\nbalance for treasury")
exchange_contract, exchange_contract_address = get_exchange_contract()
print("PT balance for treasury : {} ".format(exchange_contract.caller({'from': exchange_addr}).get_balance_pt(treasury_addr)))
for stk in stks:
print("isin : {}".format(stk["isin"]))
st_balance_issuance = exchange_contract.caller({'from': exchange_addr}).get_balance_st(treasury_addr, stk["isin"])
print("ST balance for treasury before burn: {} ".format(st_balance_issuance))
burn_st(st_balance_issuance, stk["isin"])
st_balance_issuance = exchange_contract.caller({'from': exchange_addr}).get_balance_st(treasury_addr, stk["isin"])
print("ST balance for treasury after burn: {} ".format(st_balance_issuance))
print("\nbalance for MMs")
index = 0
for mm in mm_addr:
index = index + 1
print("\nPT balance for mm {} : {} ".format(index, exchange_contract.caller({'from': exchange_addr}).get_balance_pt(mm)))
for st in stks:
print("isin : {}".format(st["isin"]))
print("ST balance for mm {}: {} ".format(index, exchange_contract.caller({'from': exchange_addr}).get_balance_st(mm, st["isin"])))
def burn_st(amount, st_isin):
"""
burn security token for an ST
Arguments:
amount: amount of st token to be burned
st_isin: isin value
Return:
None
"""
exchange_contract, exchange_contract_address = get_exchange_contract()
st_address = exchange_contract.caller({'from': exchange_addr}).getStAddress(st_isin)
compiled_sol = compile_files(
["contracts/SecurityToken.sol"], solc_version='0.8.9', optimize=True)
st_abi = compiled_sol['contracts/SecurityToken.sol:SecurityToken']['abi']
st_contract = w3.eth.contract(address=st_address, abi=st_abi)
construct_txn = st_contract.functions.burn(amount).buildTransaction({
'from': treasury_addr,
'gas': 2000000,
'gasPrice': 0,
'nonce': w3.eth.get_transaction_count(treasury_addr),
'chainId': 5000
})
tx_receipt_poll(construct_txn, treasury_key)
def handle_event(event, event_template):
"""
get event data and decode it
Arguments:
event: event
event_template: event template(object representing event)
"""
try:
result = get_event_data(event_template.web3.codec, event_template._get_event_abi(), event)
return True, result
except:
return False, None
def swap_tokens(mm_num, st_val, st_isin, delay=False):
"""
swap Security token for payment token
Arguments:
mm_num: market make index
st_val: security token amount to be swapped
st_isin: ISIN for security token
:return:
"""
swap_token_start(mm_num, st_val, st_isin)
transaction_number = 0
exchange_contract, exchange_contract_address = get_exchange_contract()
event_template = exchange_contract.events.approvalRequired
events = w3.eth.get_logs({})
for event in events:
# does it get the last one??? this can give incorrect results
suc, res = handle_event(event=event, event_template=event_template)
if suc:
print("\nEvent found", res)
transaction_number = res["args"]["tx_number"]
if mm_addr[mm_num] == res["args"]["from"]:
pt_allowance_val = res["args"]["amount"]
increase_allowance_pt(mm_num, res["args"]["amount"])
if treasury_addr == res["args"]["from"]:
st_allowance_val = res["args"]["amount"]
increase_allowance_st(st_isin, res["args"]["amount"])
try:
# swap token
if delay:
print("\n introduced delay of 60 sec")
time.sleep(60)
swap_token_complete(transaction_number, st_isin, mm_num)
except Exception as e:
print("\nswap token failed - {}".format(e))
print("\nDecreasing allowance")
decrease_allowance_pt(mm_num, pt_allowance_val)
decrease_allowance_st(st_isin, st_allowance_val)
def set_env_var():
"""
export host value to environment
"""
cmd = 'source ./.env'
subprocess.call(cmd, shell=True, stdout=subprocess.PIPE)
def get_exchange_contract():
"""
get exchange details
returns:
exchange contract object, exchange contract address
"""
with open('./resources/contracts.json','r') as file_read:
data = json.load(file_read)
exchange_contract_address = data["exchange_contract_address"]
exchange_bytecode, exchange_abi = compile_exchange()
exchange_contract = w3.eth.contract(address=exchange_contract_address, abi=exchange_abi)
return exchange_contract, exchange_contract_address
def get_balance(account, isin=None):
"""
get balance for an account
Arguments:
account: account address
isin: ISIN value of Security Token
"""
exchange_contract, exchange_contract_address = get_exchange_contract()
if isin:
balance_st = exchange_contract.caller({'from': exchange_addr}).get_balance_st(account, isin)
print("\nST Balance is : {}".format(balance_st))
else:
json_file = open('./resources/security-tokens.json')
stks = json.load(json_file)["tokens"]
for stk in stks:
st_balance = exchange_contract.caller({'from': exchange_addr}).get_balance_st(account,
stk["isin"])
print("\nST Balance for ISIN : {} is : {}".format(stk["isin"], st_balance))
balance_pt = exchange_contract.caller({'from': exchange_addr}).get_balance_pt(account)
print("\nPT Balance is : {}".format(balance_pt))