erc20-faucet

ERC20 token faucet
Log | Files | Refs

commit e3cee0101823dc64e0a810260a350f59bb7390b1
parent 23083fc4abf5be52ce5f7a66e7a8ce77a0f9d68a
Author: nolash <dev@holbrook.no>
Date:   Fri, 30 Apr 2021 19:00:47 +0200

WIP rehabilitating tests

Diffstat:
A.gitignore | 6++++++
Mpython/erc20_single_shot_faucet/__init__.py | 2+-
Mpython/erc20_single_shot_faucet/data/ERC20SingleShotFaucetStorage.bin | 2++
Mpython/erc20_single_shot_faucet/data/ERC20SingleShotFaucetStorage.json | 1+
Mpython/erc20_single_shot_faucet/faucet.py | 75++++++++++++++++++---------------------------------------------------------
Apython/erc20_single_shot_faucet/interface.py | 75+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apython/erc20_single_shot_faucet/runnable/list.py | 83+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mpython/requirements.txt | 4++--
Mpython/test_requirements.txt | 2++
Apython/tests/test_basic.py | 74++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msolidity/ERC20SingleShotFaucetStorage.bin | 2++
Msolidity/ERC20SingleShotFaucetStorage.json | 1+
Msolidity/ERC20SingleShotFaucetStorage.sol | 15+++++++++++----
13 files changed, 278 insertions(+), 64 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,6 @@ +__pycache__ +gmon.out +*.pyc +*.egg-info +build/ +dist/ diff --git a/python/erc20_single_shot_faucet/__init__.py b/python/erc20_single_shot_faucet/__init__.py @@ -3,4 +3,4 @@ # File-version: 1 # Description: Python interface to abi and bin files for faucet contracts -from .faucet import SingleShotFaucet +from .interface import Faucet diff --git a/python/erc20_single_shot_faucet/data/ERC20SingleShotFaucetStorage.bin b/python/erc20_single_shot_faucet/data/ERC20SingleShotFaucetStorage.bin @@ -0,0 +1 @@ +608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600360009080600181540180825580915050600190039060005260206000200160009091909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000600260008073ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550610915806101096000396000f3fe608060405234801561001057600080fd5b50600436106100a5576000357c01000000000000000000000000000000000000000000000000000000009004806379ba50971161007857806379ba50971461016a5780638da5cb5b14610188578063e2095c07146101a6578063f2fde38b146101d6576100a5565b806301ffc9a7146100aa5780630a3b0a4f146100da5780633ef250131461010a578063764b07e71461013a575b600080fd5b6100c460048036038101906100bf9190610756565b6101f2565b6040516100d191906107f0565b60405180910390f35b6100f460048036038101906100ef919061072d565b61034a565b60405161010191906107f0565b60405180910390f35b610124600480360381019061011f919061072d565b61044a565b60405161013191906107f0565b60405180910390f35b610154600480360381019061014f919061072d565b610495565b604051610161919061080b565b60405180910390f35b6101726104ad565b60405161017f91906107f0565b60405180910390f35b6101906105ef565b60405161019d91906107d5565b60405180910390f35b6101c060048036038101906101bb919061077f565b610613565b6040516101cd91906107d5565b60405180910390f35b6101f060048036038101906101eb919061072d565b610652565b005b600063cbdb05c77c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614156102475760019050610345565b6301ffc9a77c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916141561029a5760019050610345565b639493f8b27c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614156102ed5760019050610345565b6337a47be47c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614156103405760019050610345565b600090505b919050565b60008060038054905090506003839080600181540180825580915050600190039060005260206000200160009091909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550808373ffffffffffffffffffffffffffffffffffffffff167f9cc987676e7d63379f176ea50df0ae8d2d9d1141d1231d4ce15b5965f73c943060405160405180910390a36001915050919050565b600080600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054119050919050565b60026020528060005260406000206000915090505481565b60008060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461052d57600080fd5b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3600191505090565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6003818154811061062357600080fd5b906000526020600020016000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146106aa57600080fd5b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6000813590506106fd8161089a565b92915050565b600081359050610712816108b1565b92915050565b600081359050610727816108c8565b92915050565b60006020828403121561073f57600080fd5b600061074d848285016106ee565b91505092915050565b60006020828403121561076857600080fd5b600061077684828501610703565b91505092915050565b60006020828403121561079157600080fd5b600061079f84828501610718565b91505092915050565b6107b181610826565b82525050565b6107c081610838565b82525050565b6107cf81610890565b82525050565b60006020820190506107ea60008301846107a8565b92915050565b600060208201905061080560008301846107b7565b92915050565b600060208201905061082060008301846107c6565b92915050565b600061083182610870565b9050919050565b60008115159050919050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b6108a381610826565b81146108ae57600080fd5b50565b6108ba81610844565b81146108c557600080fd5b50565b6108d181610890565b81146108dc57600080fd5b5056fea2646970667358221220c9946d1d5ce7247846f019cfa3051aae1e32efe8f4d6aaf6be588bf4aadeb9f264736f6c63430008030033 +\ No newline at end of file diff --git a/python/erc20_single_shot_faucet/data/ERC20SingleShotFaucetStorage.json b/python/erc20_single_shot_faucet/data/ERC20SingleShotFaucetStorage.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":"_account","type":"address"}],"name":"add","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"entry","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":[{"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":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"usedAccounts","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}] diff --git a/python/erc20_single_shot_faucet/faucet.py b/python/erc20_single_shot_faucet/faucet.py @@ -19,13 +19,16 @@ from chainlib.eth.contract import ( from chainlib.jsonrpc import jsonrpc_template from hexathon import add_0x -logg = logging.getLogger() +# local imports +from .interface import Faucet + +logg = logging.getLogger().getChild(__name__) moddir = os.path.dirname(__file__) datadir = os.path.join(moddir, 'data') -class SingleShotFaucet(TxFactory): +class SingleShotFaucet(Faucet): __abi = None __bytecode = None @@ -71,59 +74,17 @@ class SingleShotFaucet(TxFactory): return self.build(tx) - def usable_for(self, contract_address, address, sender_address=ZERO_ADDRESS): - o = jsonrpc_template() - o['method'] = 'eth_call' - enc = ABIContractEncoder() - enc.method('cooldown') - enc.typ(ABIContractType.ADDRESS) - enc.address(address) - data = add_0x(enc.get()) - tx = self.template(sender_address, contract_address) - tx = self.set_code(tx, data) - o['params'].append(self.normalize(tx)) - return o - - - @classmethod - def parse_usable_for(self, v): - r = abi_decode_single(ABIContractType.UINT256, v) - return r == 0 - - - def token(self, contract_address, sender_address=ZERO_ADDRESS): - o = jsonrpc_template() - o['method'] = 'eth_call' - enc = ABIContractEncoder() - enc.method('token') - data = add_0x(enc.get()) - tx = self.template(sender_address, contract_address) - tx = self.set_code(tx, data) - o['params'].append(self.normalize(tx)) - return o - - - @classmethod - def parse_token(self, v): - return abi_decode_single(ABIContractType.ADDRESS, v) - - - def amount(self, contract_address, block_height=None, sender_address=ZERO_ADDRESS): - o = jsonrpc_template() - o['method'] = 'eth_call' + # TODO: allow multiple overriders + def constructor(self, sender_address, token, store, accounts_index, overrider): + code = SingleShotFaucet.bytecode() enc = ABIContractEncoder() - enc.method('amount') - data = add_0x(enc.get()) - tx = self.template(sender_address, contract_address) - tx = self.set_code(tx, data) - o['params'].append(self.normalize(tx)) - - if block_height != None: - o['params'].append(block_height) - - return o - - - @classmethod - def parse_amount(self, v): - return abi_decode_single(ABIContractType.UINT256, v) + enc.uint256(0x60) + enc.address(token) + enc.address(store) + enc.address(accounts_index) + enc.uint256(0x01) + enc.address(overrider) + code += enc.get() + tx = self.template(sender_address, None, use_nonce=True) + tx = self.set_code(tx, code) + return self.build(tx) diff --git a/python/erc20_single_shot_faucet/interface.py b/python/erc20_single_shot_faucet/interface.py @@ -0,0 +1,75 @@ +# standard imports +import logging + +# external imports +from chainlib.eth.tx import TxFactory +from chainlib.eth.constant import ZERO_ADDRESS +from chainlib.eth.contract import ( + abi_decode_single, + ABIContractEncoder, + ABIContractType, + ) +from chainlib.jsonrpc import jsonrpc_template +from hexathon import add_0x + +logg = logging.getLogger().getChild(__name__) + + +class Faucet(TxFactory): + + def usable_for(self, contract_address, address, sender_address=ZERO_ADDRESS): + o = jsonrpc_template() + o['method'] = 'eth_call' + enc = ABIContractEncoder() + enc.method('cooldown') + enc.typ(ABIContractType.ADDRESS) + enc.address(address) + data = add_0x(enc.get()) + tx = self.template(sender_address, contract_address) + tx = self.set_code(tx, data) + o['params'].append(self.normalize(tx)) + return o + + + @classmethod + def parse_usable_for(self, v): + r = abi_decode_single(ABIContractType.UINT256, v) + return r == 0 + + + def token(self, contract_address, sender_address=ZERO_ADDRESS): + o = jsonrpc_template() + o['method'] = 'eth_call' + enc = ABIContractEncoder() + enc.method('token') + data = add_0x(enc.get()) + tx = self.template(sender_address, contract_address) + tx = self.set_code(tx, data) + o['params'].append(self.normalize(tx)) + return o + + + @classmethod + def parse_token(self, v): + return abi_decode_single(ABIContractType.ADDRESS, v) + + + def amount(self, contract_address, block_height=None, sender_address=ZERO_ADDRESS): + o = jsonrpc_template() + o['method'] = 'eth_call' + enc = ABIContractEncoder() + enc.method('amount') + data = add_0x(enc.get()) + tx = self.template(sender_address, contract_address) + tx = self.set_code(tx, data) + o['params'].append(self.normalize(tx)) + + if block_height != None: + o['params'].append(block_height) + + return o + + + @classmethod + def parse_amount(self, v): + return abi_decode_single(ABIContractType.UINT256, v) diff --git a/python/erc20_single_shot_faucet/runnable/list.py b/python/erc20_single_shot_faucet/runnable/list.py @@ -0,0 +1,83 @@ +"""Query faucet store + +.. moduleauthor:: Louis Holbrook <dev@holbrook.no> +.. pgp:: 0826EDA1702D1E87C6E2875121D2E7BB88C2A746 + +""" + +# standard imports +import sys +import os +import json +import argparse +import logging + +# third-party imports +from crypto_dev_signer.eth.signer import ReferenceSigner as EIP155Signer +from crypto_dev_signer.keystore.dict import DictKeystore +from crypto_dev_signer.eth.helper import EthTxExecutor +from chainlib.chain import ChainSpec +from chainlib.eth.nonce import RPCNonceOracle +from chainlib.eth.gas import RPCGasOracle +from chainlib.eth.connection import EthHTTPConnection +from chainlib.eth.tx import receipt +from chainlib.eth.constant import ZERO_CONTENT +from chainlib.error import JSONRPCException + +# local imports +from erc20_single_shot_faucet import SingleShotFaucet + +logging.basicConfig(level=logging.WARNING) +logg = logging.getLogger() + +script_dir = os.path.dirname(__file__) +data_dir = os.path.join(script_dir, '..', 'data') + +default_format = 'terminal' + +argparser = argparse.ArgumentParser() +argparser.add_argument('-p', '--provider', dest='p', default='http://localhost:8545', type=str, help='RPC provider url (http only)') +argparser.add_argument('-i', '--chain-spec', dest='i', type=str, default='evm:ethereum:1', help='Chain specification string') +argparser.add_argument('-a', '--contract-address', dest='a', required=True, type=str, help='Faucet store contract address') +argparser.add_argument('-f', '--format', dest='f', type=str, default=default_format, help='Output format [human, brief]') +argparser.add_argument('-v', action='store_true', help='Be verbose') +argparser.add_argument('-vv', action='store_true', help='Be more verbose') +argparser.add_argument('--env-prefix', default=os.environ.get('CONFINI_ENV_PREFIX'), dest='env_prefix', type=str, help='environment prefix for variables to overwrite configuration') +argparser.add_argument('address', type=str, help='Address to check faucet usage for') +args = argparser.parse_args() + +if args.vv: + logg.setLevel(logging.DEBUG) +elif args.v: + logg.setLevel(logging.INFO) + +chain_spec = ChainSpec.from_chain_str(args.i) + +rpc = EthHTTPConnection(args.p) +faucet_store_address = args.a +address = args.address +fmt = args.f + + +def out_element(e, fmt=default_format, w=sys.stdout): + logg.debug('format {}'.format(fmt)) + if fmt == 'brief': + w.write(str(e[1]) + '\n') + else: + w.write('{} {}\n'.format(e[0], e[1])) + + +def element(ifc, address, fmt=default_format, w=sys.stdout): + o = ifc.usable_for(faucet_store_address, address) + r = rpc.do(o) + have = ifc.parse_usable_for(r) + out_element((address, have), fmt, w) + + +def main(): + c = SingleShotFaucet(chain_spec) + element(c, address, fmt=fmt, w=sys.stdout) + + +if __name__ == '__main__': + main() diff --git a/python/requirements.txt b/python/requirements.txt @@ -1,3 +1,3 @@ confini~=0.3.6rc3 -crypto-dev-signer~=0.4.14b2 -chainlib~=0.0.2a13 +crypto-dev-signer~=0.4.14b3 +chainlib~=0.0.2b1 diff --git a/python/test_requirements.txt b/python/test_requirements.txt @@ -1,2 +1,4 @@ eth_tester==0.5.0b3 py-evm==0.3.0a20 +web3==5.12.2 +giftable_erc20_token==0.0.8a10 diff --git a/python/tests/test_basic.py b/python/tests/test_basic.py @@ -0,0 +1,74 @@ +# standard imports +import os +import unittest +import json +import logging + +# external imports +from chainlib.eth.unittest.ethtester import EthTesterCase +from chainlib.connection import RPCConnection +from chainlib.eth.nonce import RPCNonceOracle +from chainlib.eth.address import to_checksum_address +from chainlib.eth.tx import ( + receipt, + transaction, + TxFormat, + ) +from chainlib.eth.contract import ( + abi_decode_single, + ABIContractType, + ) +from chainlib.eth.nonce import RPCNonceOracle +from chainlib.eth.constant import ZERO_ADDRESS +from giftable_erc20_token import GiftableToken + +# local imports +from erc20_single_shot_faucet import Faucet +from erc20_single_shot_faucet.faucet import SingleShotFaucet + +logging.basicConfig(level=logging.DEBUG) +logg = logging.getLogger() + + +class TestFaucet(EthTesterCase): + + def setUp(self): + super(TestFaucet, self).setUp() + self.conn = RPCConnection.connect(self.chain_spec, 'default') + nonce_oracle = RPCNonceOracle(self.accounts[0], self.conn) + c = SingleShotFaucet(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + (tx_hash, o) = c.store_constructor(self.accounts[0]) + r = self.conn.do(o) + logg.debug('store deployed with hash {}'.format(r)) + + o = receipt(r) + r = self.conn.do(o) + self.store_address = to_checksum_address(r['contract_address']) + logg.debug('store contract {}'.format(self.store_address)) + + ct = GiftableToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + (tx_hash_hex, o) = ct.constructor(self.accounts[0], 'Foo Token', 'FOO', 6) + r = self.conn.do(o) + logg.debug('token deployed with hash {}'.format(r)) + + o = receipt(r) + r = self.conn.do(o) + self.token_address = to_checksum_address(r['contract_address']) + logg.debug('token contract {}'.format(self.store_address)) + + (tx_hash, o) = c.constructor(self.accounts[0], self.token_address, self.store_address, ZERO_ADDRESS, self.accounts[1]) + r = self.conn.do(o) + logg.debug('faucet deployed with hash {}'.format(r)) + + o = receipt(r) + r = self.conn.do(o) + self.address = to_checksum_address(r['contract_address']) + logg.debug('faucet contract {}'.format(self.address)) + + + def test_basic(self): + pass + + +if __name__ == '__main__': + unittest.main() diff --git a/solidity/ERC20SingleShotFaucetStorage.bin b/solidity/ERC20SingleShotFaucetStorage.bin @@ -0,0 +1 @@ +608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600360009080600181540180825580915050600190039060005260206000200160009091909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000600260008073ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550610915806101096000396000f3fe608060405234801561001057600080fd5b50600436106100a5576000357c01000000000000000000000000000000000000000000000000000000009004806379ba50971161007857806379ba50971461016a5780638da5cb5b14610188578063e2095c07146101a6578063f2fde38b146101d6576100a5565b806301ffc9a7146100aa5780630a3b0a4f146100da5780633ef250131461010a578063764b07e71461013a575b600080fd5b6100c460048036038101906100bf9190610756565b6101f2565b6040516100d191906107f0565b60405180910390f35b6100f460048036038101906100ef919061072d565b61034a565b60405161010191906107f0565b60405180910390f35b610124600480360381019061011f919061072d565b61044a565b60405161013191906107f0565b60405180910390f35b610154600480360381019061014f919061072d565b610495565b604051610161919061080b565b60405180910390f35b6101726104ad565b60405161017f91906107f0565b60405180910390f35b6101906105ef565b60405161019d91906107d5565b60405180910390f35b6101c060048036038101906101bb919061077f565b610613565b6040516101cd91906107d5565b60405180910390f35b6101f060048036038101906101eb919061072d565b610652565b005b600063cbdb05c77c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614156102475760019050610345565b6301ffc9a77c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916141561029a5760019050610345565b639493f8b27c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614156102ed5760019050610345565b6337a47be47c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614156103405760019050610345565b600090505b919050565b60008060038054905090506003839080600181540180825580915050600190039060005260206000200160009091909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550808373ffffffffffffffffffffffffffffffffffffffff167f9cc987676e7d63379f176ea50df0ae8d2d9d1141d1231d4ce15b5965f73c943060405160405180910390a36001915050919050565b600080600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054119050919050565b60026020528060005260406000206000915090505481565b60008060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461052d57600080fd5b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3600191505090565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6003818154811061062357600080fd5b906000526020600020016000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146106aa57600080fd5b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6000813590506106fd8161089a565b92915050565b600081359050610712816108b1565b92915050565b600081359050610727816108c8565b92915050565b60006020828403121561073f57600080fd5b600061074d848285016106ee565b91505092915050565b60006020828403121561076857600080fd5b600061077684828501610703565b91505092915050565b60006020828403121561079157600080fd5b600061079f84828501610718565b91505092915050565b6107b181610826565b82525050565b6107c081610838565b82525050565b6107cf81610890565b82525050565b60006020820190506107ea60008301846107a8565b92915050565b600060208201905061080560008301846107b7565b92915050565b600060208201905061082060008301846107c6565b92915050565b600061083182610870565b9050919050565b60008115159050919050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b6108a381610826565b81146108ae57600080fd5b50565b6108ba81610844565b81146108c557600080fd5b50565b6108d181610890565b81146108dc57600080fd5b5056fea2646970667358221220c9946d1d5ce7247846f019cfa3051aae1e32efe8f4d6aaf6be588bf4aadeb9f264736f6c63430008030033 +\ No newline at end of file diff --git a/solidity/ERC20SingleShotFaucetStorage.json b/solidity/ERC20SingleShotFaucetStorage.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":"_account","type":"address"}],"name":"add","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"entry","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":[{"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":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"usedAccounts","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}] diff --git a/solidity/ERC20SingleShotFaucetStorage.sol b/solidity/ERC20SingleShotFaucetStorage.sol @@ -17,6 +17,8 @@ contract SingleShotFaucetStorage { constructor() public { owner = msg.sender; + entry.push(address(0)); + usedAccounts[address(0)] = 0; } // Implements EIP 173 @@ -36,18 +38,22 @@ contract SingleShotFaucetStorage { // Implements AccountsIndex function have(address _account) external view returns (bool) { - return usedAccounts[_account]; + return usedAccounts[_account] > 0; } // Implements AccountsIndex function add(address _account) external returns (bool) { - usedAccounts[_account] = true; - emit AccountAdded(_account, ); + uint256 l; + + l = entry.length; + entry.push(_account); + usedAccounts[_account] = l; + emit AddressAdded(_account, l); return true; } // Implements EIP165 - function supportsInterface(bytes4 _sum) { + function supportsInterface(bytes4 _sum) public pure returns (bool) { if (_sum == 0xcbdb05c7) { // AccountsIndex return true; } @@ -60,5 +66,6 @@ contract SingleShotFaucetStorage { if (_sum == 0x37a47be4) { // OwnedAccepter return true; } + return false; } }