commit f8466066f1b1ed91a778983af050cfc5992cd001
Author: nolash <dev@holbrook.no>
Date: Mon, 4 Oct 2021 16:24:51 +0200
initial commit, factored out from eth-address-index
Diffstat:
19 files changed, 692 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,4 @@
+*.egg-info
+build
+*.pyc
+__pycache__
diff --git a/python/MANIFEST.in b/python/MANIFEST.in
@@ -0,0 +1 @@
+include **/data/*.json **/data/*.bin *requirements.txt
diff --git a/python/eth_token_index/__init__.py b/python/eth_token_index/__init__.py
@@ -0,0 +1,4 @@
+from .index import (
+ TokenUniqueSymbolIndex,
+ to_identifier,
+ )
diff --git a/python/eth_token_index/data/ERC20.json b/python/eth_token_index/data/ERC20.json
@@ -0,0 +1 @@
+[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_owner","type":"address"},{"indexed":true,"internalType":"address","name":"_spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_from","type":"address"},{"indexed":true,"internalType":"address","name":"_to","type":"address"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_spender","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]
diff --git a/python/eth_token_index/data/GiftableToken.bin b/python/eth_token_index/data/GiftableToken.bin
@@ -0,0 +1 @@
+60806040523480156200001157600080fd5b506040516200120338038062001203833981810160405260608110156200003757600080fd5b81019080805160405193929190846401000000008211156200005857600080fd5b838201915060208201858111156200006f57600080fd5b82518660018202830111640100000000821117156200008d57600080fd5b8083526020830192505050908051906020019080838360005b83811015620000c3578082015181840152602081019050620000a6565b50505050905090810190601f168015620000f15780820380516001836020036101000a031916815260200191505b50604052602001805160405193929190846401000000008211156200011557600080fd5b838201915060208201858111156200012c57600080fd5b82518660018202830111640100000000821117156200014a57600080fd5b8083526020830192505050908051906020019080838360005b838110156200018057808201518184015260208101905062000163565b50505050905090810190601f168015620001ae5780820380516001836020036101000a031916815260200191505b5060405260200180519060200190929190505050336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555082600290805190602001906200021a929190620002af565b50816003908051906020019062000233929190620002af565b5080600460006101000a81548160ff021916908360ff16021790555060018060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555050505062000365565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282620002e7576000855562000333565b82601f106200030257805160ff191683800117855562000333565b8280016001018555821562000333579182015b828111156200033257825182559160200191906001019062000315565b5b50905062000342919062000346565b5090565b5b808211156200036157600081600090555060010162000347565b5090565b610e8e80620003756000396000f3fe608060405234801561001057600080fd5b50600436106100b45760003560e01c806355b6ed5c1161007157806355b6ed5c146102bd57806370a082311461033557806395d89b411461038d578063983b2d5614610410578063a0712d681461046a578063a9059cbb146104ae576100b4565b806306fdde03146100b9578063095ea7b31461013c57806318160ddd146101a057806323b872dd146101be5780633092afd514610242578063313ce5671461029c575b600080fd5b6100c1610512565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101015780820151818401526020810190506100e6565b50505050905090810190601f16801561012e5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6101886004803603604081101561015257600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506105b0565b60405180821515815260200191505060405180910390f35b6101a86106ab565b6040518082815260200191505060405180910390f35b61022a600480360360608110156101d457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506106b1565b60405180821515815260200191505060405180910390f35b6102846004803603602081101561025857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506109a8565b60405180821515815260200191505060405180910390f35b6102a4610a9a565b604051808260ff16815260200191505060405180910390f35b61031f600480360360408110156102d357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610aad565b6040518082815260200191505060405180910390f35b6103776004803603602081101561034b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610ad2565b6040518082815260200191505060405180910390f35b610395610aea565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156103d55780820151818401526020810190506103ba565b50505050905090810190601f1680156104025780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6104526004803603602081101561042657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610b88565b60405180821515815260200191505060405180910390f35b6104966004803603602081101561048057600080fd5b8101908080359060200190929190505050610c43565b60405180821515815260200191505060405180910390f35b6104fa600480360360408110156104c457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610d01565b60405180821515815260200191505060405180910390f35b60028054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105a85780601f1061057d576101008083540402835291602001916105a8565b820191906000526020600020905b81548152906001019060200180831161058b57829003601f168201915b505050505081565b600081600760003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60055481565b600081600760008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101561073c57600080fd5b81600660008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101561078857600080fd5b81600760008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205403600760008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555081600660008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600660008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055503373ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167f5f7542858008eeb041631f30e6109ae94b83a58e9a58261dd2c42c508850f939856040518082815260200191505060405180910390a4600190509392505050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161480610a3057508173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b610a3957600080fd5b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555060019050919050565b600460009054906101000a900460ff1681565b6007602052816000526040600020602052806000526040600020600091509150505481565b60066020528060005260406000206000915090505481565b60038054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610b805780601f10610b5557610100808354040283529160200191610b80565b820191906000526020600020905b815481529060010190602001808311610b6357829003601f168201915b505050505081565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610be357600080fd5b60018060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555060019050919050565b6000600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16610c9b57600080fd5b81600660003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508160056000828254019250508190555060019050919050565b600081600660003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015610d4f57600080fd5b81600660003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600660008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3600190509291505056fea2646970667358221220decedd646f4686c330f11369f5c7b4908d8940d20f00c85711890c5f75172d8764736f6c63430007050033
+\ No newline at end of file
diff --git a/python/eth_token_index/data/GiftableToken.json b/python/eth_token_index/data/GiftableToken.json
@@ -0,0 +1 @@
+[{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"uint8","name":"_decimals","type":"uint8"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_owner","type":"address"},{"indexed":true,"internalType":"address","name":"_spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_from","type":"address"},{"indexed":true,"internalType":"address","name":"_to","type":"address"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_from","type":"address"},{"indexed":true,"internalType":"address","name":"_to","type":"address"},{"indexed":true,"internalType":"address","name":"_spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"}],"name":"TransferFrom","type":"event"},{"inputs":[{"internalType":"address","name":"_minter","type":"address"}],"name":"addMinter","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowances","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_spender","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"mint","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_minter","type":"address"}],"name":"removeMinter","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]
diff --git a/python/eth_token_index/data/TokenUniqueSymbolIndex.bin b/python/eth_token_index/data/TokenUniqueSymbolIndex.bin
@@ -0,0 +1 @@
+608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600360009080600181540180825580915050600190039060005260206000200160009091909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550611021806100c46000396000f3fe608060405234801561001057600080fd5b50600436106100bb576000357c0100000000000000000000000000000000000000000000000000000000900480637ef50298116100835780637ef502981461018c5780638da5cb5b146101bc578063bb34534c146101da578063e2095c071461020a578063f2fde38b1461023a576100bb565b806301ffc9a7146100c05780630a3b0a4f146100f05780630cbb0f83146101205780634420e4861461013e57806379ba50971461016e575b600080fd5b6100da60048036038101906100d59190610b9c565b61026a565b6040516100e79190610ccf565b60405180910390f35b61010a60048036038101906101059190610b15565b610415565b6040516101179190610ccf565b60405180910390f35b610128610427565b6040516101359190610cea565b60405180910390f35b61015860048036038101906101539190610b15565b610440565b6040516101659190610ccf565b60405180910390f35b610176610709565b6040516101839190610ccf565b60405180910390f35b6101a660048036038101906101a19190610b42565b6108ab565b6040516101b39190610cea565b60405180910390f35b6101c46108c3565b6040516101d19190610cb4565b60405180910390f35b6101f460048036038101906101ef9190610b42565b6108e7565b6040516102019190610cb4565b60405180910390f35b610224600480360381019061021f9190610c12565b610947565b6040516102319190610cb4565b60405180910390f35b610254600480360381019061024f9190610b15565b61099b565b6040516102619190610ccf565b60405180910390f35b600063cbdb05c77c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614156102bf5760019050610410565b63bb34534c7c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614156103125760019050610410565b6301ffc9a77c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614156103655760019050610410565b639493f8b27c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614156103b85760019050610410565b6337a47be47c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916141561040b5760019050610410565b600090505b919050565b600061042082610440565b9050919050565b6000600160038054905061043b9190610dc7565b905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461049b57600080fd5b60606000806000808673ffffffffffffffffffffffffffffffffffffffff166040516024016040516020818303038152906040527f95d89b41000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161054a9190610c9d565b6000604051808303816000865af19150503d8060008114610587576040519150601f19603f3d011682016040523d82523d6000602084013e61058c565b606091505b50915091508161059b57600080fd5b808060200190518101906105af9190610bc9565b94506002856040516105c19190610c9d565b602060405180830381855afa1580156105de573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906106019190610b6f565b9350600260008581526020019081526020016000205492506000831461062657600080fd5b60038054905060026000868152602001908152602001600020819055506003879080600181540180825580915050600190039060005260206000200160009091909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060016003805490506106b89190610dc7565b8773ffffffffffffffffffffffffffffffffffffffff167f9cc987676e7d63379f176ea50df0ae8d2d9d1141d1231d4ce15b5965f73c943060405160405180910390a3600195505050505050919050565b600080600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461076657600080fd5b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35090565b60026020528060005260406000206000915090505481565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600080600260008481526020019081526020016000205490506003818154811061091457610913610f0c565b5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16915050919050565b600060036001836109589190610d71565b8154811061096957610968610f0c565b5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146109f657600080fd5b81600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550919050565b6000610a4f610a4a84610d2a565b610d05565b905082815260208101848484011115610a6b57610a6a610f6f565b5b610a76848285610e79565b509392505050565b600081359050610a8d81610f8f565b92915050565b600081359050610aa281610fa6565b92915050565b600081519050610ab781610fa6565b92915050565b600081359050610acc81610fbd565b92915050565b600082601f830112610ae757610ae6610f6a565b5b8151610af7848260208601610a3c565b91505092915050565b600081359050610b0f81610fd4565b92915050565b600060208284031215610b2b57610b2a610f79565b5b6000610b3984828501610a7e565b91505092915050565b600060208284031215610b5857610b57610f79565b5b6000610b6684828501610a93565b91505092915050565b600060208284031215610b8557610b84610f79565b5b6000610b9384828501610aa8565b91505092915050565b600060208284031215610bb257610bb1610f79565b5b6000610bc084828501610abd565b91505092915050565b600060208284031215610bdf57610bde610f79565b5b600082015167ffffffffffffffff811115610bfd57610bfc610f74565b5b610c0984828501610ad2565b91505092915050565b600060208284031215610c2857610c27610f79565b5b6000610c3684828501610b00565b91505092915050565b610c4881610dfb565b82525050565b610c5781610e0d565b82525050565b6000610c6882610d5b565b610c728185610d66565b9350610c82818560208601610e79565b80840191505092915050565b610c9781610e6f565b82525050565b6000610ca98284610c5d565b915081905092915050565b6000602082019050610cc96000830184610c3f565b92915050565b6000602082019050610ce46000830184610c4e565b92915050565b6000602082019050610cff6000830184610c8e565b92915050565b6000610d0f610d20565b9050610d1b8282610eac565b919050565b6000604051905090565b600067ffffffffffffffff821115610d4557610d44610f3b565b5b610d4e82610f7e565b9050602081019050919050565b600081519050919050565b600081905092915050565b6000610d7c82610e6f565b9150610d8783610e6f565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03821115610dbc57610dbb610edd565b5b828201905092915050565b6000610dd282610e6f565b9150610ddd83610e6f565b925082821015610df057610def610edd565b5b828203905092915050565b6000610e0682610e4f565b9050919050565b60008115159050919050565b6000819050919050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b60005b83811015610e97578082015181840152602081019050610e7c565b83811115610ea6576000848401525b50505050565b610eb582610f7e565b810181811067ffffffffffffffff82111715610ed457610ed3610f3b565b5b80604052505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b610f9881610dfb565b8114610fa357600080fd5b50565b610faf81610e19565b8114610fba57600080fd5b50565b610fc681610e23565b8114610fd157600080fd5b50565b610fdd81610e6f565b8114610fe857600080fd5b5056fea26469706673582212201cbc4bf05e3ed0bb6e627801ab4486f9b8d966bf2e342645a13008d6bfddb33b64736f6c63430008070033
+\ No newline at end of file
diff --git a/python/eth_token_index/data/TokenUniqueSymbolIndex.json b/python/eth_token_index/data/TokenUniqueSymbolIndex.json
@@ -0,0 +1 @@
+[{"inputs":[],"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":[{"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/eth_token_index/index.py b/python/eth_token_index/index.py
@@ -0,0 +1,147 @@
+# Author: Louis Holbrook <dev@holbrook.no> 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):
+
+ __abi = None
+ __bytecode = None
+
+
+ @staticmethod
+ def abi():
+ if TokenUniqueSymbolIndex.__abi == None:
+ f = open(os.path.join(datadir, 'TokenUniqueSymbolIndex.json'), 'r')
+ TokenUniqueSymbolIndex.__abi = json.load(f)
+ f.close()
+ return TokenUniqueSymbolIndex.__abi
+
+
+ @staticmethod
+ def bytecode():
+ if TokenUniqueSymbolIndex.__bytecode == None:
+ f = open(os.path.join(datadir, 'TokenUniqueSymbolIndex.bin'))
+ TokenUniqueSymbolIndex.__bytecode = f.read()
+ f.close()
+ return TokenUniqueSymbolIndex.__bytecode
+
+
+ @staticmethod
+ def gas(code=None):
+ return 1200000
+
+
+ def constructor(self, sender_address):
+ code = TokenUniqueSymbolIndex.bytecode()
+ tx = self.template(sender_address, None, use_nonce=True)
+ tx = self.set_code(tx, code)
+ return self.build(tx)
+
+
+ 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/eth_token_index/runnable/add.py b/python/eth_token_index/runnable/add.py
@@ -0,0 +1,89 @@
+"""Adds a new token to the token symbol index
+
+.. moduleauthor:: Louis Holbrook <dev@holbrook.no>
+.. pgp:: 0826EDA1702D1E87C6E2875121D2E7BB88C2A746
+
+"""
+
+# standard imports
+import sys
+import os
+import json
+import argparse
+import logging
+import hashlib
+
+# external imports
+import chainlib.eth.cli
+from chainlib.chain import ChainSpec
+from chainlib.eth.tx import receipt
+from eth_erc20 import ERC20
+from chainlib.eth.address import to_checksum_address
+from hexathon import add_0x
+
+# local imports
+from eth_token_index import TokenUniqueSymbolIndex
+
+logging.basicConfig(level=logging.WARNING)
+logg = logging.getLogger()
+
+arg_flags = chainlib.eth.cli.argflag_std_write | chainlib.eth.cli.Flag.EXEC
+argparser = chainlib.eth.cli.ArgumentParser(arg_flags)
+argparser.add_argument('token_address', type=str, help='Token address to add to index')
+args = argparser.parse_args()
+
+extra_args = {
+ 'token_address': None,
+ }
+config = chainlib.eth.cli.Config.from_args(args, arg_flags, extra_args=extra_args, default_fee_limit=TokenUniqueSymbolIndex.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()
+
+ c = TokenUniqueSymbolIndex(chain_spec, signer=signer, gas_oracle=gas_oracle, nonce_oracle=nonce_oracle)
+
+ token_address = to_checksum_address(config.get('_TOKEN_ADDRESS'))
+ if not config.true('_UNSAFE') and token_address != add_0x(config.get('_TOKEN_ADDRESS')):
+ raise ValueError('invalid checksum address for token_address')
+
+ contract_address = to_checksum_address(config.get('_EXEC_ADDRESS'))
+ if not config.true('_UNSAFE') and contract_address != add_0x(config.get('_EXEC_ADDRESS')):
+ raise ValueError('invalid checksum address for contract')
+
+ (tx_hash_hex, o) = c.register(contract_address, signer_address, token_address)
+
+ 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)
+
+ c = ERC20(chain_spec)
+ o = c.symbol(token_address)
+ r = conn.do(o)
+ token_symbol = ERC20.parse_symbol(r)
+
+ logg.info('added token {} at {} to token index {}'.format(token_symbol, token_address, contract_address))
+
+ print(tx_hash_hex)
+ else:
+ print(o)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/python/eth_token_index/runnable/deploy.py b/python/eth_token_index/runnable/deploy.py
@@ -0,0 +1,69 @@
+"""Deploys the token symbol index
+
+.. moduleauthor:: Louis Holbrook <dev@holbrook.no>
+.. 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_token_index import TokenUniqueSymbolIndex
+
+logging.basicConfig(level=logging.WARNING)
+logg = logging.getLogger()
+
+arg_flags = chainlib.eth.cli.argflag_std_write
+argparser = chainlib.eth.cli.ArgumentParser(arg_flags)
+args = argparser.parse_args()
+
+config = chainlib.eth.cli.Config.from_args(args, arg_flags, default_fee_limit=TokenUniqueSymbolIndex.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()
+
+ c = TokenUniqueSymbolIndex(chain_spec, signer=signer, gas_oracle=gas_oracle, nonce_oracle=nonce_oracle)
+
+ (tx_hash_hex, o) = c.constructor(signer_address)
+ 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/eth_token_index/runnable/list.py b/python/eth_token_index/runnable/list.py
@@ -0,0 +1,100 @@
+"""Adds a new token to the token symbol index
+
+.. moduleauthor:: Louis Holbrook <dev@holbrook.no>
+.. pgp:: 0826EDA1702D1E87C6E2875121D2E7BB88C2A746
+
+"""
+
+# standard imports
+import sys
+import os
+import json
+import argparse
+import logging
+import hashlib
+
+# external imports
+import chainlib.eth.cli
+from chainlib.chain import ChainSpec
+from eth_erc20 import ERC20
+from chainlib.eth.address import to_checksum_address
+from hexathon import add_0x
+
+# local imports
+from eth_token_index import TokenUniqueSymbolIndex
+
+logging.basicConfig(level=logging.WARNING)
+logg = logging.getLogger()
+default_format = 'terminal'
+
+script_dir = os.path.dirname(__file__)
+data_dir = os.path.join(script_dir, '..', 'data')
+
+arg_flags = chainlib.eth.cli.argflag_std_write | chainlib.eth.cli.Flag.EXEC
+argparser = chainlib.eth.cli.ArgumentParser(arg_flags)
+argparser.add_argument('token_symbol', type=str, nargs='?', help='Token symbol to return address for')
+args = argparser.parse_args()
+
+extra_args = {
+ 'token_symbol': None,
+ }
+config = chainlib.eth.cli.Config.from_args(args, arg_flags, extra_args=extra_args, default_fee_limit=TokenUniqueSymbolIndex.gas())
+
+#wallet = chainlib.eth.cli.Wallet()
+#wallet.from_config(config)
+
+rpc = chainlib.eth.cli.Rpc()
+conn = rpc.connect_by_config(config)
+
+chain_spec = ChainSpec.from_chain_str(config.get('CHAIN_SPEC'))
+
+
+def out_element(e, w=sys.stdout):
+ if config.get('_RAW'):
+ w.write(e[1] + '\n')
+ else:
+ w.write(e[1] + '\t' + e[0] + '\n')
+
+
+def element(ifc, conn, contract_address, token_symbol, w=sys.stdout):
+ o = ifc.address_of(contract_address, token_symbol)
+ r = conn.do(o)
+ a = ifc.parse_address_of(r)
+ out_element((token_symbol, a), w)
+
+
+def ls(ifc, conn, contract_address, token_ifc, w=sys.stdout):
+ o = ifc.entry_count(contract_address)
+ r = conn.do(o)
+ count = ifc.parse_entry_count(r)
+ logg.debug('count {}'.format(count))
+
+ for i in range(count):
+ o = ifc.entry(contract_address, i)
+ r = conn.do(o)
+ token_address = ifc.parse_entry(r)
+
+ o = token_ifc.symbol(token_address)
+ r = conn.do(o)
+ token_symbol = token_ifc.parse_symbol(r)
+
+ element(ifc, conn, contract_address, token_symbol, w)
+
+
+def main():
+ token_ifc = ERC20(chain_spec)
+ ifc = TokenUniqueSymbolIndex(chain_spec)
+
+ contract_address = to_checksum_address(config.get('_EXEC_ADDRESS'))
+ if not config.true('_UNSAFE') and contract_address != add_0x(config.get('_EXEC_ADDRESS')):
+ raise ValueError('invalid checksum address for contract')
+
+ token_symbol = config.get('_TOKEN_SYMBOL')
+ if token_symbol != None:
+ element(ifc, conn, contract_address, token_symbol, sys.stdout)
+ else:
+ ls(ifc, conn, contract_address, token_ifc, sys.stdout)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/python/requirements.txt b/python/requirements.txt
@@ -0,0 +1,5 @@
+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
diff --git a/python/setup.cfg b/python/setup.cfg
@@ -0,0 +1,45 @@
+[metadata]
+name = eth-token-index
+version = 0.2.4a1
+description = Token symbol to address unique index
+author = Louis Holbrook
+author_email = dev@holbrook.no
+url = https://gitlab.com/cicnet/eth-address-index
+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 =
+ eth_token_index
+ eth_token_index.runnable
+
+[options.extras_require]
+testing =
+ eth-tester==0.5.0b2
+ py-evm==0.3.0a20
+
+[options.package_data]
+* =
+ data/TokenUniqueSymbolIndex.bin
+ data/TokenUniqueSymbolIndex.json
+ data/ERC20.json
+
+[options.entry_points]
+console_scripts =
+ eth-token-index-deploy = eth_token_index.runnable.deploy:main
+ eth-token-index-add = eth_token_index.runnable.add:main
+ eth-token-index-list = eth_token_index.runnable.list:main
diff --git a/python/setup.py 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
@@ -0,0 +1,2 @@
+eth-tester==0.5.0b3
+py-evm==0.3.0a20
diff --git a/python/tests/test_tokenindex.py b/python/tests/test_tokenindex.py
@@ -0,0 +1,78 @@
+# 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
+
+# local imports
+from eth_token_index.index import (
+ TokenUniqueSymbolIndex,
+ to_identifier,
+ )
+
+# test imports
+from eth_address_declarator.declarator import Declarator
+from eth_address_declarator.unittest import TestAddressDeclaratorBase
+
+logging.basicConfig(level=logging.DEBUG)
+logg = logging.getLogger()
+
+testdir = os.path.dirname(__file__)
+
+
+class TestTokenUniqueSymbolIndex(TestAddressDeclaratorBase):
+
+ def setUp(self):
+ super(TestTokenUniqueSymbolIndex, self).setUp()
+ nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
+ c = TokenUniqueSymbolIndex(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
+ (tx_hash_hex, o) = c.constructor(self.accounts[0])
+ 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 = TokenUniqueSymbolIndex(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)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/solidity/Makefile b/solidity/Makefile
@@ -0,0 +1,11 @@
+SOLC = /usr/bin/solc
+
+all:
+ $(SOLC) TokenUniqueSymbolIndex.sol --abi --evm-version byzantium | awk 'NR>3' > TokenUniqueSymbolIndex.json
+ $(SOLC) TokenUniqueSymbolIndex.sol --bin --evm-version byzantium | awk 'NR>3' > TokenUniqueSymbolIndex.bin
+ truncate -s -1 TokenUniqueSymbolIndex.bin
+
+install: all
+ cp -v TokenUniqueSymbolIndex.{json,bin} ../python/eth_token_index/data/
+
+.PHONY: install
diff --git a/solidity/TokenUniqueSymbolIndex.sol b/solidity/TokenUniqueSymbolIndex.sol
@@ -0,0 +1,105 @@
+pragma solidity >0.6.11;
+
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+contract TokenUniqueSymbolIndex {
+
+ // EIP 173
+ address public owner;
+ address newOwner;
+
+ 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() public {
+ owner = msg.sender;
+ 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);
+
+ bytes memory token_symbol;
+ bytes32 token_symbol_key;
+ uint256 idx;
+
+ (bool _ok, bytes memory _r) = _token.call(abi.encodeWithSignature('symbol()'));
+ require(_ok);
+
+ token_symbol = abi.decode(_r, (bytes));
+ token_symbol_key = sha256(token_symbol);
+
+ 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;
+ }
+}