From df3a86eb26b174a92747c17d9989a00654b74770 Mon Sep 17 00:00:00 2001 From: nolash Date: Mon, 4 Oct 2021 16:32:39 +0200 Subject: [PATCH] Initial commit --- python/okota/accounts_index/__init__.py | 1 + python/okota/accounts_index/accounts_index.py | 54 ++++++++ .../okota/accounts_index/runnable/deploy.py | 85 +++++++++++++ .../data/AccountsIndexAddressDeclarator.bin | 1 + .../data/AccountsIndexAddressDeclarator.json | 1 + ...okenUniqueSymbolIndexAddressDeclarator.bin | 1 + ...kenUniqueSymbolIndexAddressDeclarator.json | 1 + python/okota/token_index/__init__.py | 1 + python/okota/token_index/index.py | 77 ++++++++++++ python/okota/token_index/interface.py | 113 +++++++++++++++++ python/okota/token_index/runnable/deploy.py | 78 ++++++++++++ python/requirements.txt | 7 ++ python/setup.cfg | 50 ++++++++ python/setup.py | 25 ++++ python/test_requirements.txt | 2 + python/tests/test_accounts_index.py | 72 +++++++++++ python/tests/test_tokenindex.py | 87 +++++++++++++ solidity/AccountsIndexAddressDeclarator.sol | 50 ++++++++ solidity/Makefile | 20 +++ ...okenUniqueSymbolIndexAddressDeclarator.sol | 115 ++++++++++++++++++ 20 files changed, 841 insertions(+) create mode 100644 python/okota/accounts_index/__init__.py create mode 100644 python/okota/accounts_index/accounts_index.py create mode 100644 python/okota/accounts_index/runnable/deploy.py create mode 100644 python/okota/data/AccountsIndexAddressDeclarator.bin create mode 100644 python/okota/data/AccountsIndexAddressDeclarator.json create mode 100644 python/okota/data/TokenUniqueSymbolIndexAddressDeclarator.bin create mode 100644 python/okota/data/TokenUniqueSymbolIndexAddressDeclarator.json create mode 100644 python/okota/token_index/__init__.py create mode 100644 python/okota/token_index/index.py create mode 100644 python/okota/token_index/interface.py create mode 100644 python/okota/token_index/runnable/deploy.py create mode 100644 python/requirements.txt create mode 100644 python/setup.cfg create mode 100644 python/setup.py create mode 100644 python/test_requirements.txt create mode 100644 python/tests/test_accounts_index.py create mode 100644 python/tests/test_tokenindex.py create mode 100644 solidity/AccountsIndexAddressDeclarator.sol create mode 100644 solidity/Makefile create mode 100644 solidity/TokenUniqueSymbolIndexAddressDeclarator.sol diff --git a/python/okota/accounts_index/__init__.py b/python/okota/accounts_index/__init__.py new file mode 100644 index 0000000..8ea4c7a --- /dev/null +++ b/python/okota/accounts_index/__init__.py @@ -0,0 +1 @@ +from .accounts_index import * diff --git a/python/okota/accounts_index/accounts_index.py b/python/okota/accounts_index/accounts_index.py new file mode 100644 index 0000000..d3d7387 --- /dev/null +++ b/python/okota/accounts_index/accounts_index.py @@ -0,0 +1,54 @@ +# standard imports +import os + +# external imports +from chainlib.eth.tx import ( + TxFormat, + ) +from chainlib.eth.contract import ( + ABIContractEncoder, + ABIContractType, + ) +from eth_accounts_index.interface import AccountsIndex + +moddir = os.path.dirname(__file__) +datadir = os.path.join(moddir, '..', 'data') + + +class AccountsIndexAddressDeclarator(AccountsIndex): + + __abi = None + __bytecode = None + + @staticmethod + def abi(): + if AccountsIndexAddressDeclarator.__abi == None: + f = open(os.path.join(datadir, 'AccountsIndexAddressDeclarator.json'), 'r') + AccountsIndexAddressDeclarator.__abi = json.load(f) + f.close() + return AccountsIndexAddressDeclarator.__abi + + + @staticmethod + def bytecode(): + if AccountsIndexAddressDeclarator.__bytecode == None: + f = open(os.path.join(datadir, 'AccountsIndexAddressDeclarator.bin')) + AccountsIndexAddressDeclarator.__bytecode = f.read() + f.close() + return AccountsIndexAddressDeclarator.__bytecode + + + @staticmethod + def gas(code=None): + return 700000 + + + def constructor(self, sender_address, context_address, address_declarator_address): + code = AccountsIndexAddressDeclarator.bytecode() + tx = self.template(sender_address, None, use_nonce=True) + enc = ABIContractEncoder() + enc.address(context_address) + enc.address(address_declarator_address) + code += enc.get() + tx = self.set_code(tx, code) + return self.build(tx) diff --git a/python/okota/accounts_index/runnable/deploy.py b/python/okota/accounts_index/runnable/deploy.py new file mode 100644 index 0000000..5935e20 --- /dev/null +++ b/python/okota/accounts_index/runnable/deploy.py @@ -0,0 +1,85 @@ +"""Deploys accounts index, registering arbitrary number of writers + +.. moduleauthor:: Louis Holbrook +.. pgp:: 0826EDA1702D1E87C6E2875121D2E7BB88C2A746 + +""" + +# standard imports +import sys +import os +import json +import argparse +import logging + +# external imports +import chainlib.eth.cli +from chainlib.chain import ChainSpec +from chainlib.eth.connection import EthHTTPConnection +from chainlib.eth.tx import receipt + +# local imports +from eth_address_declarator.accounts_index import AccountsIndexAddressDeclarator + +logging.basicConfig(level=logging.WARNING) +logg = logging.getLogger() + +arg_flags = chainlib.eth.cli.argflag_std_write +argparser = chainlib.eth.cli.ArgumentParser(arg_flags) +argparser.add_argument('--address-declarator', type=str, required=True, dest='address_declarator', help='address declarator backend address') +argparser.add_argument('--token-address', type=str, required=True, dest='token_address', help='token address context for accounts registry') +args = argparser.parse_args() + +extra_args = { + 'address_declarator': None, + 'token_address': None, + } +config = chainlib.eth.cli.Config.from_args(args, arg_flags, extra_args=extra_args, default_fee_limit=AccountsIndexAddressDeclarator.gas()) + +wallet = chainlib.eth.cli.Wallet() +wallet.from_config(config) + +rpc = chainlib.eth.cli.Rpc(wallet=wallet) +conn = rpc.connect_by_config(config) + +chain_spec = ChainSpec.from_chain_str(config.get('CHAIN_SPEC')) + + +def main(): + signer = rpc.get_signer() + signer_address = rpc.get_sender_address() + + gas_oracle = rpc.get_gas_oracle() + nonce_oracle = rpc.get_nonce_oracle() + + address_declarator = config.get('_ADDRESS_DECLARATOR') + if not config.true('_UNSAFE') and not is_checksum_address(address_declarator): + raise ValueError('address declarator {} is not a valid checksum address'.format(address_declarator)) + + token_address = config.get('_TOKEN_ADDRESS') + if not config.true('_UNSAFE') and not is_checksum_address(token_address): + raise ValueError('token {} is not a valid checksum address'.format(token_address)) + + c = AccountsIndexAddressDeclarator(chain_spec, signer=signer, gas_oracle=gas_oracle, nonce_oracle=nonce_oracle) + + (tx_hash_hex, o) = c.constructor(signer_address, token_address, address_declarator) + + if config.get('_RPC_SEND'): + conn.do(o) + if config.get('_WAIT'): + r = conn.wait(tx_hash_hex) + if r['status'] == 0: + sys.stderr.write('EVM revert while deploying contract. Wish I had more to tell you') + sys.exit(1) + # TODO: pass through translator for keys (evm tester uses underscore instead of camelcase) + address = r['contractAddress'] + + print(address) + else: + print(tx_hash_hex) + else: + print(o) + + +if __name__ == '__main__': + main() diff --git a/python/okota/data/AccountsIndexAddressDeclarator.bin b/python/okota/data/AccountsIndexAddressDeclarator.bin new file mode 100644 index 0000000..8d0f1ab --- /dev/null +++ b/python/okota/data/AccountsIndexAddressDeclarator.bin @@ -0,0 +1 @@ +60806040523480156200001157600080fd5b5060405162000ace38038062000ace8339818101604052810190620000379190620001d9565b606033600560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555081600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550826000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040516020016200012e9190620002b3565b60405160208183030381529060405290506002816040516200015191906200029a565b602060405180830381855afa1580156200016f573d6000803e3d6000fd5b5050506040513d601f19601f8201168201806040525081019062000194919062000220565b600181905550600160048190555050505062000393565b600081519050620001bc816200035f565b92915050565b600081519050620001d38162000379565b92915050565b60008060408385031215620001f357620001f26200035a565b5b60006200020385828601620001ab565b92505060206200021685828601620001ab565b9150509250929050565b6000602082840312156200023957620002386200035a565b5b60006200024984828501620001c2565b91505092915050565b6200025d81620002e6565b82525050565b60006200027082620002d0565b6200027c8185620002db565b93506200028e81856020860162000324565b80840191505092915050565b6000620002a8828462000263565b915081905092915050565b6000602082019050620002ca600083018462000252565b92915050565b600081519050919050565b600081905092915050565b6000620002f38262000304565b9050919050565b6000819050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60005b838110156200034457808201518184015260208101905062000327565b8381111562000354576000848401525b50505050565b600080fd5b6200036a81620002e6565b81146200037657600080fd5b50565b6200038481620002fa565b81146200039057600080fd5b50565b61072b80620003a36000396000f3fe608060405234801561001057600080fd5b5060043610610074576000357c0100000000000000000000000000000000000000000000000000000000900480630a3b0a4f14610079578063370f91fb146100a95780633ef25013146100c75780638da5cb5b146100f75780639d76ea5814610115575b600080fd5b610093600480360381019061008e9190610496565b610133565b6040516100a0919061057c565b60405180910390f35b6100b16103c6565b6040516100be9190610538565b60405180910390f35b6100e160048036038101906100dc9190610496565b6103ec565b6040516100ee919061057c565b60405180910390f35b6100ff610437565b60405161010c9190610538565b60405180910390f35b61011d61045d565b60405161012a9190610538565b60405180910390f35b60008060606000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1685600154604051602401610188929190610553565b6040516020818303038152906040527fae47ece0000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516102129190610521565b6000604051808303816000865af19150503d806000811461024f576040519150601f19603f3d011682016040523d82523d6000602084013e610254565b606091505b5080935081945050508261026757600080fd5b60017f01000000000000000000000000000000000000000000000000000000000000000282601f8151811061029f5761029e6106aa565b5b60200101517f010000000000000000000000000000000000000000000000000000000000000090047f0100000000000000000000000000000000000000000000000000000000000000027effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161461031557600080fd5b600454905080600360008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506004600081548092919061037190610632565b9190505550808573ffffffffffffffffffffffffffffffffffffffff167f9cc987676e7d63379f176ea50df0ae8d2d9d1141d1231d4ce15b5965f73c943060405160405180910390a360019350505050919050565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600080600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054119050919050565b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600081359050610490816106de565b92915050565b6000602082840312156104ac576104ab6106d9565b5b60006104ba84828501610481565b91505092915050565b6104cc816105ad565b82525050565b6104db816105bf565b82525050565b6104ea816105cb565b82525050565b60006104fb82610597565b61050581856105a2565b93506105158185602086016105ff565b80840191505092915050565b600061052d82846104f0565b915081905092915050565b600060208201905061054d60008301846104c3565b92915050565b600060408201905061056860008301856104c3565b61057560208301846104e1565b9392505050565b600060208201905061059160008301846104d2565b92915050565b600081519050919050565b600081905092915050565b60006105b8826105d5565b9050919050565b60008115159050919050565b6000819050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b60005b8381101561061d578082015181840152602081019050610602565b8381111561062c576000848401525b50505050565b600061063d826105f5565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214156106705761066f61067b565b5b600182019050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600080fd5b6106e7816105ad565b81146106f257600080fd5b5056fea26469706673582212201f78efc5447ec317cfce0b750b61e3d42cfebbea205576a2e8924af71a2492d364736f6c63430008070033 \ No newline at end of file diff --git a/python/okota/data/AccountsIndexAddressDeclarator.json b/python/okota/data/AccountsIndexAddressDeclarator.json new file mode 100644 index 0000000..0c04d04 --- /dev/null +++ b/python/okota/data/AccountsIndexAddressDeclarator.json @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"address","name":"_tokenAddress","type":"address"},{"internalType":"address","name":"_addressDeclaratorAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"addedAccount","type":"address"},{"indexed":true,"internalType":"uint256","name":"accountIndex","type":"uint256"}],"name":"AddressAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"add","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"addressDeclaratorAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"have","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}] diff --git a/python/okota/data/TokenUniqueSymbolIndexAddressDeclarator.bin b/python/okota/data/TokenUniqueSymbolIndexAddressDeclarator.bin new file mode 100644 index 0000000..1fde786 --- /dev/null +++ b/python/okota/data/TokenUniqueSymbolIndexAddressDeclarator.bin @@ -0,0 +1 @@ +60806040523480156200001157600080fd5b50604051620014543803806200145483398181016040528101906200003791906200013a565b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600460009080600181540180825580915050600190039060005260206000200160009091909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050620001bf565b6000815190506200013481620001a5565b92915050565b600060208284031215620001535762000152620001a0565b5b6000620001638482850162000123565b91505092915050565b6000620001798262000180565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600080fd5b620001b0816200016c565b8114620001bc57600080fd5b50565b61128580620001cf6000396000f3fe608060405234801561001057600080fd5b50600436106100c6576000357c01000000000000000000000000000000000000000000000000000000009004806379ba50971161008e57806379ba5097146101975780637ef50298146101b55780638da5cb5b146101e5578063bb34534c14610203578063e2095c0714610233578063f2fde38b14610263576100c6565b806301ffc9a7146100cb5780630a3b0a4f146100fb5780630cbb0f831461012b578063370f91fb146101495780634420e48614610167575b600080fd5b6100e560048036038101906100e09190610dc8565b610293565b6040516100f29190610f33565b60405180910390f35b61011560048036038101906101109190610d41565b61043e565b6040516101229190610f33565b60405180910390f35b610133610450565b6040516101409190610f4e565b60405180910390f35b610151610469565b60405161015e9190610eef565b60405180910390f35b610181600480360381019061017c9190610d41565b61048f565b60405161018e9190610f33565b60405180910390f35b61019f610935565b6040516101ac9190610f33565b60405180910390f35b6101cf60048036038101906101ca9190610d6e565b610ad7565b6040516101dc9190610f4e565b60405180910390f35b6101ed610aef565b6040516101fa9190610eef565b60405180910390f35b61021d60048036038101906102189190610d6e565b610b13565b60405161022a9190610eef565b60405180910390f35b61024d60048036038101906102489190610e3e565b610b73565b60405161025a9190610eef565b60405180910390f35b61027d60048036038101906102789190610d41565b610bc7565b60405161028a9190610f33565b60405180910390f35b600063cbdb05c77c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614156102e85760019050610439565b63bb34534c7c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916141561033b5760019050610439565b6301ffc9a77c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916141561038e5760019050610439565b639493f8b27c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614156103e15760019050610439565b6337a47be47c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614156104345760019050610439565b600090505b919050565b60006104498261048f565b9050919050565b60006001600480549050610464919061102b565b905090565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104ea57600080fd5b60006060806000808673ffffffffffffffffffffffffffffffffffffffff166040516024016040516020818303038152906040527f95d89b41000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516105999190610ed8565b6000604051808303816000865af19150503d80600081146105d6576040519150601f19603f3d011682016040523d82523d6000602084013e6105db565b606091505b508095508196505050846105ee57600080fd5b838060200190518101906106029190610df5565b92506002836040516106149190610ed8565b602060405180830381855afa158015610631573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906106549190610d9b565b9150600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1687836040516024016106a2929190610f0a565b6040516020818303038152906040527fae47ece0000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161072c9190610ed8565b6000604051808303816000865af19150503d8060008114610769576040519150601f19603f3d011682016040523d82523d6000602084013e61076e565b606091505b5080955081965050508461078157600080fd5b60017f01000000000000000000000000000000000000000000000000000000000000000284601f815181106107b9576107b8611170565b5b60200101517f010000000000000000000000000000000000000000000000000000000000000090047f0100000000000000000000000000000000000000000000000000000000000000027effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161461082f57600080fd5b600360008381526020019081526020016000205490506000811461085257600080fd5b60048054905060036000848152602001908152602001600020819055506004879080600181540180825580915050600190039060005260206000200160009091909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060016004805490506108e4919061102b565b8773ffffffffffffffffffffffffffffffffffffffff167f9cc987676e7d63379f176ea50df0ae8d2d9d1141d1231d4ce15b5965f73c943060405160405180910390a3600195505050505050919050565b600080600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461099257600080fd5b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35090565b60036020528060005260406000206000915090505481565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000806003600084815260200190815260200160002054905060048181548110610b4057610b3f611170565b5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16915050919050565b60006004600183610b849190610fd5565b81548110610b9557610b94611170565b5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610c2257600080fd5b81600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550919050565b6000610c7b610c7684610f8e565b610f69565b905082815260208101848484011115610c9757610c966111d3565b5b610ca28482856110dd565b509392505050565b600081359050610cb9816111f3565b92915050565b600081359050610cce8161120a565b92915050565b600081519050610ce38161120a565b92915050565b600081359050610cf881611221565b92915050565b600082601f830112610d1357610d126111ce565b5b8151610d23848260208601610c68565b91505092915050565b600081359050610d3b81611238565b92915050565b600060208284031215610d5757610d566111dd565b5b6000610d6584828501610caa565b91505092915050565b600060208284031215610d8457610d836111dd565b5b6000610d9284828501610cbf565b91505092915050565b600060208284031215610db157610db06111dd565b5b6000610dbf84828501610cd4565b91505092915050565b600060208284031215610dde57610ddd6111dd565b5b6000610dec84828501610ce9565b91505092915050565b600060208284031215610e0b57610e0a6111dd565b5b600082015167ffffffffffffffff811115610e2957610e286111d8565b5b610e3584828501610cfe565b91505092915050565b600060208284031215610e5457610e536111dd565b5b6000610e6284828501610d2c565b91505092915050565b610e748161105f565b82525050565b610e8381611071565b82525050565b610e928161107d565b82525050565b6000610ea382610fbf565b610ead8185610fca565b9350610ebd8185602086016110dd565b80840191505092915050565b610ed2816110d3565b82525050565b6000610ee48284610e98565b915081905092915050565b6000602082019050610f046000830184610e6b565b92915050565b6000604082019050610f1f6000830185610e6b565b610f2c6020830184610e89565b9392505050565b6000602082019050610f486000830184610e7a565b92915050565b6000602082019050610f636000830184610ec9565b92915050565b6000610f73610f84565b9050610f7f8282611110565b919050565b6000604051905090565b600067ffffffffffffffff821115610fa957610fa861119f565b5b610fb2826111e2565b9050602081019050919050565b600081519050919050565b600081905092915050565b6000610fe0826110d3565b9150610feb836110d3565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156110205761101f611141565b5b828201905092915050565b6000611036826110d3565b9150611041836110d3565b92508282101561105457611053611141565b5b828203905092915050565b600061106a826110b3565b9050919050565b60008115159050919050565b6000819050919050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b60005b838110156110fb5780820151818401526020810190506110e0565b8381111561110a576000848401525b50505050565b611119826111e2565b810181811067ffffffffffffffff821117156111385761113761119f565b5b80604052505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b6111fc8161105f565b811461120757600080fd5b50565b6112138161107d565b811461121e57600080fd5b50565b61122a81611087565b811461123557600080fd5b50565b611241816110d3565b811461124c57600080fd5b5056fea26469706673582212204911abf9d30c9740a3179d08f0501394ea3544c2aac045bfc4b37fff2f33cc8d64736f6c63430008070033 \ No newline at end of file diff --git a/python/okota/data/TokenUniqueSymbolIndexAddressDeclarator.json b/python/okota/data/TokenUniqueSymbolIndexAddressDeclarator.json new file mode 100644 index 0000000..19d6c49 --- /dev/null +++ b/python/okota/data/TokenUniqueSymbolIndexAddressDeclarator.json @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"address","name":"_addressDeclaratorAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"addedAccount","type":"address"},{"indexed":true,"internalType":"uint256","name":"accountIndex","type":"uint256"}],"name":"AddressAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"add","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"addressDeclaratorAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_key","type":"bytes32"}],"name":"addressOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_idx","type":"uint256"}],"name":"entry","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"entryCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"register","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"registry","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_sum","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}] diff --git a/python/okota/token_index/__init__.py b/python/okota/token_index/__init__.py new file mode 100644 index 0000000..e07b40a --- /dev/null +++ b/python/okota/token_index/__init__.py @@ -0,0 +1 @@ +from .interface import * diff --git a/python/okota/token_index/index.py b/python/okota/token_index/index.py new file mode 100644 index 0000000..0f362de --- /dev/null +++ b/python/okota/token_index/index.py @@ -0,0 +1,77 @@ +# Author: Louis Holbrook 0826EDA1702D1E87C6E2875121D2E7BB88C2A746 +# SPDX-License-Identifier: GPL-3.0-or-later +# File-version: 1 +# Description: Python interface to abi and bin files for token index with address declarator backend + +# standard imports +import logging +import json +import os +import hashlib + +# external imports +from chainlib.eth.contract import ( + ABIContractEncoder, + ABIContractType, + abi_decode_single, + ) +from chainlib.eth.tx import ( + TxFactory, + TxFormat, + ) +from chainlib.jsonrpc import JSONRPCRequest +from chainlib.eth.constant import ZERO_ADDRESS +from hexathon import ( + add_0x, + ) + +# local imports +from .interface import ( + TokenUniqueSymbolIndex, + to_identifier, + ) + +logg = logging.getLogger(__name__) + +moddir = os.path.dirname(__file__) +datadir = os.path.join(moddir, '..', 'data') + + + +class TokenUniqueSymbolIndexAddressDeclarator(TokenUniqueSymbolIndex): + + __abi = None + __bytecode = None + + + @staticmethod + def abi(): + if TokenUniqueSymbolIndexAddressDeclarator.__abi == None: + f = open(os.path.join(datadir, 'TokenUniqueSymbolIndexAddressDeclarator.json'), 'r') + TokenUniqueSymbolIndexAddressDeclarator.__abi = json.load(f) + f.close() + return TokenUniqueSymbolIndexAddressDeclarator.__abi + + + @staticmethod + def bytecode(): + if TokenUniqueSymbolIndexAddressDeclarator.__bytecode == None: + f = open(os.path.join(datadir, 'TokenUniqueSymbolIndexAddressDeclarator.bin')) + TokenUniqueSymbolIndexAddressDeclarator.__bytecode = f.read() + f.close() + return TokenUniqueSymbolIndexAddressDeclarator.__bytecode + + + @staticmethod + def gas(code=None): + return 2000000 + + + def constructor(self, sender_address, address_declarator_address): + code = TokenUniqueSymbolIndexAddressDeclarator.bytecode() + tx = self.template(sender_address, None, use_nonce=True) + enc = ABIContractEncoder() + enc.address(address_declarator_address) + code += enc.get() + tx = self.set_code(tx, code) + return self.build(tx) diff --git a/python/okota/token_index/interface.py b/python/okota/token_index/interface.py new file mode 100644 index 0000000..7d7cf9f --- /dev/null +++ b/python/okota/token_index/interface.py @@ -0,0 +1,113 @@ +# Author: Louis Holbrook 0826EDA1702D1E87C6E2875121D2E7BB88C2A746 +# SPDX-License-Identifier: GPL-3.0-or-later +# File-version: 1 +# Description: Python interface to abi and bin files for faucet contracts + +# standard imports +import logging +import json +import os +import hashlib + +# external imports +from chainlib.eth.contract import ( + ABIContractEncoder, + ABIContractType, + abi_decode_single, + ) +from chainlib.eth.tx import ( + TxFactory, + TxFormat, + ) +from chainlib.jsonrpc import JSONRPCRequest +from chainlib.eth.constant import ZERO_ADDRESS +from hexathon import ( + add_0x, + ) + +logg = logging.getLogger(__name__) + +moddir = os.path.dirname(__file__) +datadir = os.path.join(moddir, '..', 'data') + + +def to_identifier(s): + h = hashlib.new('sha256') + h.update(s.encode('utf-8')) + return h.digest().hex() + + +class TokenUniqueSymbolIndex(TxFactory): + + def register(self, contract_address, sender_address, address, tx_format=TxFormat.JSONRPC): + enc = ABIContractEncoder() + enc.method('register') + enc.typ(ABIContractType.ADDRESS) + enc.address(address) + data = enc.get() + tx = self.template(sender_address, contract_address, use_nonce=True) + tx = self.set_code(tx, data) + tx = self.finalize(tx, tx_format) + return tx + + + def address_of(self, contract_address, token_symbol, sender_address=ZERO_ADDRESS, id_generator=None): + j = JSONRPCRequest(id_generator) + o = j.template() + o['method'] = 'eth_call' + enc = ABIContractEncoder() + enc.method('addressOf') + enc.typ(ABIContractType.BYTES32) + token_symbol_digest = to_identifier(token_symbol) + enc.bytes32(token_symbol_digest) + data = add_0x(enc.get()) + tx = self.template(sender_address, contract_address) + tx = self.set_code(tx, data) + o['params'].append(self.normalize(tx)) + o = j.finalize(o) + return o + + + def entry(self, contract_address, idx, sender_address=ZERO_ADDRESS, id_generator=None): + j = JSONRPCRequest(id_generator) + o = j.template() + o['method'] = 'eth_call' + enc = ABIContractEncoder() + enc.method('entry') + enc.typ(ABIContractType.UINT256) + enc.uint256(idx) + data = add_0x(enc.get()) + tx = self.template(sender_address, contract_address) + tx = self.set_code(tx, data) + o['params'].append(self.normalize(tx)) + o = j.finalize(o) + return o + + + def entry_count(self, contract_address, sender_address=ZERO_ADDRESS, id_generator=None): + j = JSONRPCRequest(id_generator) + o = j.template() + o['method'] = 'eth_call' + enc = ABIContractEncoder() + enc.method('entryCount') + data = add_0x(enc.get()) + tx = self.template(sender_address, contract_address) + tx = self.set_code(tx, data) + o['params'].append(self.normalize(tx)) + o = j.finalize(o) + return o + + + @classmethod + def parse_address_of(self, v): + return abi_decode_single(ABIContractType.ADDRESS, v) + + + @classmethod + def parse_entry(self, v): + return abi_decode_single(ABIContractType.ADDRESS, v) + + + @classmethod + def parse_entry_count(self, v): + return abi_decode_single(ABIContractType.UINT256, v) diff --git a/python/okota/token_index/runnable/deploy.py b/python/okota/token_index/runnable/deploy.py new file mode 100644 index 0000000..6bd57bb --- /dev/null +++ b/python/okota/token_index/runnable/deploy.py @@ -0,0 +1,78 @@ +"""Deploys the token symbol index + +.. moduleauthor:: Louis Holbrook +.. pgp:: 0826EDA1702D1E87C6E2875121D2E7BB88C2A746 + +""" + +# standard imports +import sys +import os +import json +import argparse +import logging + +# external imports +import chainlib.eth.cli +from chainlib.chain import ChainSpec +from chainlib.eth.tx import receipt + +# local imports +from eth_address_declarator.token_index.index import TokenUniqueSymbolIndexAddressDeclarator + +logging.basicConfig(level=logging.WARNING) +logg = logging.getLogger() + +arg_flags = chainlib.eth.cli.argflag_std_write +argparser = chainlib.eth.cli.ArgumentParser(arg_flags) +argparser.add_argument('--address-declarator', type=str, required=True, dest='address_declarator', help='address declarator backend address') +args = argparser.parse_args() + +extra_args = { + 'address_declarator': None, + } + +config = chainlib.eth.cli.Config.from_args(args, arg_flags, extra_args=extra_args, default_fee_limit=TokenUniqueSymbolIndexAddressDeclarator.gas()) + +wallet = chainlib.eth.cli.Wallet() +wallet.from_config(config) + +rpc = chainlib.eth.cli.Rpc(wallet=wallet) +conn = rpc.connect_by_config(config) + +chain_spec = ChainSpec.from_chain_str(config.get('CHAIN_SPEC')) + + +def main(): + signer = rpc.get_signer() + signer_address = rpc.get_sender_address() + + gas_oracle = rpc.get_gas_oracle() + nonce_oracle = rpc.get_nonce_oracle() + + address_declarator = config.get('_ADDRESS_DECLARATOR') + if not config.true('_UNSAFE') and not is_checksum_address(address_declarator): + raise ValueError('address declarator {} is not a valid checksum address'.format(address_declarator)) + + c = TokenUniqueSymbolIndexAddressDeclarator(chain_spec, signer=signer, gas_oracle=gas_oracle, nonce_oracle=nonce_oracle) + + (tx_hash_hex, o) = c.constructor(signer_address, config.get('_ADDRESS_DECLARATOR')) + if config.get('_RPC_SEND'): + conn.do(o) + if config.get('_WAIT'): + r = conn.wait(tx_hash_hex) + if r['status'] == 0: + sys.stderr.write('EVM revert while deploying contract. Wish I had more to tell you') + sys.exit(1) + # TODO: pass through translator for keys (evm tester uses underscore instead of camelcase) + address = r['contractAddress'] + + print(address) + else: + print(tx_hash_hex) + else: + print(o) + + +if __name__ == '__main__': + main() diff --git a/python/requirements.txt b/python/requirements.txt new file mode 100644 index 0000000..30ab1de --- /dev/null +++ b/python/requirements.txt @@ -0,0 +1,7 @@ +confini>=0.3.6rc3,<0.5.0 +crypto-dev-signer>=0.4.15rc2,<=0.4.15 +chainlib-eth>=0.0.9a13,<=0.1.0 +eth_erc20>=0.1.2a3,<=0.2.0 +eth-address-index>=0.2.4a1,<=0.3.0 +eth-accounts-index>=0.1.2a3,<=0.2.0 +#eth-token-index>=0.2.4a1,<=0.3.0 diff --git a/python/setup.cfg b/python/setup.cfg new file mode 100644 index 0000000..7646a3b --- /dev/null +++ b/python/setup.cfg @@ -0,0 +1,50 @@ +[metadata] +name = okota +version = 0.0.1a1 +description = Signed metadata declarations for ethereum addresses +author = Louis Holbrook +author_email = dev@holbrook.no +url = https://gitlab.com/cicnet/okota +keywords = + ethereum +classifiers = + Programming Language :: Python :: 3 + Operating System :: OS Independent + Development Status :: 3 - Alpha + Environment :: No Input/Output (Daemon) + Intended Audience :: Developers + License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+) + Topic :: Internet + #Topic :: Blockchain :: EVM +license = GPL3 +licence_files = + LICENSE + +[options] +include_package_data = True +python_requires = >= 3.6 +packages = + okota.token_index + okota.accounts_index + +[options.extras_require] +testing = + eth-tester==0.5.0b2 + py-evm==0.3.0a20 + +[options.package_data] +* = + #data/AddressDeclarator.json + #data/AddressDeclarator.bin + #data/GiftableToken.bin + #data/GifttableToken.json + data/TokenUniqueSymbolIndexAddressDeclarator.bin + data/TokenUniqueSymbolIndexAddressDeclarator.json + data/AccountsIndexAddressDeclarator.bin + data/AccountsIndexAddressDeclarator.json + data/ERC20.json + +[options.entry_points] +console_scripts = + okota-accounts-index-deploy = okota.accounts_index.runnable.deploy:main + okota-token-index-deploy = okota.token_index.runnable.deploy:main diff --git a/python/setup.py b/python/setup.py new file mode 100644 index 0000000..bc71993 --- /dev/null +++ b/python/setup.py @@ -0,0 +1,25 @@ +from setuptools import setup + +requirements = [] +f = open('requirements.txt', 'r') +while True: + l = f.readline() + if l == '': + break + requirements.append(l.rstrip()) +f.close() + +test_requirements = [] +f = open('test_requirements.txt', 'r') +while True: + l = f.readline() + if l == '': + break + test_requirements.append(l.rstrip()) +f.close() + + +setup( + install_requires=requirements, + tests_require=test_requirements, + ) diff --git a/python/test_requirements.txt b/python/test_requirements.txt new file mode 100644 index 0000000..71d6d67 --- /dev/null +++ b/python/test_requirements.txt @@ -0,0 +1,2 @@ +eth-tester==0.5.0b3 +py-evm==0.3.0a20 diff --git a/python/tests/test_accounts_index.py b/python/tests/test_accounts_index.py new file mode 100644 index 0000000..c6f65c4 --- /dev/null +++ b/python/tests/test_accounts_index.py @@ -0,0 +1,72 @@ +# standard imports +import unittest +import logging +import hashlib + +# external imports +from eth_accounts_index import AccountsIndex +from chainlib.eth.nonce import RPCNonceOracle +from giftable_erc20_token import GiftableToken +from chainlib.eth.tx import receipt +from chainlib.eth.contract import ABIContractEncoder +from eth_address_declarator import Declarator +from eth_address_declarator.unittest import TestAddressDeclaratorBase + +# local imports +from okota.accounts_index import AccountsIndexAddressDeclarator + +# test imports + +logging.basicConfig(level=logging.DEBUG) +logg = logging.getLogger() + + +class TestAccountsIndex(TestAddressDeclaratorBase): + + def setUp(self): + super(TestAccountsIndex, self).setUp() + nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) + + c = AccountsIndexAddressDeclarator(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + (tx_hash_hex, o) = c.constructor(self.accounts[0], self.foo_token_address, self.address) + r = self.rpc.do(o) + + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + self.accounts_index_address = r['contract_address'] + logg.debug('accounts index deployed with address {}'.format(self.accounts_index_address)) + + + def test_accounts_index_address_declarator(self): + nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) + c = AccountsIndex(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + (tx_hash, o) = c.add(self.accounts_index_address, self.accounts[0], self.accounts[1]) + r = self.rpc.do(o) + self.assertEqual(tx_hash, r) + + o = receipt(tx_hash) + rcpt = self.rpc.do(o) + + self.helper.mine_block() + o = c.have(self.accounts_index_address, self.accounts[1], sender_address=self.accounts[0]) + r = self.rpc.do(o) + + c = Declarator(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + o = c.declaration(self.address, self.accounts[0], self.accounts[1], sender_address=self.accounts[0]) + r = self.rpc.do(o) + proofs = c.parse_declaration(r) + + enc = ABIContractEncoder() + enc.address(self.foo_token_address) + token_address_padded = enc.get() + logg.debug('proof {} {}'.format(proofs, token_address_padded)) + h = hashlib.sha256() + h.update(bytes.fromhex(token_address_padded)) + r = h.digest() + self.assertEqual(r.hex(), proofs[0]) + + +if __name__ == '__main__': + unittest.main() diff --git a/python/tests/test_tokenindex.py b/python/tests/test_tokenindex.py new file mode 100644 index 0000000..a156ae1 --- /dev/null +++ b/python/tests/test_tokenindex.py @@ -0,0 +1,87 @@ +# standard imports +import os +import unittest +import json +import logging +import hashlib + +# external imports +from chainlib.eth.unittest.ethtester import EthTesterCase +from chainlib.eth.nonce import RPCNonceOracle +from chainlib.eth.tx import receipt +from giftable_erc20_token import GiftableToken +from chainlib.eth.tx import unpack +from hexathon import strip_0x +from chainlib.eth.contract import ABIContractEncoder +from eth_address_declarator import Declarator + +# local imports +from okota.token_index.index import ( + TokenUniqueSymbolIndexAddressDeclarator as TokenIndex, + to_identifier, + ) + +# test imports +from eth_address_declarator.unittest import TestAddressDeclaratorBase + +logging.basicConfig(level=logging.DEBUG) +logg = logging.getLogger() + +testdir = os.path.dirname(__file__) + + +class TestTokenIndex(TestAddressDeclaratorBase): + + def setUp(self): + super(TestTokenIndex, self).setUp() + nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) + c = TokenIndex(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + (tx_hash_hex, o) = c.constructor(self.accounts[0], self.address) + self.rpc.do(o) + + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + self.token_index_address = r['contract_address'] + + + def test_register(self): + nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) + c = TokenIndex(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + + (tx_hash_hex, o) = c.register(self.token_index_address, self.accounts[0], self.foo_token_address) + self.rpc.do(o) + e = unpack(bytes.fromhex(strip_0x(o['params'][0])), self.chain_spec) + + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + o = c.address_of(self.token_index_address, 'FOO', sender_address=self.accounts[0]) + r = self.rpc.do(o) + address = c.parse_address_of(r) + self.assertEqual(address, strip_0x(self.foo_token_address)) + + o = c.entry(self.token_index_address, 0, sender_address=self.accounts[0]) + r = self.rpc.do(o) + address = c.parse_entry(r) + self.assertEqual(address, strip_0x(self.foo_token_address)) + + o = c.entry_count(self.token_index_address, sender_address=self.accounts[0]) + r = self.rpc.do(o) + count = c.parse_entry_count(r) + self.assertEqual(count, 1) + + c = Declarator(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + o = c.declaration(self.address, self.accounts[0], self.foo_token_address, sender_address=self.accounts[0]) + r = self.rpc.do(o) + proofs = c.parse_declaration(r) + + token_symbol_identifier = to_identifier('FOO') + + self.assertEqual(token_symbol_identifier, proofs[0]) + + +if __name__ == '__main__': + unittest.main() diff --git a/solidity/AccountsIndexAddressDeclarator.sol b/solidity/AccountsIndexAddressDeclarator.sol new file mode 100644 index 0000000..8f7bc90 --- /dev/null +++ b/solidity/AccountsIndexAddressDeclarator.sol @@ -0,0 +1,50 @@ +pragma solidity >0.6.11; + +// SPDX-License-Identifier: GPL-3.0-or-later + + +contract AccountsIndexAddressDeclarator { + + address public tokenAddress; + bytes32 tokenAddressHash; + address public addressDeclaratorAddress; + mapping(address => uint256) entryIndex; + uint256 count; + + address public owner; + address newOwner; + + event AddressAdded(address indexed addedAccount, uint256 indexed accountIndex); // AccountsIndex + event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); // EIP173 + + constructor(address _tokenAddress, address _addressDeclaratorAddress) public { + bytes memory _tokenAddressPadded; + owner = msg.sender; + addressDeclaratorAddress = _addressDeclaratorAddress; + tokenAddress = _tokenAddress; + _tokenAddressPadded = abi.encode(tokenAddress); + tokenAddressHash = sha256(_tokenAddressPadded); + count = 1; + } + + function add(address _account) external returns (bool) { + bool ok; + bytes memory r; + uint256 oldEntryIndex; + + (ok, r) = addressDeclaratorAddress.call(abi.encodeWithSignature("addDeclaration(address,bytes32)", _account, tokenAddressHash)); + require(ok); + require(r[31] == 0x01); + + oldEntryIndex = count; + entryIndex[_account] = oldEntryIndex; + count++; + + emit AddressAdded(_account, oldEntryIndex); + return true; + } + + function have(address _account) external view returns (bool) { + return entryIndex[_account] > 0; + } +} diff --git a/solidity/Makefile b/solidity/Makefile new file mode 100644 index 0000000..f7cd940 --- /dev/null +++ b/solidity/Makefile @@ -0,0 +1,20 @@ +SOLC = /usr/bin/solc + +all: token_index accounts_index + +token_index: + $(SOLC) TokenUniqueSymbolIndexAddressDeclarator.sol --abi --evm-version byzantium | awk 'NR>3' > TokenUniqueSymbolIndexAddressDeclarator.json + $(SOLC) TokenUniqueSymbolIndexAddressDeclarator.sol --bin --evm-version byzantium | awk 'NR>3' > TokenUniqueSymbolIndexAddressDeclarator.bin + truncate -s -1 TokenUniqueSymbolIndexAddressDeclarator.bin + + +accounts_index: + $(SOLC) AccountsIndexAddressDeclarator.sol --abi --evm-version byzantium | awk 'NR>3' > AccountsIndexAddressDeclarator.json + $(SOLC) AccountsIndexAddressDeclarator.sol --bin --evm-version byzantium | awk 'NR>3' > AccountsIndexAddressDeclarator.bin + truncate -s -1 AccountsIndexAddressDeclarator.bin + +install: all + cp -v TokenUniqueSymbolIndexAddressDeclarator.{json,bin} ../python/okota/data/ + cp -v AccountsIndexAddressDeclarator.{json,bin} ../python/okota/data/ + +.PHONY: test install diff --git a/solidity/TokenUniqueSymbolIndexAddressDeclarator.sol b/solidity/TokenUniqueSymbolIndexAddressDeclarator.sol new file mode 100644 index 0000000..001dcfb --- /dev/null +++ b/solidity/TokenUniqueSymbolIndexAddressDeclarator.sol @@ -0,0 +1,115 @@ +pragma solidity >0.6.11; + +// SPDX-License-Identifier: GPL-3.0-or-later + +contract TokenUniqueSymbolIndexAddressDeclarator { + + // EIP 173 + address public owner; + address newOwner; + address public addressDeclaratorAddress; + + mapping ( bytes32 => uint256 ) public registry; + address[] tokens; + + event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); // EIP173 + event AddressAdded(address indexed addedAccount, uint256 indexed accountIndex); // AccountsIndex + + constructor(address _addressDeclaratorAddress) public { + owner = msg.sender; + addressDeclaratorAddress = _addressDeclaratorAddress; + tokens.push(address(0)); + } + + // Implements AccountsIndex + function entry(uint256 _idx) public view returns ( address ) { + return tokens[_idx + 1]; + } + + // Implements Registry + function addressOf(bytes32 _key) public view returns ( address ) { + uint256 idx; + + idx = registry[_key]; + return tokens[idx]; + } + + function register(address _token) public returns (bool) { + require(msg.sender == owner); + + bool ok; + bytes memory r; + + bytes memory token_symbol; + bytes32 token_symbol_key; + uint256 idx; + + (ok, r) = _token.call(abi.encodeWithSignature('symbol()')); + require(ok); + + token_symbol = abi.decode(r, (bytes)); + token_symbol_key = sha256(token_symbol); + + (ok, r) = addressDeclaratorAddress.call(abi.encodeWithSignature("addDeclaration(address,bytes32)", _token, token_symbol_key)); + require(ok); + require(r[31] == 0x01); + + idx = registry[token_symbol_key]; + require(idx == 0); + + registry[token_symbol_key] = tokens.length; + tokens.push(_token); + + emit AddressAdded(_token, tokens.length - 1); + return true; + } + + // Implements AccountsIndex + function add(address _token) public returns (bool) { + return register(_token); + } + + + // Implements AccountsIndex + function entryCount() public view returns ( uint256 ) { + return tokens.length - 1; + } + + // Implements EIP173 + function transferOwnership(address _newOwner) public returns (bool) { + require(msg.sender == owner); + newOwner = _newOwner; + } + + // Implements OwnedAccepter + function acceptOwnership() public returns (bool) { + address oldOwner; + + require(msg.sender == newOwner); + oldOwner = owner; + owner = newOwner; + newOwner = address(0); + emit OwnershipTransferred(oldOwner, owner); + } + + + // Implements EIP165 + function supportsInterface(bytes4 _sum) public pure returns (bool) { + if (_sum == 0xcbdb05c7) { // AccountsIndex + return true; + } + if (_sum == 0xbb34534c) { // Registry + return true; + } + if (_sum == 0x01ffc9a7) { // EIP165 + return true; + } + if (_sum == 0x9493f8b2) { // EIP173 + return true; + } + if (_sum == 0x37a47be4) { // OwnedAccepter + return true; + } + return false; + } +}