erc20-faucet

ERC20 token faucet
Log | Files | Refs

commit 6929b16d8c38d90a57000538f836d8d8731de04d
parent e585c158428f69679d86262ab6eafa36a23e2b12
Author: nolash <dev@holbrook.no>
Date:   Sat, 12 Dec 2020 10:49:42 +0100

Add python package

Diffstat:
Mpython/CHANGELOG | 2++
Apython/erc20_single_shot_faucet/faucet.py | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mpython/setup.cfg | 2+-
Mpython/tests/test_app.py | 47+++++++++++++++--------------------------------
Apython/tests/test_contract.py | 135+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msolidity/ERC20SingleShotFaucetStorage.sol | 2+-
6 files changed, 220 insertions(+), 34 deletions(-)

diff --git a/python/CHANGELOG b/python/CHANGELOG @@ -1,3 +1,5 @@ +- 0.0.3 + * Add python interface - 0.0.2 * Rename contracts-dir flag to abi-dir * Use package data dir as abi dir diff --git a/python/erc20_single_shot_faucet/faucet.py b/python/erc20_single_shot_faucet/faucet.py @@ -0,0 +1,66 @@ +# 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 + +logging.basicConfig(level=logging.DEBUG) +logg = logging.getLogger() + +moddir = os.path.dirname(__file__) +datadir = os.path.join(moddir, 'data') + + +class Faucet: + + __abi = None + __bytecode = None + + def __init__(self, w3, address, signer_address=None): + abi = Faucet.abi() + Faucet.bytecode() + self.contract = w3.eth.contract(abi=abi, address=address) + self.w3 = w3 + if signer_address != None: + self.signer_address = signer_address + else: + if type(self.w3.eth.defaultAccount).__name__ == 'Empty': + self.w3.eth.defaultAccount = self.w3.eth.accounts[0] + self.signer_address = self.w3.eth.defaultAccount + + + @staticmethod + def abi(): + if Faucet.__abi == None: + f = open(os.path.join(datadir, 'ERC20SingleShotFaucet.abi.json'), 'r') + Faucet.__abi = json.load(f) + f.close() + return Faucet.__abi + + + @staticmethod + def bytecode(part=None): + if Faucet.__bytecode == None: + f = open(os.path.join(datadir, 'ERC20SingleShotFaucet.bin')) + Faucet.__bytecode = f.read() + f.close() + if part == 'storage': + f = open(os.path.join(datadir, 'ERC20SingleShotFaucetStorage.bin')) + bytecode = f.read() + f.close() + return bytecode + elif part != None: + raise ValueError('unknown bytecode identifier "{}"'.format(part)) + + return Faucet.__bytecode + + + def give_to(self, address): + tx_hash = self.contract.functions.giveTo(address).transact({ + 'from': self.signer_address, + }) + return tx_hash diff --git a/python/setup.cfg b/python/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = erc20-single-shot-faucet -version = 0.0.2 +version = 0.0.3 description = ERC20 token faucet that can be used once per account author = Louis Holbrook author_email = dev@holbrook.no diff --git a/python/tests/test_app.py b/python/tests/test_app.py @@ -7,6 +7,8 @@ import web3 import eth_tester import eth_abi +from erc20_single_shot_faucet import Faucet + logging.basicConfig(level=logging.DEBUG) logg = logging.getLogger() @@ -56,15 +58,15 @@ class Test(unittest.TestCase): self.abi_token = json.load(f) f.close() - t = self.w3.eth.contract(abi=self.abi_token, bytecode=bytecode) - tx_hash = t.constructor('Foo Token', 'FOO', 18).transact({'from': self.w3.eth.accounts[0]}) + self.token = self.w3.eth.contract(abi=self.abi_token, bytecode=bytecode) + tx_hash = self.token.constructor('Foo Token', 'FOO', 18).transact({'from': self.w3.eth.accounts[0]}) r = self.w3.eth.getTransactionReceipt(tx_hash) self.address_token = r.contractAddress - t = self.w3.eth.contract(abi=self.abi_token, address=self.address_token) + self.token = self.w3.eth.contract(abi=self.abi_token, address=self.address_token) - tx_hash = t.functions.mint(1000).transact({'from': self.w3.eth.accounts[0]}) + tx_hash = self.token.functions.mint(1000).transact({'from': self.w3.eth.accounts[0]}) # create faucet @@ -94,42 +96,23 @@ class Test(unittest.TestCase): self.address_faucet = r.contractAddress c = self.w3.eth.contract(abi=self.abi_faucet, address=self.address_faucet) - tx_hash = t.functions.transfer(self.address_faucet, 100).transact({'from': self.w3.eth.accounts[0]}) + tx_hash = self.token.functions.transfer(self.address_faucet, 100).transact({'from': self.w3.eth.accounts[0]}) + + c.functions.setAmount(10).transact({'from': self.w3.eth.accounts[2]}) def tearDown(self): pass - def test_basic(self): - c = self.w3.eth.contract(abi=self.abi_faucet, address=self.address_faucet) - self.assertTrue(c.functions.setAmount(10).transact({'from': self.w3.eth.accounts[0]})) - self.assertTrue(c.functions.setAmount(20).transact({'from': self.w3.eth.accounts[1]})) - with self.assertRaises(Exception): - c.functions.setAmount(30).transact({'from': self.w3.eth.accounts[3]}) - - - def test_giveto(self): - c = self.w3.eth.contract(abi=self.abi_faucet, address=self.address_faucet) - c.functions.setAmount(10).transact({'from': self.w3.eth.accounts[2]}) - c.functions.giveTo(self.w3.eth.accounts[3]).transact({'from': self.w3.eth.accounts[1]}) - - t = self.w3.eth.contract(abi=self.abi_token, address=self.address_token) - self.assertEqual(t.functions.balanceOf(self.w3.eth.accounts[3]).call(), 10); - self.assertEqual(t.functions.balanceOf(self.address_faucet).call(), 90); - - with self.assertRaises(Exception): - c.functions.giveTo(self.w3.eth.accounts[3]).transact({'from': self.w3.eth.accounts[1]}) + def test_sanity(self): + faucet = Faucet(self.w3, self.address_faucet) - c.functions.setAmount(50).transact({'from': self.w3.eth.accounts[1]}) - c.functions.giveTo(self.w3.eth.accounts[4]).transact({'from': self.w3.eth.accounts[1]}) - self.assertEqual(t.functions.balanceOf(self.w3.eth.accounts[4]).call(), 50); - self.assertEqual(t.functions.balanceOf(self.address_faucet).call(), 40); - with self.assertRaises(Exception): - c.functions.giveTo(self.w3.eth.accounts[5]).transact({'from': self.w3.eth.accounts[1]}) - self.assertEqual(t.functions.balanceOf(self.w3.eth.accounts[5]).call(), 0); - self.assertEqual(t.functions.balanceOf(self.address_faucet).call(), 40); + def test_give(self): + faucet = Faucet(self.w3, self.address_faucet) + faucet.give_to(self.w3.eth.accounts[3]) + self.assertEqual(self.token.functions.balanceOf(self.w3.eth.accounts[3]).call(), 10); if __name__ == '__main__': unittest.main() diff --git a/python/tests/test_contract.py b/python/tests/test_contract.py @@ -0,0 +1,135 @@ +import os +import unittest +import json +import logging + +import web3 +import eth_tester +import eth_abi + +logging.basicConfig(level=logging.DEBUG) +logg = logging.getLogger() + +logging.getLogger('web3').setLevel(logging.WARNING) +logging.getLogger('eth.vm').setLevel(logging.WARNING) + +testdir = os.path.dirname(__file__) + + +class Test(unittest.TestCase): + + contract = None + + def setUp(self): + eth_params = eth_tester.backends.pyevm.main.get_default_genesis_params({ + 'gas_limit': 9000000, + }) + + # create store of used accounts + f = open(os.path.join(testdir, '../erc20_single_shot_faucet/data/ERC20SingleShotFaucetStorage.bin'), 'r') + bytecode = f.read() + f.close() + + f = open(os.path.join(testdir, '../erc20_single_shot_faucet/data/ERC20SingleShotFaucetStorage.abi.json'), 'r') + self.abi_storage = json.load(f) + f.close() + + + backend = eth_tester.PyEVMBackend(eth_params) + self.eth_tester = eth_tester.EthereumTester(backend) + provider = web3.Web3.EthereumTesterProvider(self.eth_tester) + self.w3 = web3.Web3(provider) + c = self.w3.eth.contract(abi=self.abi_storage, bytecode=bytecode) + tx_hash = c.constructor().transact({'from': self.w3.eth.accounts[0]}) + + r = self.w3.eth.getTransactionReceipt(tx_hash) + + self.address_storage = r.contractAddress + + + # create token + f = open(os.path.join(testdir, '../erc20_single_shot_faucet/data/GiftableToken.bin'), 'r') + bytecode = f.read() + f.close() + + f = open(os.path.join(testdir, '../erc20_single_shot_faucet/data/GiftableToken.abi.json'), 'r') + self.abi_token = json.load(f) + f.close() + + t = self.w3.eth.contract(abi=self.abi_token, bytecode=bytecode) + tx_hash = t.constructor('Foo Token', 'FOO', 18).transact({'from': self.w3.eth.accounts[0]}) + + r = self.w3.eth.getTransactionReceipt(tx_hash) + + self.address_token = r.contractAddress + t = self.w3.eth.contract(abi=self.abi_token, address=self.address_token) + + tx_hash = t.functions.mint(1000).transact({'from': self.w3.eth.accounts[0]}) + + + # create faucet + f = open(os.path.join(testdir, '../erc20_single_shot_faucet/data/ERC20SingleShotFaucet.bin'), 'r') + bytecode = f.read() + f.close() + + f = open(os.path.join(testdir, '../erc20_single_shot_faucet/data/ERC20SingleShotFaucet.abi.json'), 'r') + self.abi_faucet = json.load(f) + f.close() + +# f = open(os.path.join(testdir, '../erc20_single_shot_faucet/data/ERC20SingleShotFaucet.abi.json'), 'r') +# abi_storage_interface = json.load(f) +# f.close() + + c = self.w3.eth.contract(abi=self.abi_faucet, bytecode=bytecode) + tx_hash = c.constructor([ + self.w3.eth.accounts[1], + self.w3.eth.accounts[2], + ], + self.address_token, + self.address_storage, + ).transact({'from': self.w3.eth.accounts[0]}) + + r = self.w3.eth.getTransactionReceipt(tx_hash) + + self.address_faucet = r.contractAddress + c = self.w3.eth.contract(abi=self.abi_faucet, address=self.address_faucet) + + tx_hash = t.functions.transfer(self.address_faucet, 100).transact({'from': self.w3.eth.accounts[0]}) + + + def tearDown(self): + pass + + + def test_basic(self): + c = self.w3.eth.contract(abi=self.abi_faucet, address=self.address_faucet) + self.assertTrue(c.functions.setAmount(10).transact({'from': self.w3.eth.accounts[0]})) + self.assertTrue(c.functions.setAmount(20).transact({'from': self.w3.eth.accounts[1]})) + with self.assertRaises(Exception): + c.functions.setAmount(30).transact({'from': self.w3.eth.accounts[3]}) + + + def test_giveto(self): + c = self.w3.eth.contract(abi=self.abi_faucet, address=self.address_faucet) + c.functions.setAmount(10).transact({'from': self.w3.eth.accounts[2]}) + c.functions.giveTo(self.w3.eth.accounts[3]).transact({'from': self.w3.eth.accounts[1]}) + + t = self.w3.eth.contract(abi=self.abi_token, address=self.address_token) + self.assertEqual(t.functions.balanceOf(self.w3.eth.accounts[3]).call(), 10); + self.assertEqual(t.functions.balanceOf(self.address_faucet).call(), 90); + + with self.assertRaises(Exception): + c.functions.giveTo(self.w3.eth.accounts[3]).transact({'from': self.w3.eth.accounts[1]}) + + c.functions.setAmount(50).transact({'from': self.w3.eth.accounts[1]}) + c.functions.giveTo(self.w3.eth.accounts[4]).transact({'from': self.w3.eth.accounts[1]}) + self.assertEqual(t.functions.balanceOf(self.w3.eth.accounts[4]).call(), 50); + self.assertEqual(t.functions.balanceOf(self.address_faucet).call(), 40); + + with self.assertRaises(Exception): + c.functions.giveTo(self.w3.eth.accounts[5]).transact({'from': self.w3.eth.accounts[1]}) + self.assertEqual(t.functions.balanceOf(self.w3.eth.accounts[5]).call(), 0); + self.assertEqual(t.functions.balanceOf(self.address_faucet).call(), 40); + + + diff --git a/solidity/ERC20SingleShotFaucetStorage.sol b/solidity/ERC20SingleShotFaucetStorage.sol @@ -18,7 +18,7 @@ contract SingleShotFaucetStorage { return true; } - function completeOwnership() external returns (bool) { + function acceptOwnership() external returns (bool) { require(msg.sender == new_owner); owner = msg.sender; return true;