Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
459 lines (388 sloc) 23.9 KB
# Requires Python 3.6, Vyper, and pyethereum dependencies
# Manually verified for full branch/decision, statement coverage (on Vyper contract)
# Author: Philip Daian (contributions from Florian Tramer, Lorenz Breidenbach)
import unittest
from ethereum.tools import tester
import ethereum.utils as utils
import ethereum.abi as abi
from utils.pyethereum_test_utils import PyEthereumTestCase, bytes_to_int
MAX_UINT256 = (2 ** 256) - 1 # Max num256 value
MAX_UINT128 = (2 ** 128) - 1 # Max num128 value
# Base path to contracts from current directory
# Allow for running tests either in root or ERC20 dir
try:
from examples.ERC20.test.test_config import PATH_TO_CONTRACTS
except:
PATH_TO_CONTRACTS = "."
class TestERC20(PyEthereumTestCase):
t = None
s = None
c = None
# Topics for logged events
transfer_topic = 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
approval_topic = 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925
strict_log_mode = False
@classmethod
def setUpClass(cls):
super(TestERC20, cls).setUpClass()
# Initialize tester, contract and expose relevant objects
cls.t = tester
cls.s = cls.t.Chain()
cls.s.head_state.gas_limit = 10**80
cls.s.head_state.set_balance(cls.t.a0, 10**80)
cls.s.head_state.set_balance(cls.t.a1, MAX_UINT256 * 3)
cls.s.head_state.set_balance(cls.t.a2, utils.denoms.ether * 1)
cls.initial_state = None
@classmethod
def listenForEvents(cls):
cls.events = []
cls.s.head_state.log_listeners.append(
lambda x: cls.events.append(cls.c.translator.listen(x)))
def setUp(self):
self.s.revert(self.initial_state)
super().setUp()
def test_initial_state(self):
# Check total supply is 0
self.assertEqual(self.c.totalSupply(), 0)
# Check several account balances as 0
self.assertEqual(self.c.balanceOf(self.t.a1), 0)
self.assertEqual(self.c.balanceOf(self.t.a2), 0)
self.assertEqual(self.c.balanceOf(self.t.a3), 0)
# Check several allowances as 0
self.assertEqual(self.c.allowance(self.t.a1, self.t.a1), 0)
self.assertEqual(self.c.allowance(self.t.a1, self.t.a2), 0)
self.assertEqual(self.c.allowance(self.t.a1, self.t.a3), 0)
self.assertEqual(self.c.allowance(self.t.a2, self.t.a3), 0)
def test_deposit_and_withdraw(self):
initial_a1_balance = self.s.head_state.get_balance(self.t.a1)
initial_a2_balance = self.s.head_state.get_balance(self.t.a2)
# Test scenario where a1 deposits 2, withdraws twice (check balance consistency)
self.assertEqual(self.c.balanceOf(self.t.a1), 0)
self.assertIsNone(self.c.deposit(value=2, sender=self.t.k1))
self.assertEqual(self.c.balanceOf(self.t.a1), 2)
# Check that 2 Wei have been debited from a1
self.assertEqual(initial_a1_balance - self.s.head_state.get_balance(self.t.a1), 2)
# ... and added to the contract
self.assertEqual(self.s.head_state.get_balance(self.c.address), 2)
self.assertTrue(self.c.withdraw(2, sender=self.t.k1))
self.assertEqual(self.c.balanceOf(self.t.a1), 0)
# a1 should have all his money back
self.assertEqual(self.s.head_state.get_balance(self.t.a1), initial_a1_balance)
self.assert_tx_failed(lambda: self.c.withdraw(2, sender=self.t.k1))
self.assertEqual(self.c.balanceOf(self.t.a1), 0)
# Test scenario where a2 deposits 0, withdraws (check balance consistency, false withdraw)
self.assertIsNone(self.c.deposit(value=0, sender=self.t.k2))
self.assertEqual(self.c.balanceOf(self.t.a2), 0)
self.assertEqual(self.s.head_state.get_balance(self.t.a2), initial_a2_balance)
self.assert_tx_failed(lambda: self.c.withdraw(2, sender=self.t.k2))
# Check that a1 cannot withdraw after depleting their balance
self.assert_tx_failed(lambda: self.c.withdraw(1, sender=self.t.k1))
def test_totalSupply(self):
# Test total supply initially, after deposit, between two withdraws, and after failed withdraw
self.assertEqual(self.c.totalSupply(), 0)
self.assertIsNone(self.c.deposit(value=2, sender=self.t.k1))
self.assertEqual(self.c.totalSupply(), 2)
self.assertTrue(self.c.withdraw(1, sender=self.t.k1))
self.assertEqual(self.c.totalSupply(), 1)
# Ensure total supply is equal to balance
self.assertEqual(self.c.totalSupply(), self.s.head_state.get_balance(self.c.address))
self.assertTrue(self.c.withdraw(1, sender=self.t.k1))
self.assertEqual(self.c.totalSupply(), 0)
self.assert_tx_failed(lambda: self.c.withdraw(1, sender=self.t.k1))
self.assertEqual(self.c.totalSupply(), 0)
# Test that 0-valued deposit can't affect supply
self.assertIsNone(self.c.deposit(value=0, sender=self.t.k1))
self.assertEqual(self.c.totalSupply(), 0)
def test_transfer(self):
# Test interaction between deposit/withdraw and transfer
self.assert_tx_failed(lambda: self.c.withdraw(1, sender=self.t.k2))
self.assertIsNone(self.c.deposit(value=2, sender=self.t.k1))
self.assertTrue(self.c.withdraw(1, sender=self.t.k1))
self.assertTrue(self.c.transfer(self.t.a2, 1, sender=self.t.k1))
self.assert_tx_failed(lambda: self.c.withdraw(1, sender=self.t.k1))
self.assertTrue(self.c.withdraw(1, sender=self.t.k2))
self.assert_tx_failed(lambda: self.c.withdraw(1, sender=self.t.k2))
# Ensure transfer fails with insufficient balance
self.assert_tx_failed(lambda: self.c.transfer(self.t.a1, 1, sender=self.t.k2))
# Ensure 0-transfer always succeeds
self.assertTrue(self.c.transfer(self.t.a1, 0, sender=self.t.k2))
def test_transferFromAndAllowance(self):
# Test interaction between deposit/withdraw and transferFrom
self.assert_tx_failed(lambda: self.c.withdraw(1, sender=self.t.k2))
self.assertIsNone(self.c.deposit(value=1, sender=self.t.k1))
self.assertIsNone(self.c.deposit(value=1, sender=self.t.k2))
self.assertTrue(self.c.withdraw(1, sender=self.t.k1))
# This should fail; no allowance or balance (0 always succeeds)
self.assert_tx_failed(lambda: self.c.transferFrom(self.t.a1, self.t.a3, 1, sender=self.t.k2))
self.assertTrue(self.c.transferFrom(self.t.a1, self.t.a3, 0, sender=self.t.k2))
# Correct call to approval should update allowance (but not for reverse pair)
self.assertTrue(self.c.approve(self.t.a2, 1, sender=self.t.k1))
self.assertEqual(self.c.allowance(self.t.a1, self.t.a2, sender=self.t.k3), 1)
self.assertEqual(self.c.allowance(self.t.a2, self.t.a1, sender=self.t.k2), 0)
# transferFrom should succeed when allowed, fail with wrong sender
self.assert_tx_failed(lambda: self.c.transferFrom(self.t.a2, self.t.a3, 1, sender=self.t.k3))
self.assertEqual(self.c.balanceOf(self.t.a2), 1)
self.assertTrue(self.c.approve(self.t.a1, 1, sender=self.t.k2))
self.assertTrue(self.c.transferFrom(self.t.a2, self.t.a3, 1, sender=self.t.k1))
# Allowance should be correctly updated after transferFrom
self.assertEqual(self.c.allowance(self.t.a2, self.t.a1, sender=self.t.k2), 0)
# transferFrom with no funds should fail despite approval
self.assertTrue(self.c.approve(self.t.a1, 1, sender=self.t.k2))
self.assertEqual(self.c.allowance(self.t.a2, self.t.a1, sender=self.t.k2), 1)
self.assert_tx_failed(lambda: self.c.transferFrom(self.t.a2, self.t.a3, 1, sender=self.t.k1))
# 0-approve should not change balance or allow transferFrom to change balance
self.assertIsNone(self.c.deposit(value=1, sender=self.t.k2))
self.assertEqual(self.c.allowance(self.t.a2, self.t.a1, sender=self.t.k2), 1)
self.assertTrue(self.c.approve(self.t.a1, 0, sender=self.t.k2))
self.assertEqual(self.c.allowance(self.t.a2, self.t.a1, sender=self.t.k2), 0)
self.assertTrue(self.c.approve(self.t.a1, 0, sender=self.t.k2))
self.assertEqual(self.c.allowance(self.t.a2, self.t.a1, sender=self.t.k2), 0)
self.assert_tx_failed(lambda: self.c.transferFrom(self.t.a2, self.t.a3, 1, sender=self.t.k1))
# Test that if non-zero approval exists, 0-approval is NOT required to proceed
# a non-conformant implementation is described in countermeasures at
# https://docs.google.com/document/d/1YLPtQxZu1UAvO9cZ1O2RPXBbT0mooh4DYKjA_jp-RLM/edit#heading=h.m9fhqynw2xvt
# the final spec insists on NOT using this behavior
self.assertEqual(self.c.allowance(self.t.a2, self.t.a1, sender=self.t.k2), 0)
self.assertTrue(self.c.approve(self.t.a1, 1, sender=self.t.k2))
self.assertEqual(self.c.allowance(self.t.a2, self.t.a1, sender=self.t.k2), 1)
self.assertTrue(self.c.approve(self.t.a1, 2, sender=self.t.k2))
self.assertEqual(self.c.allowance(self.t.a2, self.t.a1, sender=self.t.k2), 2)
# Check that approving 0 then amount also works
self.assertTrue(self.c.approve(self.t.a1, 0, sender=self.t.k2))
self.assertEqual(self.c.allowance(self.t.a2, self.t.a1, sender=self.t.k2), 0)
self.assertTrue(self.c.approve(self.t.a1, 5, sender=self.t.k2))
self.assertEqual(self.c.allowance(self.t.a2, self.t.a1, sender=self.t.k2), 5)
def test_maxInts(self):
initial_a1_balance = self.s.head_state.get_balance(self.t.a1)
# Check boundary conditions - a1 can deposit max amount
# @TODO fix this, it's hacky and will cause divergences
# we'd like to deposit MAX_UINT256: see https://github.com/ethereum/vyper/issues/653
self.assertIsNone(self.c.deposit(value=int(MAX_UINT256/2)-1, sender=self.t.k1))
self.assertIsNone(self.c.deposit(value=int(MAX_UINT256/2)-1, sender=self.t.k1))
self.assertIsNone(self.c.deposit(value=1, sender=self.t.k1))
self.assertEqual(initial_a1_balance - self.s.head_state.get_balance(self.t.a1), MAX_UINT256)
self.assertEqual(self.c.balanceOf(self.t.a1), MAX_UINT256)
self.assert_tx_failed(lambda: self.c.deposit(value=1, sender=self.t.k1))
self.assert_tx_failed(lambda: self.c.deposit(value=MAX_UINT256, sender=self.t.k1))
# Check that totalSupply cannot overflow, even when deposit from other sender
self.assert_tx_failed(lambda: self.c.deposit(value=1, sender=self.t.k2))
# Check that corresponding deposit is allowed after withdraw
self.assertTrue(self.c.withdraw(1, sender=self.t.k1))
self.assertIsNone(self.c.deposit(value=1, sender=self.t.k2))
self.assert_tx_failed(lambda: self.c.deposit(value=1, sender=self.t.k2))
self.assertTrue(self.c.transfer(self.t.a1, 1, sender=self.t.k2))
# Assert that after obtaining max number of tokens, a1 can transfer those but no more
self.assertEqual(self.c.balanceOf(self.t.a1), MAX_UINT256)
self.assertTrue(self.c.transfer(self.t.a2, MAX_UINT256, sender=self.t.k1))
self.assertEqual(self.c.balanceOf(self.t.a2), MAX_UINT256)
self.assertEqual(self.c.balanceOf(self.t.a1), 0)
# [ next line should never work in EVM ]
self.assert_tx_failed(lambda: self.c.transfer(self.t.a1, MAX_UINT256 + 1, sender=self.t.k2), exception=abi.ValueOutOfBounds)
# Check approve/allowance w max possible token values
self.assertEqual(self.c.balanceOf(self.t.a2), MAX_UINT256)
self.assertTrue(self.c.approve(self.t.a1, MAX_UINT256, sender=self.t.k2))
self.assertTrue(self.c.transferFrom(self.t.a2, self.t.a1, MAX_UINT256, sender=self.t.k1))
self.assertEqual(self.c.balanceOf(self.t.a1), MAX_UINT256)
self.assertEqual(self.c.balanceOf(self.t.a2), 0)
# Check that max amount can be withdrawn
# (-1 because a1 withdrew from a2 transferring in)
self.assertEqual(initial_a1_balance - MAX_UINT256, self.s.head_state.get_balance(self.t.a1) - 1)
self.assertTrue(self.c.withdraw(MAX_UINT256, sender=self.t.k1))
self.assertEqual(self.c.balanceOf(self.t.a1), 0)
self.assertEqual(initial_a1_balance, self.s.head_state.get_balance(self.t.a1) - 1)
def test_payability(self):
# Make sure functions are appopriately payable (or not)
# Payable functions - ensure success
self.assertIsNone(self.c.deposit(value=2, sender=self.t.k1))
# Non payable functions - ensure all fail with value, succeed without
self.assert_tx_failed(lambda: self.c.withdraw(0, value=2, sender=self.t.k1))
self.assertTrue(self.c.withdraw(0, value=0, sender=self.t.k1))
self.assert_tx_failed(lambda: self.c.totalSupply(value=2, sender=self.t.k1))
self.assertEqual(self.c.totalSupply(value=0, sender=self.t.k1), 2)
self.assert_tx_failed(lambda: self.c.balanceOf(self.t.a1, value=2, sender=self.t.k1))
self.assertEqual(self.c.balanceOf(self.t.a1, value=0, sender=self.t.k1), 2)
self.assert_tx_failed(lambda: self.c.transfer(self.t.a2, 0, value=2, sender=self.t.k1))
self.assertTrue(self.c.transfer(self.t.a2, 0, value=0, sender=self.t.k1))
self.assert_tx_failed(lambda: self.c.approve(self.t.a2, 1, value=2, sender=self.t.k1))
self.assertTrue(self.c.approve(self.t.a2, 1, value=0, sender=self.t.k1))
self.assert_tx_failed(lambda: self.c.allowance(self.t.a1, self.t.a2, value=2, sender=self.t.k1))
self.assertEqual(self.c.allowance(self.t.a1, self.t.a2, value=0, sender=self.t.k1), 1)
self.assert_tx_failed(lambda: self.c.transferFrom(self.t.a1, self.t.a2, 0, value=2, sender=self.t.k1))
self.assertTrue(self.c.transferFrom(self.t.a1, self.t.a2, 0, value=0, sender=self.t.k1))
def test_raw_logs(self):
self.s.head_state.receipts[-1].logs = []
# Check that deposit appropriately emits Deposit event
self.assertIsNone(self.c.deposit(value=2, sender=self.t.k1))
self.check_logs([self.transfer_topic, 0, bytes_to_int(self.t.a1)], (2).to_bytes(32, byteorder='big'))
self.assertIsNone(self.c.deposit(value=0, sender=self.t.k1))
self.check_logs([self.transfer_topic, 0, bytes_to_int(self.t.a1)], (0).to_bytes(32, byteorder='big'))
# Check that withdraw appropriately emits Withdraw event
self.assertTrue(self.c.withdraw(1, sender=self.t.k1))
self.check_logs([self.transfer_topic, bytes_to_int(self.t.a1), 0], (1).to_bytes(32, byteorder='big'))
self.assertTrue(self.c.withdraw(0, sender=self.t.k1))
self.check_logs([self.transfer_topic, bytes_to_int(self.t.a1), 0], (0).to_bytes(32, byteorder='big'))
# Check that transfer appropriately emits Transfer event
self.assertTrue(self.c.transfer(self.t.a2, 1, sender=self.t.k1))
self.check_logs([self.transfer_topic, bytes_to_int(self.t.a1), bytes_to_int(self.t.a2)], (1).to_bytes(32, byteorder='big'))
self.assertTrue(self.c.transfer(self.t.a2, 0, sender=self.t.k1))
self.check_logs([self.transfer_topic, bytes_to_int(self.t.a1), bytes_to_int(self.t.a2)], (0).to_bytes(32, byteorder='big'))
# Check that approving amount emits events
self.assertTrue(self.c.approve(self.t.a1, 1, sender=self.t.k2))
self.check_logs([self.approval_topic, bytes_to_int(self.t.a2), bytes_to_int(self.t.a1)], (1).to_bytes(32, byteorder='big'))
self.assertTrue(self.c.approve(self.t.a2, 0, sender=self.t.k3))
self.check_logs([self.approval_topic, bytes_to_int(self.t.a3), bytes_to_int(self.t.a2)], (0).to_bytes(32, byteorder='big'))
# Check that transferFrom appropriately emits Transfer event
self.assertTrue(self.c.transferFrom(self.t.a2, self.t.a3, 1, sender=self.t.k1))
self.check_logs([self.transfer_topic, bytes_to_int(self.t.a2), bytes_to_int(self.t.a3)], (1).to_bytes(32, byteorder='big'))
self.assertTrue(self.c.transferFrom(self.t.a2, self.t.a3, 0, sender=self.t.k1))
self.check_logs([self.transfer_topic, bytes_to_int(self.t.a2), bytes_to_int(self.t.a3)], (0).to_bytes(32, byteorder='big'))
# Check that no other ERC-compliant calls emit any events
self.s.head_state.receipts[-1].logs = []
self.assertEqual(self.c.totalSupply(), 1)
self.assertEqual(self.s.head_state.receipts[-1].logs, [])
self.assertEqual(self.c.balanceOf(self.t.a1), 0)
self.assertEqual(self.s.head_state.receipts[-1].logs, [])
self.assertEqual(self.c.allowance(self.t.a1, self.t.a2), 0)
self.assertEqual(self.s.head_state.receipts[-1].logs, [])
# Check that failed (Deposit, Withdraw, Transfer) calls emit no events
self.assert_tx_failed(lambda: self.c.deposit(value=MAX_UINT256, sender=self.t.k1))
self.assertEqual(self.s.head_state.receipts[-1].logs, [])
self.assert_tx_failed(lambda: self.c.withdraw(1, sender=self.t.k1))
self.assertEqual(self.s.head_state.receipts[-1].logs, [])
self.assert_tx_failed(lambda: self.c.transfer(self.t.a2, 1, sender=self.t.k1))
self.assertEqual(self.s.head_state.receipts[-1].logs, [])
self.assert_tx_failed(lambda: self.c.transferFrom(self.t.a2, self.t.a3, 1, sender=self.t.k1))
self.assertEqual(self.s.head_state.receipts[-1].logs, [])
def test_failed_send_in_withdraw(self):
external_code = """
contract ERC20 {
function deposit() payable;
function withdraw(uint256 _value) returns (bool success);
}
contract Dummy {
address private erc20_addr;
uint256 val;
function Dummy(address _erc20_addr) {
erc20_addr = _erc20_addr;
}
function my_deposit() external payable {
val = msg.value;
ERC20(erc20_addr).deposit.value(val)();
}
function my_withdraw() returns (bool success) {
return ERC20(erc20_addr).withdraw(val);
}
function() external payable {
throw;
}
}
"""
# deploy the contract and pass the ERC20 contract's address as argument
ext = self.s.contract(external_code, args=[self.c.address], language='solidity')
# deposit should work and a Deposit event should be logged
self.assertIsNone(ext.my_deposit(value=2))
self.check_logs([self.transfer_topic, 0, bytes_to_int(ext.address)], (2).to_bytes(32, byteorder='big'))
# withdraw should throw
self.assert_tx_failed(lambda: ext.my_withdraw())
# re-deploy the contract with a working default function
external_code2 = external_code.replace("throw", "return")
ext2 = self.s.contract(external_code2, args=[self.c.address], language='solidity')
# deposit should work and yield the correct event
self.assertIsNone(ext2.my_deposit(value=2))
self.check_logs([self.transfer_topic, 0, bytes_to_int(ext2.address)], (2).to_bytes(32, byteorder='big'))
# withdraw should work and yield the correct event
self.assertTrue(ext2.my_withdraw())
self.check_logs([self.transfer_topic, bytes_to_int(ext2.address), 0], (2).to_bytes(32, byteorder='big'))
class TestVyperERC20(TestERC20):
@classmethod
def setUpClass(cls):
super(TestVyperERC20, cls).setUpClass()
from vyper import compiler
cls.t.languages['vyper'] = compiler.Compiler()
with open(PATH_TO_CONTRACTS + '/ERC20.v.py') as fd:
contract_code = fd.read()
cls.c = cls.s.contract(contract_code, language='vyper')
# Bad version of contract where totalSupply / num_issued never gets updated after init
# (required for full decision/branch coverage)
bad_code = contract_code.replace("self.num_issued = num256_add(self.num_issued, _value)", "p:num = 5")
cls.c_bad = cls.s.contract(bad_code, language='vyper')
cls.initial_state = cls.s.snapshot()
cls.strict_log_mode = True
cls.listenForEvents()
def setUp(self):
super().setUp()
def test_bad_transfer(self):
# Ensure transfer fails if it would otherwise overflow balance
# (bad contract is used or overflow checks on total supply would fail)
# @TODO fix this, it's hacky and will cause divergences
# we'd like to deposit MAX_UINT256: see https://github.com/ethereum/vyper/issues/653
self.assertIsNone(self.c_bad.deposit(value=int(MAX_UINT256/2)-1, sender=self.t.k1))
self.assertIsNone(self.c_bad.deposit(value=int(MAX_UINT256/2)-1, sender=self.t.k1))
self.assertIsNone(self.c_bad.deposit(value=1, sender=self.t.k1))
self.assertIsNone(self.c_bad.deposit(value=3, sender=self.t.k2))
self.assert_tx_failed(lambda: self.c_bad.transfer(self.t.a1, 3, sender=self.t.k2))
self.assertTrue(self.c_bad.transfer(self.t.a2, MAX_UINT256 - 3, sender=self.t.k1))
def test_bad_deposit(self):
# Check that, in event when totalSupply is corrupted, it can't be underflowed
self.assertEqual(self.c_bad.balanceOf(self.t.a1), 0)
self.assertIsNone(self.c_bad.deposit(value=2, sender=self.t.k1))
self.assertEqual(self.c_bad.balanceOf(self.t.a1), 2)
self.assert_tx_failed(lambda: self.c_bad.withdraw(2, sender=self.t.k1))
def test_bad_transferFrom(self):
# Ensure transferFrom fails if it would otherwise overflow balance
# @TODO fix this, it's hacky and will cause divergences
# we'd like to deposit MAX_UINT256: see https://github.com/ethereum/vyper/issues/653
self.assertIsNone(self.c_bad.deposit(value=int(MAX_UINT256/2)-1, sender=self.t.k1))
self.assertIsNone(self.c_bad.deposit(value=int(MAX_UINT256/2)-1, sender=self.t.k1))
self.assertIsNone(self.c_bad.deposit(value=1, sender=self.t.k1))
self.assertIsNone(self.c_bad.deposit(value=1, sender=self.t.k2))
self.assertTrue(self.c_bad.approve(self.t.a1, 1, sender=self.t.k2))
self.assert_tx_failed(lambda: self.c_bad.transferFrom(self.t.a2, self.t.a1, 1, sender=self.t.k1))
self.assertTrue(self.c_bad.approve(self.t.a2, MAX_UINT256, sender=self.t.k1))
self.assertEqual(self.c_bad.allowance(self.t.a1, self.t.a2, sender=self.t.k3), MAX_UINT256)
self.assertTrue(self.c_bad.transferFrom(self.t.a1, self.t.a2, MAX_UINT256 - 1, sender=self.t.k2))
self.assertEqual(self.c_bad.allowance(self.t.a1, self.t.a2, sender=self.t.k3), 1)
class TestSolidity1ERC20(TestERC20):
@classmethod
def setUpClass(cls):
super(TestSolidity1ERC20, cls).setUpClass()
with open(PATH_TO_CONTRACTS + '/nonvyper/ERC20_solidity_1.sol') as fd:
contract_code = fd.read()
cls.c = cls.s.contract(contract_code, language='solidity')
cls.initial_state = cls.s.snapshot()
cls.strict_log_mode = True
cls.listenForEvents()
def setUp(self):
super().setUp()
class TestSerpentERC20(TestERC20):
@classmethod
def setUpClass(cls):
super(TestSerpentERC20, cls).setUpClass()
with open(PATH_TO_CONTRACTS + '/nonvyper/ERC20_serpent.se') as fd:
contract_code = fd.read()
cls.c = cls.s.contract(contract_code, language='serpent')
cls.initial_state = cls.s.snapshot()
cls.strict_log_mode = True
cls.listenForEvents()
def setUp(self):
super().setUp()
class TestSolidity2ERC20(TestERC20):
@classmethod
def setUpClass(cls):
super(TestSolidity2ERC20, cls).setUpClass()
with open(PATH_TO_CONTRACTS + '/nonvyper/ERC20_solidity_2.sol') as fd:
contract_code = fd.read()
cls.c = cls.s.contract(contract_code, language='solidity')
cls.initial_state = cls.s.snapshot()
cls.strict_log_mode = True
cls.listenForEvents()
def setUp(self):
super().setUp()
def load_tests(loader, tests, pattern):
full_suite = unittest.TestSuite()
for suite in [TestVyperERC20, TestSolidity1ERC20, TestSolidity2ERC20, TestSerpentERC20]:
tests = loader.loadTestsFromTestCase(suite)
full_suite.addTests(tests)
return full_suite
if __name__ == '__main__':
unittest.main(verbosity=2)