Compare commits
45 Commits
master
...
devnet-see
Author | SHA1 | Date | |
---|---|---|---|
e239e73cd5 | |||
2055038c80 | |||
679d92ae4d | |||
8abbf2f938 | |||
c3bcd54ed7 | |||
e79665190a | |||
8bc9f5619c | |||
b32d776183 | |||
db815494d3 | |||
682836fa1e | |||
1ea883ae54 | |||
4a4eb0f077 | |||
885271ce69 | |||
b586273f70 | |||
3a9a123648 | |||
01d7de89c2 | |||
7b9bff6541 | |||
2f0b59e579 | |||
e5ed224c72 | |||
e530925d9c | |||
1bbf26ceb1 | |||
c5ff6f3bf1 | |||
d1a794fe37 | |||
9aaa125948 | |||
78fc736ed7 | |||
dc87181e0a | |||
a5e84ba7df | |||
dece337480 | |||
3e42f10625 | |||
|
e343ee8bae | ||
24172aa4b5 | |||
71d157a482 | |||
4da0e5812f | |||
9b513b672d | |||
f6c0e2de04 | |||
541d083fc7 | |||
2bfc1ce3b2 | |||
5061aace41 | |||
a043f33242 | |||
d1a4f2ee5d | |||
083e3e6b69 | |||
baf21fca96 | |||
a3d149a659 | |||
|
f25d45af4c | ||
|
fa64acac1d |
17
.env.example
Normal file
17
.env.example
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#Chain params
|
||||||
|
CHAIN_ID = 44787
|
||||||
|
RPC = https://alfajores-forno.celo-testnet.org
|
||||||
|
GAS_FEE_CAP = 35000000000
|
||||||
|
|
||||||
|
|
||||||
|
#Amount of gas to topup each generated account
|
||||||
|
GAS_TOPUP = "0.01ether"
|
||||||
|
|
||||||
|
#address to token index
|
||||||
|
TOKEN_INDEX = 0xD774bc082003eaF8DF74eEcD43AD44F03D488418
|
||||||
|
|
||||||
|
#Private key with the required permissions
|
||||||
|
MASTER_PRIVATE_KEY = 1e1d0c1519479f68d9
|
||||||
|
|
||||||
|
#Token required to perform custodial api calls
|
||||||
|
BEARER_TOKEN=eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9
|
13
custodial_seed_commands.txt
Normal file
13
custodial_seed_commands.txt
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
VOUCHER_CREATE - MIA MIAPESA 6
|
||||||
|
VOUCHER_CREATE - MES MAASAIPESA 6
|
||||||
|
VOUCHER_CREATE - KMP KAMBAPESA 6
|
||||||
|
KEY_CREATE - mijikenda custodialstore
|
||||||
|
KEY_CREATE - kamba custodialstore
|
||||||
|
KEY_CREATE - maasai custodialstore
|
||||||
|
KEY_CREATE - agiriama custodialstore
|
||||||
|
VOUCHER_MINT - MIA 100000000 mijikenda
|
||||||
|
VOUCHER_MINT - MES 100000000 maasai
|
||||||
|
VOUCHER_MINT - KMP 100000000 kamba
|
||||||
|
VOUCHER_TRANSFER - MIA 2000000 kamba mijikenda
|
||||||
|
VOUCHER_TRANSFER - MES 2000000 mijikenda maasai
|
||||||
|
VOUCHER_TRANSFER - KMP 2000000 mijikenda kamba
|
756
parse.py
756
parse.py
@ -1,73 +1,131 @@
|
|||||||
|
import json
|
||||||
|
import random
|
||||||
|
import re
|
||||||
|
import string
|
||||||
|
import time
|
||||||
import ply.lex as lex
|
import ply.lex as lex
|
||||||
import ply.yacc as yacc
|
import ply.yacc as yacc
|
||||||
import enum
|
import enum
|
||||||
import uuid
|
import uuid
|
||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
|
import sys
|
||||||
|
import select
|
||||||
from multiprocessing import Process
|
from multiprocessing import Process
|
||||||
from multiprocessing import Value
|
|
||||||
from multiprocessing import Pipe
|
from multiprocessing import Pipe
|
||||||
|
import subprocess
|
||||||
|
import requests
|
||||||
|
import requests.adapters
|
||||||
|
from web3 import Web3
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
logg = logging.getLogger()
|
logg = logging.getLogger()
|
||||||
logg.setLevel(logging.DEBUG)
|
logg.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
|
||||||
tokens = (
|
tokens = (
|
||||||
'ID',
|
"ID",
|
||||||
'NOID',
|
"NOID",
|
||||||
'HEX',
|
"HEX",
|
||||||
'LABEL',
|
"LABEL",
|
||||||
'VALUE',
|
"VALUE",
|
||||||
'END',
|
"END",
|
||||||
)
|
)
|
||||||
|
|
||||||
t_ID = r'[a-zA-Z0-9]+(-[a-zA-Z0-9]+)+'
|
t_ID = r"[a-zA-Z0-9]+(-[a-zA-Z0-9]+)+"
|
||||||
t_NOID = r'\-'
|
t_NOID = r"\-"
|
||||||
t_LABEL = r'[a-zA-Z_]+'
|
t_LABEL = r"[a-zA-Z_]+"
|
||||||
t_VALUE = r'\d+'
|
t_VALUE = r"\d+"
|
||||||
t_END = r'\n'
|
t_END = r"\n"
|
||||||
t_HEX = r'0x([a-fA-F0-9])+'
|
t_HEX = r"0x([a-fA-F0-9])+"
|
||||||
|
|
||||||
#def t_VALUE(t):
|
# def t_VALUE(t):
|
||||||
# r'\d+'
|
# r'\d+'
|
||||||
# t.value = int(t.value)
|
# t.value = int(t.value)
|
||||||
|
|
||||||
#def t_HEX(t):
|
# def t_HEX(t):
|
||||||
# r'0x([a-fA-F0-9])+'
|
# r'0x([a-fA-F0-9])+'
|
||||||
#print(t)
|
# print(t)
|
||||||
#try:
|
# try:
|
||||||
# t.value = bytes.fromhex(t.value)
|
# t.value = bytes.fromhex(t.value)
|
||||||
#except:
|
# except:
|
||||||
# return False
|
# return False
|
||||||
#return t.value
|
# return t.value
|
||||||
#t.value = t.value
|
# t.value = t.value
|
||||||
# pass
|
# pass
|
||||||
|
|
||||||
t_ignore = ' \t'
|
t_ignore = " \t"
|
||||||
|
|
||||||
|
|
||||||
def t_error(t):
|
def t_error(t):
|
||||||
print("problem: ", t.value[0])
|
print("problem: ", t.value[0])
|
||||||
t.lexer.skip(1)
|
t.lexer.skip(1)
|
||||||
|
|
||||||
|
|
||||||
lexer = lex.lex()
|
lexer = lex.lex()
|
||||||
|
|
||||||
|
|
||||||
|
load_dotenv(override=True)
|
||||||
|
|
||||||
|
# Chain Params
|
||||||
|
chainId = os.getenv("CHAIN_ID", "44787")
|
||||||
|
rpc = os.getenv("RPC", "https://alfajores-forno.celo-testnet.org/")
|
||||||
|
gas_cap = os.getenv("GAS_FEE_CAP", "35000000000")
|
||||||
|
|
||||||
|
|
||||||
|
master_private_key = os.getenv("MASTER_PRIVATE_KEY")
|
||||||
|
token_index = os.getenv("TOKEN_INDEX", "0xD774bc082003eaF8DF74eEcD43AD44F03D488418")
|
||||||
|
gas_topup = os.getenv("GAS_TOPUP", "0.01ether")
|
||||||
|
|
||||||
|
bearer_token = os.getenv("BEARER_TOKEN")
|
||||||
|
|
||||||
|
w3 = Web3(Web3.HTTPProvider(rpc))
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
#data = '''
|
# data = '''
|
||||||
#FOOBAR uf-2etg 0xa3bdefa momo 123
|
# FOOBAR uf-2etg 0xa3bdefa momo 123
|
||||||
#
|
#
|
||||||
#BARBAR
|
# BARBAR
|
||||||
#BAZ
|
# BAZ
|
||||||
#
|
#
|
||||||
#BASFB foo-bar-baz
|
# BASFB foo-bar-baz
|
||||||
#'''
|
#'''
|
||||||
#
|
#
|
||||||
#lexer.input(data)
|
# lexer.input(data)
|
||||||
#
|
#
|
||||||
#while True:
|
# while True:
|
||||||
# tok = lexer.token()
|
# tok = lexer.token()
|
||||||
# if not tok:
|
# if not tok:
|
||||||
# break
|
# break
|
||||||
# print(tok)
|
# print(tok)
|
||||||
|
|
||||||
|
|
||||||
|
class VoucherTransfer:
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
to_address=None,
|
||||||
|
from_address=None,
|
||||||
|
amount=None,
|
||||||
|
token_address=None,
|
||||||
|
decimals=None,
|
||||||
|
):
|
||||||
|
self.to_address = to_address
|
||||||
|
self.from_address = from_address
|
||||||
|
self.decimals = decimals
|
||||||
|
self.amount = amount
|
||||||
|
self.token_address = token_address
|
||||||
|
|
||||||
|
|
||||||
|
class VoucherDetail:
|
||||||
|
def __init__(self, name=None, symbol=None, decimals=None, owner=None, address=None):
|
||||||
|
self.name = name
|
||||||
|
self.owner = owner
|
||||||
|
self.address = address
|
||||||
|
self.symbol = symbol
|
||||||
|
self.decimals = decimals
|
||||||
|
|
||||||
|
|
||||||
class CmdId(enum.IntEnum):
|
class CmdId(enum.IntEnum):
|
||||||
KEY_CREATE = 0x1
|
KEY_CREATE = 0x1
|
||||||
VOUCHER_CREATE = 0x10
|
VOUCHER_CREATE = 0x10
|
||||||
@ -80,14 +138,15 @@ class CmdId(enum.IntEnum):
|
|||||||
|
|
||||||
class Agent:
|
class Agent:
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.__class__.__name__ + ':' #{}'.format(self.__class__.__name__, self.v)
|
return (
|
||||||
|
self.__class__.__name__ + ":"
|
||||||
|
) # {}'.format(self.__class__.__name__, self.v)
|
||||||
|
|
||||||
|
|
||||||
class AddressAgent(Agent):
|
class AddressAgent(Agent):
|
||||||
def __init__(self, v):
|
def __init__(self, v):
|
||||||
self.v = bytes.fromhex(v[2:])
|
self.v = bytes.fromhex(v[2:])
|
||||||
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return Agent.__str__(self) + self.v.hex()
|
return Agent.__str__(self) + self.v.hex()
|
||||||
|
|
||||||
@ -99,8 +158,8 @@ class NameAgent(Agent):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return Agent.__str__(self) + self.v
|
return Agent.__str__(self) + self.v
|
||||||
|
|
||||||
class Cmd:
|
|
||||||
|
|
||||||
|
class Cmd:
|
||||||
def __init__(self, cmd):
|
def __init__(self, cmd):
|
||||||
self.c = CmdId[cmd]
|
self.c = CmdId[cmd]
|
||||||
self.i = None
|
self.i = None
|
||||||
@ -114,9 +173,21 @@ class Cmd:
|
|||||||
self.s = None
|
self.s = None
|
||||||
self.n = None
|
self.n = None
|
||||||
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "[{:02d}]{} i={} v={} a={} f={} t={} k={} d={} p={} s={} n={}".format(self.c, self.c.name, self.i, self.v, self.a, self.f, self.t, self.k, self.d, self.p, self.s, self.n)
|
return "[{:02d}]{} i={} v={} a={} f={} t={} k={} d={} p={} s={} n={}".format(
|
||||||
|
self.c,
|
||||||
|
self.c.name,
|
||||||
|
self.i,
|
||||||
|
self.v,
|
||||||
|
self.a,
|
||||||
|
self.f,
|
||||||
|
self.t,
|
||||||
|
self.k,
|
||||||
|
self.d,
|
||||||
|
self.p,
|
||||||
|
self.s,
|
||||||
|
self.n,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def to_store(v):
|
def to_store(v):
|
||||||
@ -133,19 +204,19 @@ def to_agent(v):
|
|||||||
|
|
||||||
|
|
||||||
def p_cmd(p):
|
def p_cmd(p):
|
||||||
'''cmd : key_create
|
"""cmd : key_create
|
||||||
| voucher_mint
|
| voucher_mint
|
||||||
| voucher_transfer
|
| voucher_transfer
|
||||||
| voucher_create
|
| voucher_create
|
||||||
| pair
|
| pair
|
||||||
'''
|
"""
|
||||||
p[0] = p[1]
|
p[0] = p[1]
|
||||||
|
|
||||||
|
|
||||||
def p_key_create(p):
|
def p_key_create(p):
|
||||||
'''key_create : change_label LABEL LABEL
|
"""key_create : change_label LABEL LABEL
|
||||||
| change_label LABEL LABEL HEX
|
| change_label LABEL LABEL HEX
|
||||||
'''
|
"""
|
||||||
o = p[1]
|
o = p[1]
|
||||||
o.f = NameAgent(p[2])
|
o.f = NameAgent(p[2])
|
||||||
o.t = to_store(p[3])
|
o.t = to_store(p[3])
|
||||||
@ -155,9 +226,9 @@ def p_key_create(p):
|
|||||||
|
|
||||||
|
|
||||||
def p_voucher_mint(p):
|
def p_voucher_mint(p):
|
||||||
'''voucher_mint : change_label hv
|
"""voucher_mint : change_label hv
|
||||||
| change_label nv
|
| change_label nv
|
||||||
'''
|
"""
|
||||||
o = p[1]
|
o = p[1]
|
||||||
if o.c.value & 0x10 == 0:
|
if o.c.value & 0x10 == 0:
|
||||||
raise ValueError("not a voucher command")
|
raise ValueError("not a voucher command")
|
||||||
@ -167,9 +238,9 @@ def p_voucher_mint(p):
|
|||||||
|
|
||||||
|
|
||||||
def p_voucher_create(p):
|
def p_voucher_create(p):
|
||||||
'''voucher_create : change_label LABEL LABEL VALUE
|
"""voucher_create : change_label LABEL LABEL VALUE
|
||||||
| change_label LABEL LABEL VALUE VALUE VALUE
|
| change_label LABEL LABEL VALUE VALUE VALUE
|
||||||
'''
|
"""
|
||||||
o = p[1]
|
o = p[1]
|
||||||
if o.c.value & 0x10 == 0:
|
if o.c.value & 0x10 == 0:
|
||||||
raise ValueError("not a voucher command")
|
raise ValueError("not a voucher command")
|
||||||
@ -183,11 +254,11 @@ def p_voucher_create(p):
|
|||||||
|
|
||||||
|
|
||||||
def p_voucher_mint_recipient(p):
|
def p_voucher_mint_recipient(p):
|
||||||
'''voucher_mint : change_label hv HEX
|
"""voucher_mint : change_label hv HEX
|
||||||
| change_label nv HEX
|
| change_label nv HEX
|
||||||
| change_label hv LABEL
|
| change_label hv LABEL
|
||||||
| change_label nv LABEL
|
| change_label nv LABEL
|
||||||
'''
|
"""
|
||||||
o = p[1]
|
o = p[1]
|
||||||
if o.c.value & 0x10 == 0:
|
if o.c.value & 0x10 == 0:
|
||||||
raise ValueError("not a voucher command")
|
raise ValueError("not a voucher command")
|
||||||
@ -198,11 +269,15 @@ def p_voucher_mint_recipient(p):
|
|||||||
|
|
||||||
|
|
||||||
def p_voucher_transfer(p):
|
def p_voucher_transfer(p):
|
||||||
'''voucher_transfer : change_label hv HEX HEX
|
"""voucher_transfer : change_label hv HEX HEX
|
||||||
| change_label nv HEX HEX
|
| change_label nv HEX HEX
|
||||||
| change_label hv LABEL HEX
|
| change_label hv LABEL HEX
|
||||||
| change_label nv LABEL HEX
|
| change_label nv LABEL HEX
|
||||||
'''
|
| change_label hv LABEL LABEL
|
||||||
|
| change_label nv LABEL LABEL
|
||||||
|
| change_label hv HEX LABEL
|
||||||
|
| change_label nv HEX LABEL
|
||||||
|
"""
|
||||||
o = p[1]
|
o = p[1]
|
||||||
if o.c.value & 0x10 == 0:
|
if o.c.value & 0x10 == 0:
|
||||||
raise ValueError("not a voucher command")
|
raise ValueError("not a voucher command")
|
||||||
@ -214,103 +289,612 @@ def p_voucher_transfer(p):
|
|||||||
|
|
||||||
|
|
||||||
def p_nv(p):
|
def p_nv(p):
|
||||||
'nv : LABEL VALUE'
|
"nv : LABEL VALUE"
|
||||||
p[0] = (p[1], p[2],)
|
p[0] = (
|
||||||
|
p[1],
|
||||||
|
p[2],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def p_hv(p):
|
def p_hv(p):
|
||||||
'hv : HEX VALUE'
|
"hv : HEX VALUE"
|
||||||
p[0] = (p[1], p[2],)
|
p[0] = (
|
||||||
|
p[1],
|
||||||
|
p[2],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def p_change_label(p):
|
def p_change_label(p):
|
||||||
'''change_label : pair
|
"""change_label : pair
|
||||||
| pairnoid
|
| pairnoid
|
||||||
'''
|
"""
|
||||||
p[0] = p[1]
|
p[0] = p[1]
|
||||||
|
|
||||||
|
|
||||||
def p_pair(p):
|
def p_pair(p):
|
||||||
'pair : LABEL ID'
|
"pair : LABEL ID"
|
||||||
o = Cmd(p[1])
|
o = Cmd(p[1])
|
||||||
o.i = p[2]
|
o.i = p[2]
|
||||||
p[0] = o
|
p[0] = o
|
||||||
|
|
||||||
|
|
||||||
def p_pairnoid(p):
|
def p_pairnoid(p):
|
||||||
'pairnoid : LABEL NOID'
|
"pairnoid : LABEL NOID"
|
||||||
o = Cmd(p[1])
|
o = Cmd(p[1])
|
||||||
o.i = str(uuid.uuid4())
|
o.i = str(uuid.uuid4())
|
||||||
p[0] = o
|
p[0] = o
|
||||||
|
|
||||||
|
|
||||||
class Router:
|
class Router:
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.__routes = {}
|
self.__routes = {}
|
||||||
self.__r = {}
|
self.__r = {}
|
||||||
|
|
||||||
|
|
||||||
def register(self, cmd_id, fn):
|
def register(self, cmd_id, fn):
|
||||||
self.__routes[cmd_id] = fn
|
self.__routes[cmd_id] = fn
|
||||||
|
|
||||||
|
|
||||||
def sync(self, uid):
|
def sync(self, uid):
|
||||||
o = self.__r[uid]
|
o = self.__r[uid]
|
||||||
if o[1] == None:
|
if o[1] == None:
|
||||||
return None
|
return None
|
||||||
r = o[1][0].recv()
|
r = o[1][0].recv()
|
||||||
o = (o[0], None,)
|
o = (
|
||||||
|
o[0],
|
||||||
|
None,
|
||||||
|
)
|
||||||
o[0].join()
|
o[0].join()
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
|
||||||
def __wrap(self, uid, fn, cmd):
|
def __wrap(self, uid, fn, cmd):
|
||||||
r = fn(cmd)
|
r = fn(cmd)
|
||||||
self.__r[uid][1][1].send(r)
|
self.__r[uid][1][1].send(r)
|
||||||
|
|
||||||
|
|
||||||
def exec(self, cmd):
|
def exec(self, cmd):
|
||||||
if cmd.c & 0xa0 > 0:
|
logg.debug("router exec {}".format(cmd))
|
||||||
|
if cmd.c & 0xA0 > 0:
|
||||||
return self.sync(cmd.i)
|
return self.sync(cmd.i)
|
||||||
fn = self.__routes[cmd.c]
|
fn = self.__routes[cmd.c]
|
||||||
pi = Pipe(False)
|
pi = Pipe(False)
|
||||||
po = Process(target=self.__wrap, args=(cmd.i, fn, cmd,))
|
po = Process(
|
||||||
self.__r[cmd.i] = (po, pi,)
|
target=self.__wrap,
|
||||||
|
args=(
|
||||||
|
cmd.i,
|
||||||
|
fn,
|
||||||
|
cmd,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
self.__r[cmd.i] = (
|
||||||
|
po,
|
||||||
|
pi,
|
||||||
|
)
|
||||||
po.start()
|
po.start()
|
||||||
|
po.join()
|
||||||
|
|
||||||
|
def finish(self):
|
||||||
|
print("syncing")
|
||||||
|
for k, v in self.__r.items():
|
||||||
|
print("syncing key " + k)
|
||||||
|
r = self.sync(k)
|
||||||
|
logg.debug("synced " + k + ": " + r)
|
||||||
|
print("synced " + k + ": " + r)
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
for k, v in self.__r.items():
|
self.finish()
|
||||||
r = self.sync(k)
|
|
||||||
logging.debug("synced " + k + ": " + r)
|
|
||||||
|
|
||||||
|
|
||||||
def noop_handler(cmd):
|
def noop_handler(cmd):
|
||||||
return str(cmd)
|
return str(cmd)
|
||||||
|
|
||||||
|
|
||||||
def foo_handler(cmd):
|
def printMessage(message):
|
||||||
return os.popen('eth-info -p https://celo.grassecon.net').read()
|
box_width = len(message) + 4
|
||||||
|
print("+" + "-" * (box_width - 2) + "+")
|
||||||
|
print("| " + message + " |")
|
||||||
|
print("+" + "-" * (box_width - 2) + "+")
|
||||||
|
|
||||||
|
|
||||||
|
def find_custodial_address(key_name):
|
||||||
|
directory = "custodialstore"
|
||||||
|
filename = f"{key_name}.json"
|
||||||
|
file_path = os.path.join(directory, filename)
|
||||||
|
|
||||||
|
# Check if the file exists with the keyname
|
||||||
|
if os.path.isfile(file_path):
|
||||||
|
with open(file_path, "r") as f:
|
||||||
|
custodial_account = json.load(f)
|
||||||
|
return custodial_account["address"]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def remove_ansi_escape_codes(text):
|
||||||
|
return re.sub(r"\u001b\[.*?m", "", text)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_private_key():
|
||||||
|
"""Generate a new private key."""
|
||||||
|
web3 = Web3()
|
||||||
|
account = web3.eth.account.create()
|
||||||
|
return account.address, w3.to_hex(account.key)
|
||||||
|
|
||||||
|
|
||||||
|
def store_key_in_keystore(keystore_dir, private_key, key_name, address):
|
||||||
|
# Create the directory if it doesn't exist
|
||||||
|
if not os.path.exists(keystore_dir):
|
||||||
|
os.makedirs(keystore_dir)
|
||||||
|
|
||||||
|
keystore = {
|
||||||
|
"key_name": key_name,
|
||||||
|
"private_key": private_key,
|
||||||
|
"address": address,
|
||||||
|
}
|
||||||
|
|
||||||
|
store_path = os.path.join(keystore_dir, f"{key_name}.json")
|
||||||
|
|
||||||
|
# Save to JSON file (simulated keystore)
|
||||||
|
with open(store_path, "w") as f:
|
||||||
|
json.dump(keystore, f)
|
||||||
|
|
||||||
|
return store_path
|
||||||
|
|
||||||
|
|
||||||
|
def load_gas(address, nonce):
|
||||||
|
command = (
|
||||||
|
f"cast send {address} "
|
||||||
|
f"--value {gas_topup} "
|
||||||
|
f"--nonce {nonce} "
|
||||||
|
f"--private-key {master_private_key} "
|
||||||
|
f"--rpc-url {rpc} "
|
||||||
|
f" --json "
|
||||||
|
)
|
||||||
|
result = subprocess.run(command, shell=True, capture_output=True, text=True)
|
||||||
|
if result.returncode != 0:
|
||||||
|
raise subprocess.CalledProcessError(
|
||||||
|
result.returncode, command, output=result.stdout, stderr=result.stderr
|
||||||
|
)
|
||||||
|
|
||||||
|
message = f"Added {gas_topup} to {address}"
|
||||||
|
printMessage(message)
|
||||||
|
|
||||||
|
|
||||||
|
def create_custodialaccount():
|
||||||
|
# create custodial account endpoint
|
||||||
|
url = "http://localhost:5003/api/v2/account/create"
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
"Authorization": f"Bearer {bearer_token}",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.post(url, headers=headers)
|
||||||
|
|
||||||
|
# Check if the request was successful (status code 200)
|
||||||
|
if response.status_code == 200:
|
||||||
|
account = response.json()
|
||||||
|
public_key = account["result"]["publicKey"]
|
||||||
|
return public_key
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
print("Error:", e)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def do_custodial_token_transfer(transfer):
|
||||||
|
# token transfer custodial endpoint
|
||||||
|
url = "http://localhost:5003/api/v2/token/transfer"
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
"Authorization": f"Bearer {bearer_token}",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
}
|
||||||
|
|
||||||
|
data = {
|
||||||
|
"from": transfer.from_address,
|
||||||
|
"to": transfer.to_address,
|
||||||
|
"amount": str(transfer.amount),
|
||||||
|
"tokenAddress": transfer.token_address,
|
||||||
|
}
|
||||||
|
json_data = json.dumps(data)
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.post(url=url, headers=headers, data=json_data)
|
||||||
|
# Check if the request was successful (status code 200)
|
||||||
|
if response.status_code == 200:
|
||||||
|
transfer = response.json()
|
||||||
|
return transfer["result"]["trackingId"]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
print("Error:", e)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def store_voucher(voucher_detail):
|
||||||
|
vouchers = []
|
||||||
|
voucher = {
|
||||||
|
"voucher_address": voucher_detail.address,
|
||||||
|
"owner": voucher_detail.owner,
|
||||||
|
"symbol": voucher_detail.symbol,
|
||||||
|
"decimals": voucher_detail.decimals,
|
||||||
|
}
|
||||||
|
vouchers.append(voucher)
|
||||||
|
try:
|
||||||
|
with open("vouchers.json", "r") as f:
|
||||||
|
existing_vouchers = json.load(f)
|
||||||
|
except FileNotFoundError:
|
||||||
|
existing_vouchers = []
|
||||||
|
|
||||||
|
for entry in existing_vouchers:
|
||||||
|
vouchers.append(entry)
|
||||||
|
|
||||||
|
with open("vouchers.json", "w") as f:
|
||||||
|
json.dump(vouchers, f)
|
||||||
|
|
||||||
|
|
||||||
|
def key_create_handler(cmd):
|
||||||
|
key_name = str(cmd.f).split(":")[1]
|
||||||
|
store_name = cmd.t
|
||||||
|
|
||||||
|
keystore_dir = "user_store"
|
||||||
|
master_address = w3.eth.account.from_key(master_private_key)
|
||||||
|
nonce = w3.eth.get_transaction_count(master_address.address, "pending")
|
||||||
|
|
||||||
|
if cmd.k is None:
|
||||||
|
address, private_key = generate_private_key()
|
||||||
|
else:
|
||||||
|
if cmd.k.startswith("0x"):
|
||||||
|
private_key = cmd.k[2:]
|
||||||
|
else:
|
||||||
|
private_key = cmd.k
|
||||||
|
address = w3.eth.account.from_key(private_key).address
|
||||||
|
|
||||||
|
if store_name == "custodialstore":
|
||||||
|
address = create_custodialaccount()
|
||||||
|
if address is None:
|
||||||
|
raise ValueError("account address cannot be None")
|
||||||
|
private_key = None
|
||||||
|
keystore_dir = "custodialstore"
|
||||||
|
|
||||||
|
load_gas(address, nonce)
|
||||||
|
|
||||||
|
store_key_in_keystore(keystore_dir, private_key, key_name, address)
|
||||||
|
return address
|
||||||
|
|
||||||
|
|
||||||
|
def voucher_create_handler(cmd):
|
||||||
|
symbol = cmd.s
|
||||||
|
|
||||||
|
if cmd.n is None or cmd.s is None or cmd.v is None:
|
||||||
|
raise ValueError("cmd.n, cmd.s, and cmd.v must not be None")
|
||||||
|
|
||||||
|
voucher_detail = VoucherDetail()
|
||||||
|
|
||||||
|
voucher_detail.name = cmd.n
|
||||||
|
voucher_detail.decimals = cmd.v
|
||||||
|
|
||||||
|
random_ascii = "".join(random.choices(string.ascii_letters, k=4)).upper()
|
||||||
|
try:
|
||||||
|
with open("vouchers.json", "r") as f:
|
||||||
|
existing_vouchers = json.load(f)
|
||||||
|
except FileNotFoundError:
|
||||||
|
existing_vouchers = []
|
||||||
|
|
||||||
|
voucher_symbols = list(map(lambda symbol: symbol["symbol"], existing_vouchers))
|
||||||
|
|
||||||
|
if symbol in voucher_symbols:
|
||||||
|
symbol = symbol + random_ascii
|
||||||
|
|
||||||
|
voucher_detail.symbol = symbol
|
||||||
|
|
||||||
|
if master_private_key.startswith("0x"):
|
||||||
|
private_key = master_private_key[2:]
|
||||||
|
else:
|
||||||
|
private_key = master_private_key
|
||||||
|
|
||||||
|
voucher_detail.owner = private_key
|
||||||
|
|
||||||
|
# Command to create a voucher
|
||||||
|
publish_token = (
|
||||||
|
f"ge-publish --private-key {voucher_detail.owner} --json "
|
||||||
|
f"--rpc {rpc} --gas-fee-cap {gas_cap} --chainid {chainId} "
|
||||||
|
f'p erc20 --name "{voucher_detail.name}" --symbol "{voucher_detail.symbol}"'
|
||||||
|
)
|
||||||
|
|
||||||
|
result = subprocess.run(publish_token, shell=True, capture_output=True, text=True)
|
||||||
|
if result.returncode != 0:
|
||||||
|
raise subprocess.CalledProcessError(
|
||||||
|
result.returncode, publish_token, output=result.stdout, stderr=result.stderr
|
||||||
|
)
|
||||||
|
|
||||||
|
output_lines = result.stderr.strip().split("\n")
|
||||||
|
deployment_result = output_lines[1]
|
||||||
|
|
||||||
|
try:
|
||||||
|
data = json.loads(deployment_result)
|
||||||
|
contract_address = data.get("contract_address", None)
|
||||||
|
voucher_detail.address = contract_address
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
print("Error parsing JSON:", e)
|
||||||
|
|
||||||
|
store_voucher(voucher_detail)
|
||||||
|
|
||||||
|
# sleep for 5 second to allow chain to sync
|
||||||
|
time.sleep(5)
|
||||||
|
|
||||||
|
master_address = w3.eth.account.from_key(master_private_key)
|
||||||
|
nonce = w3.eth.get_transaction_count(master_address.address, "pending")
|
||||||
|
|
||||||
|
# Command to add the token to the token index
|
||||||
|
add_token_to_index = (
|
||||||
|
f"cast send --private-key {master_private_key} "
|
||||||
|
f"--nonce {nonce} "
|
||||||
|
f"--rpc-url {rpc} "
|
||||||
|
f"{token_index} "
|
||||||
|
f'"add(address)" {contract_address} '
|
||||||
|
f" --json "
|
||||||
|
)
|
||||||
|
|
||||||
|
result2 = subprocess.run(
|
||||||
|
add_token_to_index, shell=True, capture_output=True, text=True
|
||||||
|
)
|
||||||
|
if result2.returncode != 0:
|
||||||
|
raise subprocess.CalledProcessError(
|
||||||
|
result2.returncode,
|
||||||
|
add_token_to_index,
|
||||||
|
output=result2.stdout,
|
||||||
|
stderr=result2.stderr,
|
||||||
|
)
|
||||||
|
|
||||||
|
message = f"Voucher {voucher_detail.name} created with address {contract_address} and symbol {symbol} and added to token index: {token_index}"
|
||||||
|
printMessage(message)
|
||||||
|
|
||||||
|
return contract_address
|
||||||
|
|
||||||
|
|
||||||
|
def voucher_transfer_handler(cmd):
|
||||||
|
voucher_transfer = VoucherTransfer()
|
||||||
|
|
||||||
|
# Amount to transfer
|
||||||
|
value = cmd.v # Amount to transfer
|
||||||
|
is_custodial_address = False
|
||||||
|
|
||||||
|
voucher_transfer.amount = value
|
||||||
|
|
||||||
|
# Token symbol to transfer
|
||||||
|
if str(cmd.a).startswith("NameAgent"):
|
||||||
|
voucher_name = str(cmd.a).split(":")[1]
|
||||||
|
with open("vouchers.json", "r") as file:
|
||||||
|
data = json.load(file)
|
||||||
|
|
||||||
|
voucher_symbols = list(map(lambda symbol: symbol["symbol"], data))
|
||||||
|
voucher_address = list(map(lambda address: address["voucher_address"], data))
|
||||||
|
voucher_decimal = list(map(lambda decimal: decimal["decimals"], data))
|
||||||
|
|
||||||
|
if voucher_name in voucher_symbols:
|
||||||
|
index = voucher_symbols.index(voucher_name)
|
||||||
|
voucher_transfer.token_address = voucher_address[index]
|
||||||
|
voucher_transfer.decimals = voucher_decimal[index]
|
||||||
|
|
||||||
|
elif str(cmd.a).startswith("AddressAgent"):
|
||||||
|
voucher_transfer.token_address = "0x" + str(cmd.a).split(":")[1]
|
||||||
|
else:
|
||||||
|
raise ValueError(
|
||||||
|
f"Invalid command: {cmd.t}. Expected 'NameAgent' or 'AddressAgent'."
|
||||||
|
)
|
||||||
|
|
||||||
|
if str(cmd.t).startswith("NameAgent"):
|
||||||
|
key_name = str(cmd.t).split(":")[1]
|
||||||
|
|
||||||
|
custodial_address = find_custodial_address(key_name)
|
||||||
|
if custodial_address is not None:
|
||||||
|
to = custodial_address
|
||||||
|
voucher_transfer.to_address = to
|
||||||
|
else:
|
||||||
|
store_path = os.path.join("user_store", f"{key_name}.json")
|
||||||
|
with open(store_path, "r") as file:
|
||||||
|
data = json.load(file)
|
||||||
|
acct = w3.eth.account.from_key(data["private_key"])
|
||||||
|
to = acct.address
|
||||||
|
voucher_transfer.to_address = to
|
||||||
|
elif str(cmd.t).startswith("AddressAgent"):
|
||||||
|
to = "0x" + str(cmd.t).split(":")[1]
|
||||||
|
voucher_transfer.to_address = to
|
||||||
|
else:
|
||||||
|
raise ValueError(
|
||||||
|
f"Invalid command: {cmd.t}. Expected 'NameAgent' or 'AddressAgent'."
|
||||||
|
)
|
||||||
|
|
||||||
|
if str(cmd.f).startswith("NameAgent"):
|
||||||
|
key_name = str(cmd.f).split(":")[1]
|
||||||
|
custodial_address = find_custodial_address(key_name)
|
||||||
|
if custodial_address is not None:
|
||||||
|
is_custodial_address = True
|
||||||
|
voucher_transfer.from_address = custodial_address
|
||||||
|
else:
|
||||||
|
store_path = os.path.join("user_store", f"{key_name}.json")
|
||||||
|
with open(store_path, "r") as file:
|
||||||
|
data = json.load(file)
|
||||||
|
from_private_key = data["private_key"]
|
||||||
|
|
||||||
|
elif str(cmd.f).startswith("AddressAgent"):
|
||||||
|
from_private_key = "0x" + str(cmd.f).split(":")[1]
|
||||||
|
else:
|
||||||
|
raise ValueError(
|
||||||
|
f"Invalid command: {cmd.t}. Expected 'NameAgent' or 'AddressAgent'."
|
||||||
|
)
|
||||||
|
|
||||||
|
amount_transfered = value / pow(10, voucher_transfer.decimals)
|
||||||
|
|
||||||
|
if is_custodial_address:
|
||||||
|
tracking_id = do_custodial_token_transfer(voucher_transfer)
|
||||||
|
if tracking_id is not None:
|
||||||
|
message = f"Transfered {amount_transfered} {voucher_name} to {to} "
|
||||||
|
printMessage(message)
|
||||||
|
return tracking_id
|
||||||
|
else:
|
||||||
|
command = (
|
||||||
|
f"cast send --private-key {from_private_key} "
|
||||||
|
f"--rpc-url {rpc} "
|
||||||
|
f"{voucher_transfer.token_address} "
|
||||||
|
f'"transfer(address,uint256)" {to} {value}'
|
||||||
|
f" --json "
|
||||||
|
)
|
||||||
|
result = subprocess.run(command, shell=True, capture_output=True, text=True)
|
||||||
|
if result.returncode != 0:
|
||||||
|
raise subprocess.CalledProcessError(
|
||||||
|
result.returncode, command, output=result.stdout, stderr=result.stderr
|
||||||
|
)
|
||||||
|
if result.stderr:
|
||||||
|
raise ValueError(f"Command failed with error: {result.stderr}")
|
||||||
|
data = json.loads(result.stdout)
|
||||||
|
message = f"Transfered {amount_transfered} {voucher_name} to {to} "
|
||||||
|
printMessage(message)
|
||||||
|
return data["transactionHash"]
|
||||||
|
|
||||||
|
|
||||||
|
def voucher_mint_handler(cmd):
|
||||||
|
value = cmd.v
|
||||||
|
master_address = w3.eth.account.from_key(master_private_key)
|
||||||
|
nonce = w3.eth.get_transaction_count(master_address.address, "pending")
|
||||||
|
if str(cmd.t).startswith("NameAgent"):
|
||||||
|
key_name = str(cmd.t).split(":")[1]
|
||||||
|
custodial_address = find_custodial_address(key_name)
|
||||||
|
if custodial_address is not None:
|
||||||
|
to = custodial_address
|
||||||
|
else:
|
||||||
|
store_path = os.path.join("user_store", f"{key_name}.json")
|
||||||
|
with open(store_path, "r") as file:
|
||||||
|
data = json.load(file)
|
||||||
|
acct = w3.eth.account.from_key(data["private_key"])
|
||||||
|
to = acct.address
|
||||||
|
elif str(cmd.t).startswith("AddressAgent"):
|
||||||
|
to = "0x" + str(cmd.t).split(":")[1]
|
||||||
|
else:
|
||||||
|
raise ValueError(
|
||||||
|
f"Invalid command: {cmd.t}. Expected 'NameAgent' or 'AddressAgent'."
|
||||||
|
)
|
||||||
|
|
||||||
|
if str(cmd.a).startswith("NameAgent"):
|
||||||
|
voucher_symbol = str(cmd.a).split(":")[1]
|
||||||
|
with open("vouchers.json", "r") as file:
|
||||||
|
data = json.load(file)
|
||||||
|
|
||||||
|
voucher_symbols = list(map(lambda symbol: symbol["symbol"], data))
|
||||||
|
voucher_address = list(map(lambda address: address["voucher_address"], data))
|
||||||
|
|
||||||
|
if voucher_symbol in voucher_symbols:
|
||||||
|
index = voucher_symbols.index(voucher_symbol)
|
||||||
|
s = voucher_address[index]
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Voucher with symbol {voucher_symbol} was not found")
|
||||||
|
|
||||||
|
elif str(cmd.a).startswith("AddressAgent"):
|
||||||
|
s = "0x" + str(cmd.a).split(":")[1]
|
||||||
|
else:
|
||||||
|
raise ValueError(
|
||||||
|
f"Invalid command: {cmd.t}. Expected 'NameAgent' or 'AddressAgent'."
|
||||||
|
)
|
||||||
|
|
||||||
|
command = (
|
||||||
|
f"cast send --private-key {master_private_key} "
|
||||||
|
f"--rpc-url {rpc} "
|
||||||
|
f"--nonce {nonce} "
|
||||||
|
f" {s} "
|
||||||
|
f'"mintTo(address,uint256)" {to} {value}'
|
||||||
|
f" --json "
|
||||||
|
)
|
||||||
|
result = subprocess.run(command, shell=True, capture_output=True, text=True)
|
||||||
|
if result.returncode != 0:
|
||||||
|
raise subprocess.CalledProcessError(
|
||||||
|
result.returncode, command, output=result.stdout, stderr=result.stderr
|
||||||
|
)
|
||||||
|
if result.stderr:
|
||||||
|
raise ValueError(f"Command failed with error: {result.stderr}")
|
||||||
|
data = json.loads(result.stdout)
|
||||||
|
|
||||||
|
mint_amount = value / 10**6
|
||||||
|
message = f"Minted {mint_amount} {voucher_symbol} to :{to}"
|
||||||
|
|
||||||
|
printMessage(message)
|
||||||
|
return data["transactionHash"]
|
||||||
|
|
||||||
|
|
||||||
parser = yacc.yacc()
|
parser = yacc.yacc()
|
||||||
|
|
||||||
cmds = [
|
running = True
|
||||||
"KEY_CREATE foo-bar Foo BAr",
|
|
||||||
"KEY_CREATE bar-baz Foo BAr 0xabcdef0",
|
|
||||||
"VOUCHER_MINT - 0xdeadbeef 13 0xfdaf",
|
class FileGet:
|
||||||
"VOUCHER_MINT baz-baz 0xdeadbeef 13 0xfdaf",
|
def __init__(self, o, fp):
|
||||||
"VOUCHER_MINT bar-bar 0xdeadbeef 42",
|
self.__running = True
|
||||||
"VOUCHER_CREATE foo-foo Foo Bar 42",
|
self.__o = o
|
||||||
"VOUCHER_CREATE inky-pinky-blinky Foo Bar 42 44 233",
|
self.__f = open(fp, "r")
|
||||||
"VOUCHER_TRANSFER clyde-sue 0xbeeffeed0123 666 foo 0x0fdc",
|
|
||||||
]
|
def run(self):
|
||||||
for v in cmds:
|
while self.__running:
|
||||||
o = Router()
|
v = ifc.get()
|
||||||
o.register(CmdId.KEY_CREATE, noop_handler)
|
if v == None:
|
||||||
o.register(CmdId.VOUCHER_MINT, noop_handler)
|
break
|
||||||
o.register(CmdId.VOUCHER_CREATE, noop_handler)
|
v = v.strip("\n")
|
||||||
o.register(CmdId.VOUCHER_TRANSFER, foo_handler)
|
if len(v) == 0:
|
||||||
|
break
|
||||||
r = parser.parse(v)
|
r = parser.parse(v)
|
||||||
o.exec(r)
|
self.__o.exec(r)
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
return self.__f.readline()
|
||||||
|
|
||||||
|
|
||||||
|
class WaitGet:
|
||||||
|
def __init__(self, o, *r):
|
||||||
|
self.__running = True
|
||||||
|
self.__o = o
|
||||||
|
self.__f = r
|
||||||
|
|
||||||
|
# TODO: router copy results in missing keys to sync when closing down
|
||||||
|
def __process(self, f):
|
||||||
|
while self.__running:
|
||||||
|
r = select.select([f], [], [])
|
||||||
|
v = r[0][0].recv()
|
||||||
|
if v == None:
|
||||||
|
break
|
||||||
|
v = v.strip("\n")
|
||||||
|
if len(v) == 0:
|
||||||
|
break
|
||||||
|
r = parser.parse(v)
|
||||||
|
self.__o.exec(r)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
(
|
||||||
|
fo,
|
||||||
|
fi,
|
||||||
|
) = Pipe()
|
||||||
|
p = Process(target=self.__process, args=(fo,))
|
||||||
|
p.start()
|
||||||
|
while self.__running:
|
||||||
|
v = input("> ")
|
||||||
|
if v == "":
|
||||||
|
fi.send(None)
|
||||||
|
break
|
||||||
|
fi.send(v)
|
||||||
|
p.join()
|
||||||
|
logg.debug("waitget run end")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
ifc = None
|
||||||
|
o = Router()
|
||||||
|
o.register(CmdId.KEY_CREATE, key_create_handler)
|
||||||
|
o.register(CmdId.VOUCHER_CREATE, voucher_create_handler)
|
||||||
|
o.register(CmdId.VOUCHER_MINT, voucher_mint_handler)
|
||||||
|
o.register(CmdId.VOUCHER_TRANSFER, voucher_transfer_handler)
|
||||||
|
|
||||||
|
if len(sys.argv) > 1:
|
||||||
|
ifc = FileGet(o, sys.argv[1])
|
||||||
|
else:
|
||||||
|
ifc = WaitGet(o, sys.stdin)
|
||||||
|
|
||||||
|
ifc.run()
|
||||||
|
o.finish()
|
||||||
|
@ -1,29 +1,61 @@
|
|||||||
aiee==0.3.2
|
aiee==0.3.2
|
||||||
|
aiohappyeyeballs==2.4.3
|
||||||
|
aiohttp==3.10.11
|
||||||
|
aiosignal==1.3.1
|
||||||
|
annotated-types==0.7.0
|
||||||
asn1crypto==1.5.1
|
asn1crypto==1.5.1
|
||||||
|
async-timeout==5.0.1
|
||||||
|
attrs==24.2.0
|
||||||
|
bitarray==3.0.0
|
||||||
|
certifi==2024.8.30
|
||||||
cffi==1.17.1
|
cffi==1.17.1
|
||||||
chainlib==0.5.4
|
chainlib==0.5.4
|
||||||
chainlib-eth==0.6.3
|
chainlib-eth==0.6.3
|
||||||
|
charset-normalizer==3.4.0
|
||||||
|
ckzg==2.0.1
|
||||||
coincurve==15.0.0
|
coincurve==15.0.0
|
||||||
confini==0.6.5
|
confini==0.6.5
|
||||||
cryptography==3.2.1
|
cryptography==3.2.1
|
||||||
cytoolz==1.0.0
|
cytoolz==1.0.0
|
||||||
|
eth-account==0.13.4
|
||||||
eth-erc20==0.9.0
|
eth-erc20==0.9.0
|
||||||
eth-hash==0.7.0
|
eth-hash==0.7.0
|
||||||
|
eth-keyfile==0.8.1
|
||||||
|
eth-keys==0.6.0
|
||||||
|
eth-rlp==2.1.0
|
||||||
eth-typing==5.0.1
|
eth-typing==5.0.1
|
||||||
eth-utils==2.3.2
|
eth-utils
|
||||||
|
eth_abi==5.1.0
|
||||||
|
frozenlist==1.5.0
|
||||||
funga==0.5.7
|
funga==0.5.7
|
||||||
funga-eth==0.8.0
|
funga-eth==0.8.0
|
||||||
hexathon==0.1.7
|
hexathon==0.1.7
|
||||||
|
hexbytes==1.2.1
|
||||||
|
idna==3.10
|
||||||
json-rpc==1.13.0
|
json-rpc==1.13.0
|
||||||
|
multidict==6.1.0
|
||||||
nats-py==2.9.0
|
nats-py==2.9.0
|
||||||
|
parsimonious==0.10.0
|
||||||
ply==3.11
|
ply==3.11
|
||||||
potaahto==0.1.2
|
potaahto==0.1.2
|
||||||
|
propcache==0.2.0
|
||||||
pycparser==2.22
|
pycparser==2.22
|
||||||
pycryptodome==3.10.1
|
pycryptodome==3.10.1
|
||||||
|
pydantic==2.10.1
|
||||||
|
pydantic_core==2.27.1
|
||||||
|
pysha3==1.0.2
|
||||||
|
python-dotenv==1.0.1
|
||||||
python-gnupg==0.4.9
|
python-gnupg==0.4.9
|
||||||
rlp==3.0.0
|
pyunormalize==16.0.0
|
||||||
|
regex==2024.11.6
|
||||||
|
requests==2.32.3
|
||||||
safe-pysha3==1.0.4
|
safe-pysha3==1.0.4
|
||||||
six==1.16.0
|
six==1.16.0
|
||||||
toolz==1.0.0
|
toolz==1.0.0
|
||||||
|
types-requests==2.32.0.20241016
|
||||||
typing_extensions==4.12.2
|
typing_extensions==4.12.2
|
||||||
|
urllib3==2.2.3
|
||||||
|
web3==6.1.0
|
||||||
websocket-client==0.57.0
|
websocket-client==0.57.0
|
||||||
|
websockets==13.1
|
||||||
|
yarl==1.15.2
|
||||||
|
1
vouchers.json
Normal file
1
vouchers.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
[{"voucher_address": "0xe1E6362959a1E868443F063aD0aBAFab4A41Bcc2", "owner": "1e1d0c1519479f68d9c8d07352a8e7e7cb9e2c676bce422f84502412cf3954ba", "symbol": "KMPJYQA", "decimals": 6}, {"voucher_address": "0xC4a7c6873b4B57EfFECE675dD8143a86d6DA5f5B", "owner": "1e1d0c1519479f68d9c8d07352a8e7e7cb9e2c676bce422f84502412cf3954ba", "symbol": "MESVLIG", "decimals": 6}, {"voucher_address": "0x167691968E9Cc3f81788eaD15cbB18e3437cFb70", "owner": "1e1d0c1519479f68d9c8d07352a8e7e7cb9e2c676bce422f84502412cf3954ba", "symbol": "MIAVGEF", "decimals": 6}, {"voucher_address": "0xa6E7258b375a98af44aECFc30e1B14BFc60913F0", "owner": "1e1d0c1519479f68d9c8d07352a8e7e7cb9e2c676bce422f84502412cf3954ba", "symbol": "KMPNRAR", "decimals": 6}, {"voucher_address": "0xeCb48fcEd893757f7a956E556a215f2E8523b067", "owner": "1e1d0c1519479f68d9c8d07352a8e7e7cb9e2c676bce422f84502412cf3954ba", "symbol": "MESROLE", "decimals": 6}, {"voucher_address": "0x57B62c8ea79032fF21C95A0CC2b89433259dc8f6", "owner": "1e1d0c1519479f68d9c8d07352a8e7e7cb9e2c676bce422f84502412cf3954ba", "symbol": "MIAPRNF", "decimals": 6}, {"voucher_address": "0x850FD17BeE9C849c4Ed61B3bA4D3c54576641595", "owner": "1e1d0c1519479f68d9c8d07352a8e7e7cb9e2c676bce422f84502412cf3954ba", "symbol": "KMPDHKA", "decimals": 6}, {"voucher_address": "0x03ceb8E85d661626C421094ce7A0AF3a3FEEF8A3", "owner": "1e1d0c1519479f68d9c8d07352a8e7e7cb9e2c676bce422f84502412cf3954ba", "symbol": "MESPUFA", "decimals": 6}, {"voucher_address": "0x58e3Fa578D42cCd9Af8E8875a37d9C378456C47D", "owner": "1e1d0c1519479f68d9c8d07352a8e7e7cb9e2c676bce422f84502412cf3954ba", "symbol": "MIAFWKG", "decimals": 6}, {"voucher_address": "0x0CA60E4b670599466125Ef48FC9d733E2B8927E5", "owner": "1e1d0c1519479f68d9c8d07352a8e7e7cb9e2c676bce422f84502412cf3954ba", "symbol": "KMPFXA", "decimals": 6}, {"voucher_address": "0x4EAbC5070721c9FE18d463Fb75bf6E2A04FbAeE7", "owner": "1e1d0c1519479f68d9c8d07352a8e7e7cb9e2c676bce422f84502412cf3954ba", "symbol": "MESAWX", "decimals": 6}, {"voucher_address": "0x803EC23bd814b954E938c2cF28356ea0241629D4", "owner": "1e1d0c1519479f68d9c8d07352a8e7e7cb9e2c676bce422f84502412cf3954ba", "symbol": "MIAVJA", "decimals": 6}, {"voucher_address": "0xCe131E4f7fC9f23Ee7768E76a8D14852b3B044aF", "owner": "1e1d0c1519479f68d9c8d07352a8e7e7cb9e2c676bce422f84502412cf3954ba", "symbol": "KMPLV", "decimals": 6}, {"voucher_address": "0xE276Dd85280B962D04F81CCee0eD295a92EC1Fa1", "owner": "1e1d0c1519479f68d9c8d07352a8e7e7cb9e2c676bce422f84502412cf3954ba", "symbol": "MESPH", "decimals": 6}, {"voucher_address": "0x4AFC95baBA518ec7F6172fDD720645f7a80B4540", "owner": "1e1d0c1519479f68d9c8d07352a8e7e7cb9e2c676bce422f84502412cf3954ba", "symbol": "MIAZV", "decimals": 6}, {"voucher_address": "0xAD8e88D24017aFdf5c32deab6fb6104518b75b05", "owner": "1e1d0c1519479f68d9c8d07352a8e7e7cb9e2c676bce422f84502412cf3954ba", "symbol": "KMPCY", "decimals": 6}, {"voucher_address": "0x4721fE870d23F97Ac0E74980061e922b7180508E", "owner": "1e1d0c1519479f68d9c8d07352a8e7e7cb9e2c676bce422f84502412cf3954ba", "symbol": "MESSH", "decimals": 6}, {"voucher_address": "0x7f5d2c9E9bFFe2f80D03a3D609Ce82C2c7fA78C2", "owner": "1e1d0c1519479f68d9c8d07352a8e7e7cb9e2c676bce422f84502412cf3954ba", "symbol": "MIAWX", "decimals": 6}, {"voucher_address": "0x49666B0E0FFB6A8316708dE7b0CbB0085aC04a63", "owner": "1e1d0c1519479f68d9c8d07352a8e7e7cb9e2c676bce422f84502412cf3954ba", "symbol": "KMP", "decimals": 6}, {"voucher_address": "0x66bA02957ae7D8C624e0BE8a7223D0fF942F6608", "owner": "1e1d0c1519479f68d9c8d07352a8e7e7cb9e2c676bce422f84502412cf3954ba", "symbol": "MES", "decimals": 6}, {"voucher_address": "0xC3744229442b410fc5CD5D438a8Bc0A749D8E4eC", "owner": "1e1d0c1519479f68d9c8d07352a8e7e7cb9e2c676bce422f84502412cf3954ba", "symbol": "MIA", "decimals": 6}]
|
Loading…
Reference in New Issue
Block a user