diff --git a/contracts/test_contracts/ERC20.vy b/contracts/test_contracts/ERC20.vy index 5c51d5b..42d95bc 100644 --- a/contracts/test_contracts/ERC20.vy +++ b/contracts/test_contracts/ERC20.vy @@ -1,81 +1,160 @@ # THIS CONTRACT IS FOR TESTING PURPOSES AND IS NOT PART OF THE PROJECT -# Modified from: https://github.com/ethereum/vyper/blob/master/examples/tokens/ERC20_solidity_compatible/ERC20.v.py +# @dev Implementation of ERC-20 token standard. +# @author Takayuki Jimba (@yudetamago) +# https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md -Transfer: event({_from: indexed(address), _to: indexed(address), _value: uint256(wei)}) -Approval: event({_owner: indexed(address), _spender: indexed(address), _value: uint256(wei)}) +from vyper.interfaces import ERC20 -name: public(bytes32) -symbol: public(bytes32) +implements: ERC20 + +Transfer: event({_from: indexed(address), _to: indexed(address), _value: uint256}) +Approval: event({_owner: indexed(address), _spender: indexed(address), _value: uint256}) + +name: public(string[64]) +symbol: public(string[32]) decimals: public(uint256) -balances: uint256(wei)[address] -allowances: (uint256(wei)[address])[address] -total_supply: uint256(wei) + +# NOTE: By declaring `balanceOf` as public, vyper automatically generates a 'balanceOf()' getter +# method to allow access to account balances. +# The _KeyType will become a required parameter for the getter and it will return _ValueType. +# See: https://vyper.readthedocs.io/en/v0.1.0-beta.8/types.html?highlight=getter#mappings +balanceOf: public(map(address, uint256)) +allowances: map(address, map(address, uint256)) +total_supply: uint256 +minter: address + @public -def __init__(_name: bytes32, _symbol: bytes32, _decimals: uint256, _supply: uint256(wei)): - _sender: address = msg.sender +def __init__(_name: string[64], _symbol: string[32], _decimals: uint256, _supply: uint256): + init_supply: uint256 = _supply * 10 ** _decimals self.name = _name self.symbol = _symbol self.decimals = _decimals - self.balances[_sender] = _supply - self.total_supply = _supply - log.Transfer(ZERO_ADDRESS, _sender, _supply) - -@public -@payable -def deposit(): - _value: uint256(wei) = msg.value - _sender: address = msg.sender - self.balances[_sender] = self.balances[_sender] + _value - self.total_supply = self.total_supply + _value - log.Transfer(ZERO_ADDRESS, _sender, _value) + self.balanceOf[msg.sender] = init_supply + self.total_supply = init_supply + self.minter = msg.sender + log.Transfer(ZERO_ADDRESS, msg.sender, init_supply) -@public -def withdraw(_value : uint256(wei)) -> bool: - _sender: address = msg.sender - self.balances[_sender] = self.balances[_sender] - _value - self.total_supply = self.total_supply - _value - send(_sender, _value) - log.Transfer(_sender, ZERO_ADDRESS, _value) - return True @public @constant -def totalSupply() -> uint256(wei): +def totalSupply() -> uint256: + """ + @dev Total number of tokens in existence. + """ return self.total_supply + @public @constant -def balanceOf(_owner : address) -> uint256(wei): - return self.balances[_owner] +def allowance(_owner : address, _spender : address) -> uint256: + """ + @dev Function to check the amount of tokens that an owner allowed to a spender. + @param _owner The address which owns the funds. + @param _spender The address which will spend the funds. + @return An uint256 specifying the amount of tokens still available for the spender. + """ + return self.allowances[_owner][_spender] + @public -def transfer(_to : address, _value : uint256(wei)) -> bool: - _sender: address = msg.sender - self.balances[_sender] = self.balances[_sender] - _value - self.balances[_to] = self.balances[_to] + _value - log.Transfer(_sender, _to, _value) +def transfer(_to : address, _value : uint256) -> bool: + """ + @dev Transfer token for a specified address + @param _to The address to transfer to. + @param _value The amount to be transferred. + """ + # NOTE: vyper does not allow underflows + # so the following subtraction would revert on insufficient balance + self.balanceOf[msg.sender] -= _value + self.balanceOf[_to] += _value + log.Transfer(msg.sender, _to, _value) return True + @public -def transferFrom(_from : address, _to : address, _value : uint256(wei)) -> bool: - _sender: address = msg.sender - allowance: uint256(wei) = self.allowances[_from][_sender] - self.balances[_from] = self.balances[_from] - _value - self.balances[_to] = self.balances[_to] + _value - self.allowances[_from][_sender] = allowance - _value +def transferFrom(_from : address, _to : address, _value : uint256) -> bool: + """ + @dev Transfer tokens from one address to another. + Note that while this function emits a Transfer event, this is not required as per the specification, + and other compliant implementations may not emit the event. + @param _from address The address which you want to send tokens from + @param _to address The address which you want to transfer to + @param _value uint256 the amount of tokens to be transferred + """ + # NOTE: vyper does not allow underflows + # so the following subtraction would revert on insufficient balance + self.balanceOf[_from] -= _value + self.balanceOf[_to] += _value + # NOTE: vyper does not allow underflows + # so the following subtraction would revert on insufficient allowance + self.allowances[_from][msg.sender] -= _value log.Transfer(_from, _to, _value) return True + @public -def approve(_spender : address, _value : uint256(wei)) -> bool: - _sender: address = msg.sender - self.allowances[_sender][_spender] = _value - log.Approval(_sender, _spender, _value) +def approve(_spender : address, _value : uint256) -> bool: + """ + @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. + Beware that changing an allowance with this method brings the risk that someone may use both the old + and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this + race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: + https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + @param _spender The address which will spend the funds. + @param _value The amount of tokens to be spent. + """ + self.allowances[msg.sender][_spender] = _value + log.Approval(msg.sender, _spender, _value) return True + @public -@constant -def allowance(_owner : address, _spender : address) -> uint256(wei): - return self.allowances[_owner][_spender] +def mint(_to: address, _value: uint256): + """ + @dev Mint an amount of the token and assigns it to an account. + This encapsulates the modification of balances such that the + proper events are emitted. + @param _to The account that will receive the created tokens. + @param _value The amount that will be created. + """ + assert msg.sender == self.minter + assert _to != ZERO_ADDRESS + self.total_supply += _value + self.balanceOf[_to] += _value + log.Transfer(ZERO_ADDRESS, _to, _value) + + +@private +def _burn(_to: address, _value: uint256): + """ + @dev Internal function that burns an amount of the token of a given + account. + @param _to The account whose tokens will be burned. + @param _value The amount that will be burned. + """ + assert _to != ZERO_ADDRESS + self.total_supply -= _value + self.balanceOf[_to] -= _value + log.Transfer(_to, ZERO_ADDRESS, _value) + + +@public +def burn(_value: uint256): + """ + @dev Burn an amount of the token of msg.sender. + @param _value The amount that will be burned. + """ + self._burn(msg.sender, _value) + + +@public +def burnFrom(_to: address, _value: uint256): + """ + @dev Burn an amount of the token from a given account. + @param _to The account whose tokens will be burned. + @param _value The amount that will be burned. + """ + self.allowances[_to][msg.sender] -= _value + self._burn(_to, _value) \ No newline at end of file diff --git a/contracts/uniswap_exchange.vy b/contracts/uniswap_exchange.vy index 5b5699a..484a11b 100644 --- a/contracts/uniswap_exchange.vy +++ b/contracts/uniswap_exchange.vy @@ -1,6 +1,7 @@ # @title Uniswap Exchange Interface V1 # @notice Source code found at https://github.com/uniswap # @notice Use at your own risk +from vyper.interfaces import ERC20 contract Factory(): def getExchange(token_addr: address) -> address: constant @@ -21,9 +22,9 @@ name: public(bytes32) # Uniswap V1 symbol: public(bytes32) # UNI-V1 decimals: public(uint256) # 18 totalSupply: public(uint256) # total number of UNI in existence -balances: uint256[address] # UNI balance of an address -allowances: (uint256[address])[address] # UNI allowance of one address on another -token: address(ERC20) # address of the ERC20 token traded on this contract +balances: map(address,uint256) # UNI balance of an address +allowances: map(address, map(address, uint256)) # UNI allowance of one address on another +token: ERC20 # address of the ERC20 token traded on this contract factory: Factory # interface for the factory that created this contract # @dev This function acts as a contract constructor which is not currently supported in contracts deployed @@ -31,8 +32,8 @@ factory: Factory # interface for the factory th @public def setup(token_addr: address): assert (self.factory == ZERO_ADDRESS and self.token == ZERO_ADDRESS) and token_addr != ZERO_ADDRESS - self.factory = msg.sender - self.token = token_addr + self.factory = Factory(msg.sender) + self.token = ERC20(token_addr) self.name = 0x556e697377617020563100000000000000000000000000000000000000000000 self.symbol = 0x554e492d56310000000000000000000000000000000000000000000000000000 self.decimals = 18 @@ -57,7 +58,7 @@ def addLiquidity(min_liquidity: uint256, max_tokens: uint256, deadline: timestam assert max_tokens >= token_amount and liquidity_minted >= min_liquidity self.balances[msg.sender] += liquidity_minted self.totalSupply = total_liquidity + liquidity_minted - assert self.token.transferFrom(msg.sender, self, token_amount) + assert_modifiable(self.token.transferFrom(msg.sender, self, token_amount)) log.AddLiquidity(msg.sender, msg.value, token_amount) log.Transfer(ZERO_ADDRESS, msg.sender, liquidity_minted) return liquidity_minted @@ -68,7 +69,7 @@ def addLiquidity(min_liquidity: uint256, max_tokens: uint256, deadline: timestam initial_liquidity: uint256 = as_unitless_number(self.balance) self.totalSupply = initial_liquidity self.balances[msg.sender] = initial_liquidity - assert self.token.transferFrom(msg.sender, self, token_amount) + assert_modifiable(self.token.transferFrom(msg.sender, self, token_amount)) log.AddLiquidity(msg.sender, msg.value, token_amount) log.Transfer(ZERO_ADDRESS, msg.sender, initial_liquidity) return initial_liquidity @@ -91,7 +92,7 @@ def removeLiquidity(amount: uint256, min_eth: uint256(wei), min_tokens: uint256, self.balances[msg.sender] -= amount self.totalSupply = total_liquidity - amount send(msg.sender, eth_amount) - assert self.token.transfer(msg.sender, token_amount) + assert_modifiable(self.token.transfer(msg.sender, token_amount)) log.RemoveLiquidity(msg.sender, eth_amount, token_amount) log.Transfer(msg.sender, ZERO_ADDRESS, amount) return eth_amount, token_amount @@ -129,7 +130,7 @@ def ethToTokenInput(eth_sold: uint256(wei), min_tokens: uint256, deadline: times token_reserve: uint256 = self.token.balanceOf(self) tokens_bought: uint256 = self.getInputPrice(as_unitless_number(eth_sold), as_unitless_number(self.balance - eth_sold), token_reserve) assert tokens_bought >= min_tokens - assert self.token.transfer(recipient, tokens_bought) + assert_modifiable(self.token.transfer(recipient, tokens_bought)) log.TokenPurchase(buyer, eth_sold, tokens_bought) return tokens_bought @@ -172,7 +173,7 @@ def ethToTokenOutput(tokens_bought: uint256, max_eth: uint256(wei), deadline: ti eth_refund: uint256(wei) = max_eth - as_wei_value(eth_sold, 'wei') if eth_refund > 0: send(buyer, eth_refund) - assert self.token.transfer(recipient, tokens_bought) + assert_modifiable(self.token.transfer(recipient, tokens_bought)) log.TokenPurchase(buyer, as_wei_value(eth_sold, 'wei'), tokens_bought) return as_wei_value(eth_sold, 'wei') @@ -206,7 +207,7 @@ def tokenToEthInput(tokens_sold: uint256, min_eth: uint256(wei), deadline: times wei_bought: uint256(wei) = as_wei_value(eth_bought, 'wei') assert wei_bought >= min_eth send(recipient, wei_bought) - assert self.token.transferFrom(buyer, self, tokens_sold) + assert_modifiable(self.token.transferFrom(buyer, self, tokens_sold)) log.EthPurchase(buyer, tokens_sold, wei_bought) return wei_bought @@ -241,7 +242,7 @@ def tokenToEthOutput(eth_bought: uint256(wei), max_tokens: uint256, deadline: ti # tokens sold is always > 0 assert max_tokens >= tokens_sold send(recipient, eth_bought) - assert self.token.transferFrom(buyer, self, tokens_sold) + assert_modifiable(self.token.transferFrom(buyer, self, tokens_sold)) log.EthPurchase(buyer, tokens_sold, eth_bought) return tokens_sold @@ -275,7 +276,7 @@ def tokenToTokenInput(tokens_sold: uint256, min_tokens_bought: uint256, min_eth_ eth_bought: uint256 = self.getInputPrice(tokens_sold, token_reserve, as_unitless_number(self.balance)) wei_bought: uint256(wei) = as_wei_value(eth_bought, 'wei') assert wei_bought >= min_eth_bought - assert self.token.transferFrom(buyer, self, tokens_sold) + assert_modifiable(self.token.transferFrom(buyer, self, tokens_sold)) tokens_bought: uint256 = Exchange(exchange_addr).ethToTokenTransferInput(min_tokens_bought, deadline, recipient, value=wei_bought) log.EthPurchase(buyer, tokens_sold, wei_bought) return tokens_bought @@ -317,7 +318,7 @@ def tokenToTokenOutput(tokens_bought: uint256, max_tokens_sold: uint256, max_eth tokens_sold: uint256 = self.getOutputPrice(as_unitless_number(eth_bought), token_reserve, as_unitless_number(self.balance)) # tokens sold is always > 0 assert max_tokens_sold >= tokens_sold and max_eth_sold >= eth_bought - assert self.token.transferFrom(buyer, self, tokens_sold) + assert_modifiable(self.token.transferFrom(buyer, self, tokens_sold)) eth_sold: uint256(wei) = Exchange(exchange_addr).ethToTokenTransferOutput(tokens_bought, deadline, recipient, value=eth_bought) log.EthPurchase(buyer, tokens_sold, eth_bought) return tokens_sold @@ -493,4 +494,4 @@ def approve(_spender : address, _value : uint256) -> bool: @public @constant def allowance(_owner : address, _spender : address) -> uint256: - return self.allowances[_owner][_spender] + return self.allowances[_owner][_spender] \ No newline at end of file diff --git a/contracts/uniswap_factory.vy b/contracts/uniswap_factory.vy index 66b518b..c16d5f0 100644 --- a/contracts/uniswap_factory.vy +++ b/contracts/uniswap_factory.vy @@ -5,9 +5,9 @@ NewExchange: event({token: indexed(address), exchange: indexed(address)}) exchangeTemplate: public(address) tokenCount: public(uint256) -token_to_exchange: address[address] -exchange_to_token: address[address] -id_to_token: address[uint256] +token_to_exchange: map(address, address) +exchange_to_token: map(address, address) +id_to_token: map(uint256, address) @public def initializeFactory(template: address): @@ -20,7 +20,7 @@ def createExchange(token: address) -> address: assert token != ZERO_ADDRESS assert self.exchangeTemplate != ZERO_ADDRESS assert self.token_to_exchange[token] == ZERO_ADDRESS - exchange: address = create_with_code_of(self.exchangeTemplate) + exchange: address = create_forwarder_to(self.exchangeTemplate) Exchange(exchange).setup(token) self.token_to_exchange[token] = exchange self.exchange_to_token[exchange] = token @@ -43,4 +43,4 @@ def getToken(exchange: address) -> address: @public @constant def getTokenWithId(token_id: uint256) -> address: - return self.id_to_token[token_id] + return self.id_to_token[token_id] \ No newline at end of file diff --git a/tests/conftest.py b/tests/conftest.py index 07911e3..963dc56 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,13 +1,29 @@ -import os +from eth_tester import ( + EthereumTester, + PyEVMBackend, + backends, +) +from eth_tester.exceptions import ( + TransactionFailed, +) +from eth_utils.toolz import ( + compose, +) import pytest from pytest import raises - +import os from web3 import Web3 -from web3.contract import ConciseContract -import eth_tester -from eth_tester import EthereumTester, PyEVMBackend -from eth_tester.exceptions import TransactionFailed -from vyper import compiler +from web3.contract import ( + Contract, + mk_collision_prop, +) +from web3.providers.eth_tester import ( + EthereumTesterProvider, +) + +from vyper import ( + compiler, +) from tests.constants import ( ETH_RESERVE, @@ -20,77 +36,189 @@ # run tests with: python -m pytest -v ''' -setattr(eth_tester.backends.pyevm.main, 'GENESIS_GAS_LIMIT', 10**9) -setattr(eth_tester.backends.pyevm.main, 'GENESIS_DIFFICULTY', 1) +setattr(backends.pyevm.main, 'GENESIS_GAS_LIMIT', 10**9) +setattr(backends.pyevm.main, 'GENESIS_DIFFICULTY', 1) + + +class VyperMethod: + ALLOWED_MODIFIERS = {'call', 'estimateGas', 'transact', 'buildTransaction'} + + def __init__(self, function, normalizers=None): + self._function = function + self._function._return_data_normalizers = normalizers + + def __call__(self, *args, **kwargs): + return self.__prepared_function(*args, **kwargs) + + def __prepared_function(self, *args, **kwargs): + if not kwargs: + modifier, modifier_dict = 'call', {} + fn_abi = [ + x + for x + in self._function.contract_abi + if x.get('name') == self._function.function_identifier + ].pop() + # To make tests faster just supply some high gas value. + modifier_dict.update({'gas': fn_abi.get('gas', 0) + 50000}) + elif len(kwargs) == 1: + modifier, modifier_dict = kwargs.popitem() + if modifier not in self.ALLOWED_MODIFIERS: + raise TypeError( + f"The only allowed keyword arguments are: {self.ALLOWED_MODIFIERS}") + else: + raise TypeError(f"Use up to one keyword argument, one of: {self.ALLOWED_MODIFIERS}") + return getattr(self._function(*args), modifier)(modifier_dict) + + +class VyperContract: + """ + An alternative Contract Factory which invokes all methods as `call()`, + unless you add a keyword argument. The keyword argument assigns the prep method. + This call + > contract.withdraw(amount, transact={'from': eth.accounts[1], 'gas': 100000, ...}) + is equivalent to this call in the classic contract: + > contract.functions.withdraw(amount).transact({'from': eth.accounts[1], 'gas': 100000, ...}) + """ + + def __init__(self, classic_contract, method_class=VyperMethod): + classic_contract._return_data_normalizers += CONCISE_NORMALIZERS + self._classic_contract = classic_contract + self.address = self._classic_contract.address + protected_fn_names = [fn for fn in dir(self) if not fn.endswith('__')] + for fn_name in self._classic_contract.functions: + # Override namespace collisions + if fn_name in protected_fn_names: + _concise_method = mk_collision_prop(fn_name) + else: + _classic_method = getattr( + self._classic_contract.functions, + fn_name) + _concise_method = method_class( + _classic_method, + self._classic_contract._return_data_normalizers + ) + setattr(self, fn_name, _concise_method) + + @classmethod + def factory(cls, *args, **kwargs): + return compose(cls, Contract.factory(*args, **kwargs)) + + +def _none_addr(datatype, data): + if datatype == 'address' and int(data, base=16) == 0: + return (datatype, None) + else: + return (datatype, data) + + +CONCISE_NORMALIZERS = (_none_addr,) + @pytest.fixture def tester(): - return EthereumTester(backend=PyEVMBackend()) + custom_genesis = PyEVMBackend._generate_genesis_params(overrides={'gas_limit': 4500000}) + backend = PyEVMBackend(genesis_parameters=custom_genesis) + return EthereumTester(backend=backend) + + +def zero_gas_price_strategy(web3, transaction_params=None): + return 0 # zero gas price makes testing simpler. + @pytest.fixture def w3(tester): - w3 = Web3(Web3.EthereumTesterProvider(tester)) - w3.eth.setGasPriceStrategy(lambda web3, params: 0) - w3.eth.defaultAccount = w3.eth.accounts[0] + w3 = Web3(EthereumTesterProvider(tester)) + w3.eth.setGasPriceStrategy(zero_gas_price_strategy) return w3 + +def _get_contract(w3, source_code, *args, **kwargs): + out = compiler.compile_code( + source_code, + ['abi', 'bytecode'], + interface_codes=kwargs.pop('interface_codes', None), + ) + abi = out['abi'] + bytecode = out['bytecode'] + value = kwargs.pop('value_in_eth', 0) * 10 ** 18 # Handle deploying with an eth value. + c = w3.eth.contract(abi=abi, bytecode=bytecode) + deploy_transaction = c.constructor(*args) + tx_info = { + 'from': w3.eth.accounts[0], + 'value': value, + 'gasPrice': 0, + } + tx_info.update(kwargs) + tx_hash = deploy_transaction.transact(tx_info) + address = w3.eth.getTransactionReceipt(tx_hash)['contractAddress'] + contract = w3.eth.contract( + address, + abi=abi, + bytecode=bytecode, + ContractFactoryClass=VyperContract, + ) + return contract + + @pytest.fixture -def pad_bytes32(): - def pad_bytes32(instr): - """ Pad a string \x00 bytes to return correct bytes32 representation. """ - bstr = instr.encode() - return bstr + (32 - len(bstr)) * b'\x00' - return pad_bytes32 +def get_contract(w3): + def get_contract(source_code, *args, **kwargs): + return _get_contract(w3, source_code, *args, **kwargs) + + return get_contract -# @pytest.fixture -def create_contract(w3, path): - wd = os.path.dirname(os.path.realpath(__file__)) - with open(os.path.join(wd, os.pardir, path)) as f: - source = f.read() - bytecode = '0x' + compiler.compile(source).hex() - abi = compiler.mk_full_signature(source) - return w3.eth.contract(abi=abi, bytecode=bytecode) - -@pytest.fixture -def exchange_template(w3): - deploy = create_contract(w3, 'contracts/uniswap_exchange.vy') - tx_hash = deploy.constructor().transact() - tx_receipt = w3.eth.getTransactionReceipt(tx_hash) - return ConciseContract(w3.eth.contract( - address=tx_receipt.contractAddress, - abi=deploy.abi - )) @pytest.fixture -def HAY_token(w3): - deploy = create_contract(w3, 'contracts/test_contracts/ERC20.vy') - tx_hash = deploy.constructor(b'HAY Token', b'HAY', 18, 100000*10**18).transact() - tx_receipt = w3.eth.getTransactionReceipt(tx_hash) - return ConciseContract(w3.eth.contract( - address=tx_receipt.contractAddress, - abi=deploy.abi - )) +def get_logs(w3): + def get_logs(tx_hash, c, event_name): + tx_receipt = w3.eth.getTransactionReceipt(tx_hash) + logs = c._classic_contract.events[event_name]().processReceipt(tx_receipt) + return logs + + return get_logs + @pytest.fixture -def DEN_token(w3): - deploy = create_contract(w3, 'contracts/test_contracts/ERC20.vy') - tx_hash = deploy.constructor(b'DEN Token', b'DEN', 18, 100000*10**18).transact() - tx_receipt = w3.eth.getTransactionReceipt(tx_hash) - return ConciseContract(w3.eth.contract( - address=tx_receipt.contractAddress, - abi=deploy.abi - )) +def assert_tx_failed(tester): + def assert_tx_failed(function_to_test, exception=TransactionFailed, exc_text=None): + snapshot_id = tester.take_snapshot() + with pytest.raises(exception) as excinfo: + function_to_test() + tester.revert_to_snapshot(snapshot_id) + if exc_text: + assert exc_text in str(excinfo.value) + + return assert_tx_failed @pytest.fixture -def factory(w3, exchange_template): - deploy = create_contract(w3, 'contracts/uniswap_factory.vy') - tx_hash = deploy.constructor().transact() - tx_receipt = w3.eth.getTransactionReceipt(tx_hash) - contract = ConciseContract(w3.eth.contract( - address=tx_receipt.contractAddress, - abi=deploy.abi - )) - contract.initializeFactory(exchange_template.address, transact={}) +def exchange_template(w3, get_contract): + with open('contracts/uniswap_exchange.vy') as f: + contract_code = f.read() + # Pass constructor variables directly to the contract + contract = get_contract(contract_code) + return contract + +@pytest.fixture +def HAY_token(w3, get_contract): + with open('contracts/test_contracts/ERC20.vy') as f: + contract_code = f.read() + contract = get_contract(contract_code, b'HAY Token', b'HAY', 18, 100000) + return contract + +@pytest.fixture +def DEN_token(w3, get_contract): + with open('contracts/test_contracts/ERC20.vy') as f: + contract_code = f.read() + contract = get_contract(contract_code, b'DEN Token', b'DEN', 18, 100000) + return contract + +@pytest.fixture +def factory(w3, get_contract, exchange_template): + with open('contracts/uniswap_factory.vy') as f: + contract_code = f.read() + contract = get_contract(contract_code) + contract.initializeFactory(exchange_template.address, transact={}) return contract @pytest.fixture @@ -103,7 +231,7 @@ def exchange_abi(): def HAY_exchange(w3, exchange_abi, factory, HAY_token): factory.createExchange(HAY_token.address, transact={}) exchange_address = factory.getExchange(HAY_token.address) - exchange = ConciseContract(w3.eth.contract( + exchange = VyperContract(w3.eth.contract( address=exchange_address, abi=exchange_abi )) @@ -115,7 +243,7 @@ def HAY_exchange(w3, exchange_abi, factory, HAY_token): def DEN_exchange(w3, exchange_abi, factory, DEN_token): factory.createExchange(DEN_token.address, transact={}) exchange_address = factory.getExchange(DEN_token.address) - exchange = ConciseContract(w3.eth.contract( + exchange = VyperContract(w3.eth.contract( address=exchange_address, abi=exchange_abi )) @@ -147,3 +275,11 @@ def assert_fail(func): with raises(Exception): func() return assert_fail + +@pytest.fixture +def pad_bytes32(): + def pad_bytes32(instr): + """ Pad a string \x00 bytes to return correct bytes32 representation. """ + bstr = instr.encode() + return bstr + (32 - len(bstr)) * b'\x00' + return pad_bytes32 \ No newline at end of file diff --git a/tests/exchange/test_ERC20.py b/tests/exchange/test_ERC20.py index 0f6948a..55ea2cd 100644 --- a/tests/exchange/test_ERC20.py +++ b/tests/exchange/test_ERC20.py @@ -1,7 +1,7 @@ -def test_ERC20(w3, HAY_token, pad_bytes32): +def test_ERC20(w3, HAY_token): a0, a1 = w3.eth.accounts[:2] - assert HAY_token.name() == pad_bytes32('HAY Token') - assert HAY_token.symbol() == pad_bytes32('HAY') + assert HAY_token.name() == 'HAY Token' + assert HAY_token.symbol() == 'HAY' assert HAY_token.decimals() == 18 assert HAY_token.totalSupply() == 100000*10**18 assert HAY_token.balanceOf(a0) == 100000*10**18