Example #1
0
    def setUp(self):
        self.patcher = ScorePatcher(Converter)
        self.patcher.start()

        self.score_address = Address.from_string("cx" + os.urandom(20).hex())
        self.score = Converter(create_db(self.score_address))

        self.owner = Address.from_string("hx" + os.urandom(20).hex())
        self.token = Address.from_string("cx" + os.urandom(20).hex())
        registry = Address.from_string("cx" + os.urandom(20).hex())
        max_conversion_fee = 1000000
        self.initial_connector_token = Address.from_string(
            "cx" + os.urandom(20).hex())
        self.initial_connector_weight = 500000

        with patch_property(IconScoreBase, 'msg', Message(self.owner)):
            self.score.on_install(self.token, registry, max_conversion_fee,
                                  self.initial_connector_token,
                                  self.initial_connector_weight)
            FlexibleTokenController.on_install.assert_called_with(
                self.score, self.token)
            self.score._token.set(self.token)

            self.assertEqual(registry, self.score._registry.get())
            self.assertEqual(registry, self.score._prev_registry.get())
            self.assertEqual(max_conversion_fee,
                             self.score._max_conversion_fee.get())

            self.assertEqual(
                True, self.score._connectors[
                    self.initial_connector_token].is_set.get())
            self.assertEqual(
                self.initial_connector_weight, self.score._connectors[
                    self.initial_connector_token].weight.get())
Example #2
0
    def setUp(self):
        self.patcher = ScorePatcher(TokenHolder)
        self.patcher.start()

        self.score_address = Address.from_string("cx" + "1" * 40)
        self.token_holder = TokenHolder(create_db(self.score_address))

        self.token_owner = Address.from_string("hx" + "2" * 40)
        with patch_property(IconScoreBase, 'msg', Message(self.token_owner)):
            self.token_holder.on_install()
            Owned.on_install.assert_called_with(self.token_holder)
Example #3
0
    def setUp(self):
        self.patcher = ScorePatcher(Owned)
        self.patcher.start()

        score_address = Address.from_string("cx" + "1" * 40)
        self.score = Owned(create_db(score_address))

        self.sender = Address.from_string("hx" + "2" * 40)
        with patch_property(IconScoreBase, 'msg', Message(self.sender)):
            self.score.on_install()
            self.assertEqual(self.sender, self.score._owner.get())
            self.assertEqual(ZERO_SCORE_ADDRESS, self.score._new_owner.get())
Example #4
0
    def setUp(self):
        self.patcher = ScorePatcher(IcxToken)
        self.patcher.start()

        self.score_address = Address.from_string("cx" + "1" * 40)
        self.icx_token = IcxToken(create_db(self.score_address))

        self.token_owner = Address.from_string("hx" + "2" * 40)
        with patch_property(IconScoreBase, 'msg', Message(self.token_owner)):
            self.icx_token.on_install()
            IRCToken.on_install.assert_called_with(self.icx_token, 'icx_token',
                                                   'ICX', 0, 18)
            TokenHolder.on_install.assert_called_with(self.icx_token)
    def setUp(self):
        self.patcher = ScorePatcher(IcxToken)
        self.patcher.start()

        self.score_address = Address.from_string("cx" + "1" * 40)
        self.flexible_token_address = Address.from_string("cx" + "2" * 40)
        self.score = FlexibleTokenController(create_db(self.score_address))

        self.owner = Address.from_string("hx" + "2" * 40)
        with MultiPatch([
            patch_property(IconScoreBase, 'msg', Message(self.owner)),
            patch('contracts.utility.flexible_token_controller.require_valid_address')
        ]) as mocks:
            self.score.on_install(self.flexible_token_address)
            TokenHolder.on_install.assert_called_with(self.score)
            mocks[1].assert_called()
Example #6
0
    def setUp(self):
        self.patcher = ScorePatcher(ScoreRegistry)
        self.patcher.start()

        self.score_address = Address.from_string("cx" + "1" * 40)
        self.registry_score = ScoreRegistry(create_db(self.score_address))

        self.registry_owner = Address.from_string("hx" + "1" * 40)
        with patch_property(IconScoreBase, 'msg',
                            Message(self.registry_owner)):
            self.registry_score.on_install()
            Owned.on_install.assert_called_with(self.registry_score)

            # success case: when deploy ScoreRegistry, score registry address should be registered by default
            self.assertEqual \
                (self.score_address,
                 self.registry_score._score_address[self.registry_score.SCORE_REGISTRY.encode()])
Example #7
0
    def setUp(self):
        self.patcher = ScorePatcher(Network)
        self.patcher.start()

        self.score_address = Address.from_string("cx" + "1" * 40)
        self.network_score = Network(create_db(self.score_address))

        self.network_owner = Address.from_string("hx" + "1" * 40)

        self.flexible_token_address_list = [
            Address.from_string("cx" + str(i) * 40) for i in range(0, 3)
        ]
        self.connector_token_list = [
            Address.from_string("cx" + str(i) * 40) for i in range(3, 7)
        ]
        self.icx_token = Address.from_string("cx" + "7" * 40)

        with patch_property(IconScoreBase, 'msg', Message(self.network_owner)):
            self.network_score.on_install()
            TokenHolder.on_install.assert_called_with(self.network_score)
Example #8
0
    def setUp(self):
        self.patcher = ScorePatcher(IRCToken)
        self.patcher.start()

        self.score_address = Address.from_string("cx" + "1" * 40)
        self.irc_token = IRCToken(create_db(self.score_address))
        token_name = "test_token"
        token_symbol = "TST"
        token_supply = 100
        token_decimals = 18

        self.token_owner = Address.from_string("hx" + "2" * 40)

        # failure case: total supply is under 0
        with patch_property(IconScoreBase, 'msg', Message(self.token_owner)):
            invalid_token_supply = -1
            self.assertRaises(RevertException, self.irc_token.on_install,
                              token_name, token_symbol, invalid_token_supply,
                              token_decimals)

        # failure case: decimal is under 0
        with patch_property(IconScoreBase, 'msg', Message(self.token_owner)):
            invalid_token_decimals = -1
            self.assertRaises(RevertException, self.irc_token.on_install,
                              token_name, token_symbol, token_supply,
                              invalid_token_decimals)

        # success case: deploy IRCToken with valid parameters
        with patch_property(IconScoreBase, 'msg', Message(self.token_owner)):
            self.irc_token.on_install(token_name, token_symbol, token_supply,
                                      token_decimals)
            self.assertEqual(token_name, self.irc_token._name.get())
            self.assertEqual(token_symbol, self.irc_token._symbol.get())
            self.assertEqual(token_decimals, self.irc_token._decimals.get())
            self.assertEqual(token_supply * 10**token_decimals,
                             self.irc_token._total_supply.get())
            self.assertEqual(token_supply * 10**token_decimals,
                             self.irc_token._balances[self.token_owner])
Example #9
0
class TestOwned(unittest.TestCase):
    def setUp(self):
        self.patcher = ScorePatcher(Owned)
        self.patcher.start()

        score_address = Address.from_string("cx" + "1" * 40)
        self.score = Owned(create_db(score_address))

        self.sender = Address.from_string("hx" + "2" * 40)
        with patch_property(IconScoreBase, 'msg', Message(self.sender)):
            self.score.on_install()
            self.assertEqual(self.sender, self.score._owner.get())
            self.assertEqual(ZERO_SCORE_ADDRESS, self.score._new_owner.get())

    def tearDown(self):
        self.patcher.stop()

    def test_getOwner(self):
        actual_owner = self.score.getOwner()
        self.assertEqual(self.score._owner.get(), actual_owner)

    def test_getNewOwner(self):
        actual_new_owner = self.score.getNewOwner()
        self.assertEqual(self.score._new_owner.get(), actual_new_owner)

    def test_transferOwnerShip(self):
        # failure case: non owner try to transfer ownership.
        non_owner = Address.from_string("hx" + "3" * 40)
        new_owner = Address.from_string("hx" + "4" * 40)
        with patch_property(IconScoreBase, 'msg', Message(non_owner)):
            self.assertRaises(RevertException, self.score.transferOwnerShip,
                              new_owner)

        # failure case: set new owner as previous owner
        with patch_property(IconScoreBase, 'msg', Message(self.sender)):
            self.assertRaises(RevertException, self.score.transferOwnerShip,
                              self.sender)

        # success case: transfer ownership to new_owner
        with patch_property(IconScoreBase, 'msg', Message(self.sender)):
            self.score.require_owner_only = Mock()
            self.score.transferOwnerShip(new_owner)
            self.score.require_owner_only.assert_called()
            self.assertEqual(self.sender, self.score._owner.get())
            self.assertEqual(new_owner, self.score._new_owner.get())

    def test_acceptOwnership(self):
        # ### initial setting for test start
        new_owner = Address.from_string("hx" + "4" * 40)
        with patch_property(IconScoreBase, 'msg', Message(self.sender)):
            self.score.transferOwnerShip(new_owner)
        # ### initial setting for test end

        # failure case: current owner try to accept ownership ( only new owner can accept ownership)
        with patch_property(IconScoreBase, 'msg', Message(self.sender)):
            self.assertRaises(RevertException, self.score.acceptOwnerShip)

        # failure case: another address try to accept ownership
        # ( only new owner can accept ownership)
        another_owner = Address.from_string("hx" + "5" * 40)
        with patch_property(IconScoreBase, 'msg', Message(another_owner)):
            self.assertRaises(RevertException, self.score.acceptOwnerShip)

        # success case: new owner accept ownership
        with patch_property(IconScoreBase, 'msg', Message(new_owner)):
            self.score.acceptOwnerShip()
            self.assertEqual(new_owner, self.score._owner.get())
            self.assertEqual(ZERO_SCORE_ADDRESS, self.score._new_owner.get())

            self.score.OwnerUpdate.assert_called_with(self.sender, new_owner)
Example #10
0
class TestTokenHolder(unittest.TestCase):
    def setUp(self):
        self.patcher = ScorePatcher(TokenHolder)
        self.patcher.start()

        self.score_address = Address.from_string("cx" + "1" * 40)
        self.token_holder = TokenHolder(create_db(self.score_address))

        self.token_owner = Address.from_string("hx" + "2" * 40)
        with patch_property(IconScoreBase, 'msg', Message(self.token_owner)):
            self.token_holder.on_install()
            Owned.on_install.assert_called_with(self.token_holder)

    def tearDown(self):
        self.patcher.stop()

    def test_withdrawTokens(self):
        token_address = Address.from_string("cx" + "2" * 40)
        token_receiver = Address.from_string("hx" + "3" * 40)
        irc_token_score_interface = \
            self.token_holder.create_interface_score(token_address, ProxyScore(ABCIRCToken))
        irc_token_score_interface.transfer = Mock()

        with MultiPatch([
                patch_property(IconScoreBase, 'msg',
                               Message(self.token_owner)),
                patch.object(TokenHolder,
                             'create_interface_score',
                             return_value=irc_token_score_interface)
        ]):
            amount = 10
            # success case: withdraw 10 token
            self.token_holder.withdrawTokens(token_address, token_receiver,
                                             amount)
            self.token_holder.create_interface_score.assert_called_with(
                token_address, ProxyScore(ABCIRCToken))
            irc_token_score_interface.transfer.assert_called_with(
                token_receiver, amount)
            self.token_holder.create_interface_score.reset_mock()
            irc_token_score_interface.transfer.reset_mock()

            # failure case: amount is under 0
            invalid_amount = -1
            self.assertRaises(RevertException,
                              self.token_holder.withdrawTokens, token_address,
                              token_receiver, invalid_amount)
            self.token_holder.create_interface_score.assert_not_called()
            irc_token_score_interface.transfer.assert_not_called()
            self.token_holder.create_interface_score.reset_mock()
            irc_token_score_interface.transfer.reset_mock()

            # failure case: 'to' address is this
            self.assertRaises(RevertException,
                              self.token_holder.withdrawTokens, token_address,
                              self.score_address, amount)
            self.token_holder.create_interface_score.assert_not_called()
            irc_token_score_interface.transfer.assert_not_called()
            self.token_holder.create_interface_score.reset_mock()
            irc_token_score_interface.transfer.reset_mock()

            # failure case: 'to' address is invalid address
            self.assertRaises(RevertException,
                              self.token_holder.withdrawTokens, token_address,
                              ZERO_SCORE_ADDRESS, amount)
            self.token_holder.create_interface_score.assert_not_called()
            irc_token_score_interface.transfer.assert_not_called()
            self.token_holder.create_interface_score.reset_mock()
            irc_token_score_interface.transfer.reset_mock()
Example #11
0
class TestIRCToken(unittest.TestCase):
    def setUp(self):
        self.patcher = ScorePatcher(IRCToken)
        self.patcher.start()

        self.score_address = Address.from_string("cx" + "1" * 40)
        self.irc_token = IRCToken(create_db(self.score_address))
        token_name = "test_token"
        token_symbol = "TST"
        token_supply = 100
        token_decimals = 18

        self.token_owner = Address.from_string("hx" + "2" * 40)

        # failure case: total supply is under 0
        with patch_property(IconScoreBase, 'msg', Message(self.token_owner)):
            invalid_token_supply = -1
            self.assertRaises(RevertException, self.irc_token.on_install,
                              token_name, token_symbol, invalid_token_supply,
                              token_decimals)

        # failure case: decimal is under 0
        with patch_property(IconScoreBase, 'msg', Message(self.token_owner)):
            invalid_token_decimals = -1
            self.assertRaises(RevertException, self.irc_token.on_install,
                              token_name, token_symbol, token_supply,
                              invalid_token_decimals)

        # success case: deploy IRCToken with valid parameters
        with patch_property(IconScoreBase, 'msg', Message(self.token_owner)):
            self.irc_token.on_install(token_name, token_symbol, token_supply,
                                      token_decimals)
            self.assertEqual(token_name, self.irc_token._name.get())
            self.assertEqual(token_symbol, self.irc_token._symbol.get())
            self.assertEqual(token_decimals, self.irc_token._decimals.get())
            self.assertEqual(token_supply * 10**token_decimals,
                             self.irc_token._total_supply.get())
            self.assertEqual(token_supply * 10**token_decimals,
                             self.irc_token._balances[self.token_owner])

    def tearDown(self):
        self.patcher.stop()

    def test_external_transfer(self):
        token_receiver = Address.from_string("hx" + "3" * 40)
        with MultiPatch([
                patch_property(IconScoreBase, 'msg',
                               Message(self.token_owner)),
                patch.object(IRCToken, '_transfer')
        ]):
            self.irc_token.transfer(token_receiver, 10)
            IRCToken._transfer.assert_called_with(self.token_owner,
                                                  token_receiver, 10, b'None')

            self.irc_token.transfer(token_receiver, 10, b'test')
            IRCToken._transfer.assert_called_with(self.token_owner,
                                                  token_receiver, 10, b'test')

    def test_transfer(self):
        eoa_token_receiver = Address.from_string("hx" + "3" * 40)
        score_token_receiver = Address.from_string("cx" + "3" * 40)

        with MultiPatch([
                patch_property(IconScoreBase, 'msg',
                               Message(self.token_owner)),
                patch.object(TokenFallbackInterface, 'tokenFallback')
        ]):
            # failure case: value is under 0
            invalid_value = -1
            self.assertRaises(RevertException, self.irc_token._transfer,
                              self.token_owner, eoa_token_receiver,
                              invalid_value, b'None')

            # failure case: value is higher than senders' total balance
            value = self.irc_token._balances[self.token_owner] + 1
            self.assertRaises(RevertException, self.irc_token._transfer,
                              self.token_owner, eoa_token_receiver, value,
                              b'None')

        # success cass: transfer 10 token to token_receiver (EOA)
        with MultiPatch([
                patch_property(IconScoreBase, 'msg',
                               Message(self.token_owner)),
                patch.object(TokenFallbackInterface, 'tokenFallback')
        ]):
            value = 10
            before_owner_balance = self.irc_token._balances[self.token_owner]
            before_eoa_receiver_balance = self.irc_token._balances[
                eoa_token_receiver]
            self.irc_token._transfer(self.token_owner, eoa_token_receiver,
                                     value, b'None')
            self.assertEqual(before_owner_balance - value,
                             self.irc_token._balances[self.token_owner])
            self.assertEqual(before_eoa_receiver_balance + value,
                             self.irc_token._balances[eoa_token_receiver])
            TokenFallbackInterface.tokenFallback.assert_not_called()

            self.irc_token.Transfer.assert_called_with(self.token_owner,
                                                       eoa_token_receiver,
                                                       value, b'None')

        # success cass: transfer 10 token to token_receiver (SCORE)
        with MultiPatch([
                patch_property(IconScoreBase, 'msg',
                               Message(self.token_owner)),
                patch.object(TokenFallbackInterface, 'tokenFallback')
        ]):
            value = 10
            before_owner_balance = self.irc_token._balances[self.token_owner]
            before_score_receiver_balance = self.irc_token._balances[
                score_token_receiver]
            self.irc_token._transfer(self.token_owner, score_token_receiver,
                                     value, b'None')
            self.assertEqual(before_owner_balance - value,
                             self.irc_token._balances[self.token_owner])
            self.assertEqual(before_score_receiver_balance + value,
                             self.irc_token._balances[score_token_receiver])
            TokenFallbackInterface.tokenFallback.assert_called_with(
                self.token_owner, value, b'None')

            self.irc_token.Transfer.assert_called_with(self.token_owner,
                                                       score_token_receiver,
                                                       value, b'None')
Example #12
0
class TestScoreRegistry(unittest.TestCase):
    def setUp(self):
        self.patcher = ScorePatcher(ScoreRegistry)
        self.patcher.start()

        self.score_address = Address.from_string("cx" + "1" * 40)
        self.registry_score = ScoreRegistry(create_db(self.score_address))

        self.registry_owner = Address.from_string("hx" + "1" * 40)
        with patch_property(IconScoreBase, 'msg',
                            Message(self.registry_owner)):
            self.registry_score.on_install()
            Owned.on_install.assert_called_with(self.registry_score)

            # success case: when deploy ScoreRegistry, score registry address should be registered by default
            self.assertEqual \
                (self.score_address,
                 self.registry_score._score_address[self.registry_score.SCORE_REGISTRY.encode()])

    def tearDown(self):
        self.patcher.stop()

    def test_getAddress(self):
        # success case: search the registered score address
        registered_score_name = self.registry_score.SCORE_REGISTRY
        actual_registered_address = self.registry_score.getAddress(
            registered_score_name)
        self.assertEqual(self.score_address, actual_registered_address)

        # success case: search the score address which has not been registered (should return zero score address)
        unregistered_score_name = self.registry_score.NETWORK
        actual_registered_address = self.registry_score.getAddress(
            unregistered_score_name)
        self.assertEqual(ZERO_SCORE_ADDRESS, actual_registered_address)

    def test_registerAddress(self):
        eoa_address = Address.from_string("hx" + "3" * 40)
        network_id = self.registry_score.NETWORK
        network_address = Address.from_string("cx" + "2" * 40)

        with patch_property(IconScoreBase, 'msg',
                            Message(self.registry_owner)):
            # failure case: invalid register score address
            self.assertRaises(RevertException,
                              self.registry_score.registerAddress, network_id,
                              ZERO_SCORE_ADDRESS)

            # failure case: try to register eoa address
            self.assertRaises(RevertException,
                              self.registry_score.registerAddress, network_id,
                              eoa_address)

            # failure case: score name is not in the SCORE_KEYS
            non_listed_id = "NON_LISTED_SCORE_ID"
            self.assertRaises(RevertException,
                              self.registry_score.registerAddress,
                              non_listed_id, network_address)

            # success case: register network
            self.registry_score.registerAddress(network_id, network_address)
            self.assertEqual(network_address,
                             self.registry_score._score_address[network_id])
            self.registry_score.AddressUpdate.assert_called_with(
                network_id, network_address)

    def test_unregisterAddress(self):
        network_id = self.registry_score.NETWORK
        network_address = Address.from_string("cx" + "2" * 40)

        # register network
        self.registry_score._score_address[network_id] = network_address

        with patch_property(IconScoreBase, 'msg',
                            Message(self.registry_owner)):
            # failure case: try to unregister not recorded score address
            non_registered_id = 'abc'
            self.assertRaises(RevertException,
                              self.registry_score.unregisterAddress,
                              non_registered_id)

            # success case: unregister score address which has been registered
            self.registry_score.unregisterAddress(network_id)
            self.assertEqual(None,
                             self.registry_score._score_address[network_id])

            self.registry_score.AddressUpdate.assert_called_with(
                network_id, ZERO_SCORE_ADDRESS)
class TestFlexibleTokenController(unittest.TestCase):

    def setUp(self):
        self.patcher = ScorePatcher(IcxToken)
        self.patcher.start()

        self.score_address = Address.from_string("cx" + "1" * 40)
        self.flexible_token_address = Address.from_string("cx" + "2" * 40)
        self.score = FlexibleTokenController(create_db(self.score_address))

        self.owner = Address.from_string("hx" + "2" * 40)
        with MultiPatch([
            patch_property(IconScoreBase, 'msg', Message(self.owner)),
            patch('contracts.utility.flexible_token_controller.require_valid_address')
        ]) as mocks:
            self.score.on_install(self.flexible_token_address)
            TokenHolder.on_install.assert_called_with(self.score)
            mocks[1].assert_called()

    def tearDown(self):
        self.patcher.stop()

    def test_transferTokenOwnership(self):
        new_owner = Address.from_string("hx" + "3" * 40)

        with MultiPatch([
            patch_property(IconScoreBase, 'msg', Message(self.owner)),
            patch.object(InternalCall, 'other_external_call'),
        ]):
            self.score.transferTokenOwnership(new_owner)
            self.score.require_owner_only.assert_called()

            assert_inter_call(
                self,
                self.score.address,
                self.score._token.get(),
                'transferOwnerShip',
                [new_owner])

    def test_acceptTokenOwnership(self):
        with MultiPatch([
            patch_property(IconScoreBase, 'msg', Message(self.owner)),
            patch.object(InternalCall, 'other_external_call'),
        ]):
            self.score.acceptTokenOwnership()
            self.score.require_owner_only.assert_called()

            assert_inter_call(
                self,
                self.score.address,
                self.score._token.get(),
                'acceptOwnerShip',
                []
            )

    def test_disableTokenTransfers(self):
        disable = True
        with MultiPatch([
            patch_property(IconScoreBase, 'msg', Message(self.owner)),
            patch.object(InternalCall, 'other_external_call'),
        ]):
            self.score.disableTokenTransfers(disable)
            self.score.require_owner_only.assert_called()

            assert_inter_call(
                self,
                self.score.address,
                self.score._token.get(),
                'disableTransfer',
                [disable]
            )

    def test_withdrawFromToken(self):
        token = Address.from_string("cx" + "3" * 40)
        to = Address.from_string("hx" + "3" * 40)
        amount = 100
        with MultiPatch([
            patch_property(IconScoreBase, 'msg', Message(self.owner)),
            patch.object(InternalCall, 'other_external_call'),
        ]):
            self.score.withdrawFromToken(token, to, amount)
            self.score.require_owner_only.assert_called()

            assert_inter_call(
                self,
                self.score.address,
                self.score._token.get(),
                'withdrawTokens',
                [token, to, amount]
            )

    def test_isActive(self):
        with MultiPatch([
            patch_property(IconScoreBase, 'msg', Message(self.owner)),
            patch.object(InternalCall, 'other_external_call'),
        ]) as mocks:
            mocks[1].return_value = self.score.address
            is_active = self.score.isActive()

            self.assertEqual(True, is_active)

        with MultiPatch([
            patch_property(IconScoreBase, 'msg', Message(self.owner)),
            patch.object(InternalCall, 'other_external_call'),
        ]) as mocks:
            mocks[1].return_value = ZERO_SCORE_ADDRESS
            is_active = self.score.isActive()

            self.assertEqual(False, is_active)

    def test_getToken(self):
        with patch.object(IconScoreBase, 'msg', Message(self.owner)):
            token = self.score.getToken()

            self.assertEqual(self.score._token.get(), token)
Example #14
0
class TestIcxToken(unittest.TestCase):
    def setUp(self):
        self.patcher = ScorePatcher(IcxToken)
        self.patcher.start()

        self.score_address = Address.from_string("cx" + "1" * 40)
        self.icx_token = IcxToken(create_db(self.score_address))

        self.token_owner = Address.from_string("hx" + "2" * 40)
        with patch_property(IconScoreBase, 'msg', Message(self.token_owner)):
            self.icx_token.on_install()
            IRCToken.on_install.assert_called_with(self.icx_token, 'icx_token',
                                                   'ICX', 0, 18)
            TokenHolder.on_install.assert_called_with(self.icx_token)

    def tearDown(self):
        self.patcher.stop()

    def test_deposit(self):
        value = 10

        with patch_property(IconScoreBase, 'msg',
                            Message(self.token_owner, value=value)):
            before_balance = self.icx_token._balances[self.token_owner]
            before_total_supply = self.icx_token._total_supply.get()

            self.icx_token.deposit()
            self.assertEqual(
                value,
                self.icx_token._balances[self.token_owner] - before_balance)
            self.assertEqual(
                value,
                self.icx_token._total_supply.get() - before_total_supply)

            self.icx_token.Issuance.assert_called_with(value)
            self.icx_token.Transfer.assert_called_with(self.icx_token.address,
                                                       self.token_owner, value,
                                                       b'None')

    def test_withdrawTo(self):
        to = Address.from_string("hx" + "3" * 40)

        self.icx_token._balances[self.token_owner] = 10
        self.icx_token._total_supply.set(10)

        with patch_property(IconScoreBase, 'msg', Message(self.token_owner)):
            # failure case: amount is under 0
            self.assertRaises(RevertException, self.icx_token.withdrawTo, -1,
                              to)

            # failure case: amount is higher than token holders' total balance
            self.assertRaises(RevertException, self.icx_token.withdrawTo, 20,
                              to)

            # success case: withdraw 10 token to 'to'
            self.icx_token.withdrawTo(10, to)
            self.icx_token.icx.transfer.assert_called_with(to, 10)

            self.assertEqual(0, self.icx_token._balances[self.token_owner])
            self.assertEqual(0, self.icx_token._total_supply.get())
            self.icx_token.Destruction.assert_called_with(10)

    def test_transfer(self):
        # failure case: transfer token to this score (should raise error)
        with patch_property(IconScoreBase, 'msg', Message(self.token_owner)):
            self.assertRaises(RevertException, self.icx_token.transfer,
                              self.score_address, 10)
            IRCToken.transfer.assert_not_called()

        # success case: send 10 token to other
        token_receiver = Address.from_string("hx" + "4" * 40)
        self.icx_token.transfer(token_receiver, 10)

        IRCToken.transfer.assert_called_with(self.icx_token, token_receiver,
                                             10, None)
Example #15
0
class TestConverter(unittest.TestCase):
    def setUp(self):
        self.patcher = ScorePatcher(Converter)
        self.patcher.start()

        self.score_address = Address.from_string("cx" + os.urandom(20).hex())
        self.score = Converter(create_db(self.score_address))

        self.owner = Address.from_string("hx" + os.urandom(20).hex())
        self.token = Address.from_string("cx" + os.urandom(20).hex())
        registry = Address.from_string("cx" + os.urandom(20).hex())
        max_conversion_fee = 1000000
        self.initial_connector_token = Address.from_string(
            "cx" + os.urandom(20).hex())
        self.initial_connector_weight = 500000

        with patch_property(IconScoreBase, 'msg', Message(self.owner)):
            self.score.on_install(self.token, registry, max_conversion_fee,
                                  self.initial_connector_token,
                                  self.initial_connector_weight)
            FlexibleTokenController.on_install.assert_called_with(
                self.score, self.token)
            self.score._token.set(self.token)

            self.assertEqual(registry, self.score._registry.get())
            self.assertEqual(registry, self.score._prev_registry.get())
            self.assertEqual(max_conversion_fee,
                             self.score._max_conversion_fee.get())

            self.assertEqual(
                True, self.score._connectors[
                    self.initial_connector_token].is_set.get())
            self.assertEqual(
                self.initial_connector_weight, self.score._connectors[
                    self.initial_connector_token].weight.get())

    def tearDown(self):
        self.patcher.stop()

    def test_tokenFallback_deposit(self):
        # Mocks parent functions
        self.score.getOwner.return_value = self.owner
        self.score._is_active.return_value = False

        network_address = Address.from_string("cx" + os.urandom(20).hex())

        # success case
        token = self.initial_connector_token
        sender = self.owner
        value = 100
        with MultiPatch([
                patch_property(IconScoreBase, 'msg', Message(token)),
                patch.object(InternalCall,
                             'other_external_call',
                             return_value=network_address)
        ]):
            self.score.tokenFallback(sender, value, b'None')

        # the value is less than zero
        token = self.initial_connector_token
        sender = self.owner
        value = -100
        with MultiPatch([
                patch_property(IconScoreBase, 'msg', Message(token)),
                patch.object(InternalCall,
                             'other_external_call',
                             return_value=network_address)
        ]):
            self.assertRaises(RevertException, self.score.tokenFallback,
                              sender, value, b'None')

        # the sender is not owner
        token = self.initial_connector_token
        sender = Address.from_string("hx" + os.urandom(20).hex())
        value = 100
        with MultiPatch([
                patch_property(IconScoreBase, 'msg', Message(token)),
                patch.object(InternalCall,
                             'other_external_call',
                             return_value=network_address)
        ]):
            self.assertRaises(RevertException, self.score.tokenFallback,
                              sender, value, b'None')

        # the token is not connector token
        token = Address.from_string("cx" + os.urandom(20).hex())
        sender = self.owner
        value = 100
        with MultiPatch([
                patch_property(IconScoreBase, 'msg', Message(token)),
                patch.object(InternalCall,
                             'other_external_call',
                             return_value=network_address)
        ]):
            self.assertRaises(RevertException, self.score.tokenFallback,
                              sender, value, b'None')

        # the converter is active
        self.score._is_active.return_value = True
        token = self.initial_connector_token
        sender = self.owner
        value = 100
        with MultiPatch([
                patch_property(IconScoreBase, 'msg', Message(token)),
                patch.object(InternalCall,
                             'other_external_call',
                             return_value=network_address)
        ]):
            self.assertRaises(RevertException, self.score.tokenFallback,
                              sender, value, b'None')

    def test_tokenFallback_convert_called_wrong_params(self):
        # Mocks parent functions
        self.score.getOwner.return_value = self.owner
        self.score._is_active.return_value = True
        self.score._convert = Mock()

        network_address = Address.from_string("cx" + os.urandom(20).hex())

        to_token = Address.from_string("cx" + os.urandom(20).hex())

        # missing param `minReturn`
        data = {
            'toToken': str(to_token),
        }

        token = self.initial_connector_token
        value = 100
        with MultiPatch([
                patch_property(IconScoreBase, 'msg', Message(token)),
                patch.object(InternalCall,
                             'other_external_call',
                             return_value=network_address)
        ]):

            self.assertRaises(RevertException, self.score.tokenFallback,
                              network_address, value,
                              json_dumps(data).encode())

            self.score._convert.assert_not_called()

    def test_tokenFallback_buy_called(self):
        # Mocks parent functions
        self.score.getOwner.return_value = self.owner
        self.score._is_active.return_value = True
        self.score._buy = Mock()

        network_address = Address.from_string("cx" + os.urandom(20).hex())

        to_token = self.token
        min_return = 10

        data = {'toToken': str(to_token), 'minReturn': min_return}

        # success case
        token = self.initial_connector_token
        value = 100
        with MultiPatch([
                patch_property(IconScoreBase, 'msg', Message(token)),
                patch.object(InternalCall,
                             'other_external_call',
                             return_value=network_address)
        ]):
            self.score.tokenFallback(network_address, value,
                                     json_dumps(data).encode())
            assert_inter_call(self, self.score.address,
                              self.score._registry.get(), 'getAddress',
                              [ScoreRegistry.NETWORK])
            self.score._buy.assert_called_with(network_address, token, value,
                                               min_return)

    def test_tokenFallback_sell_called(self):
        # Mocks parent functions
        self.score.getOwner.return_value = self.owner
        self.score._is_active.return_value = True
        self.score._sell = Mock()

        network_address = Address.from_string("cx" + os.urandom(20).hex())

        to_token = self.initial_connector_token
        min_return = 10

        data = {'toToken': str(to_token), 'minReturn': min_return}

        # success case
        token = self.token
        value = 100
        with MultiPatch([
                patch_property(IconScoreBase, 'msg', Message(token)),
                patch.object(InternalCall,
                             'other_external_call',
                             return_value=network_address)
        ]):
            self.score.tokenFallback(network_address, value,
                                     json_dumps(data).encode())
            assert_inter_call(self, self.score.address,
                              self.score._registry.get(), 'getAddress',
                              [ScoreRegistry.NETWORK])
            self.score._sell.assert_called_with(network_address, to_token,
                                                value, min_return)

    def test_tokenFallback_cross_convert_called(self):
        # Mocks parent functions
        self.score.getOwner.return_value = self.owner
        self.score._is_active.return_value = True
        self.score._convert_cross_connector = Mock()

        network_address = Address.from_string("cx" + os.urandom(20).hex())

        to_token = Address.from_string("cx" + os.urandom(20).hex())
        to_token_weight = 500000
        self.score.addConnector(to_token, to_token_weight, False)

        min_return = 10

        data = {'toToken': str(to_token), 'minReturn': min_return}

        # success case
        token = self.initial_connector_token
        value = 100
        with MultiPatch([
                patch_property(IconScoreBase, 'msg', Message(token)),
                patch.object(InternalCall,
                             'other_external_call',
                             return_value=network_address)
        ]):
            self.score.tokenFallback(network_address, value,
                                     json_dumps(data).encode())
            assert_inter_call(self, self.score.address,
                              self.score._registry.get(), 'getAddress',
                              [ScoreRegistry.NETWORK])
            self.score._convert_cross_connector.assert_called_with(
                network_address, token, to_token, value, min_return)

    def test_buy(self):
        connector_token2 = Address.from_string("cx" + os.urandom(20).hex())
        connector_token2_weight = 500000
        self.score.addConnector(connector_token2, connector_token2_weight,
                                False)

        return_amount = 100
        return_fee = 1
        connector_balance = 100
        trader = Address.from_string("cx" + os.urandom(20).hex())
        amount = 100
        min_return = 100

        purchase_return = {'amount': return_amount, 'fee': return_fee}
        self.score.get_purchase_return = Mock(return_value=purchase_return)
        self.score.getConnectorBalance = Mock(return_value=connector_balance)
        inter_call_return = Mock()

        with patch.object(InternalCall,
                          'other_external_call',
                          return_value=inter_call_return):
            result = self.score._buy(trader, connector_token2, amount,
                                     min_return)
            assert_inter_call(self, self.score.address,
                              self.score._token.get(), 'issue',
                              [trader, return_amount])
            self.score.Conversion.assert_called_with(connector_token2,
                                                     self.score._token.get(),
                                                     trader, amount,
                                                     return_amount, return_fee)
            self.score.PriceDataUpdate.assert_called_with(
                connector_token2, inter_call_return, connector_balance,
                connector_token2_weight)
            self.assertEqual(return_amount, result)

    def test_sell(self):
        connector_token2 = Address.from_string("cx" + os.urandom(20).hex())
        connector_token2_weight = 500000
        self.score.addConnector(connector_token2, connector_token2_weight,
                                False)

        return_amount = 100
        return_fee = 1
        connector_balance = 1000
        trader = Address.from_string("cx" + os.urandom(20).hex())
        amount = 100
        min_return = 100

        sale_return = {'amount': return_amount, 'fee': return_fee}
        self.score.get_sale_return = Mock(return_value=sale_return)
        self.score.getConnectorBalance = Mock(return_value=connector_balance)
        inter_call_return = Mock()

        with patch.object(InternalCall,
                          'other_external_call',
                          return_value=inter_call_return):
            result = self.score._sell(trader, connector_token2, amount,
                                      min_return)
            assert_inter_call(self, self.score.address,
                              self.score._token.get(), 'destroy',
                              [self.score.address, return_amount])
            self.score.Conversion.assert_called_with(self.score._token.get(),
                                                     connector_token2, trader,
                                                     amount, return_amount,
                                                     return_fee)
            self.score.PriceDataUpdate.assert_called_with(
                connector_token2, inter_call_return, connector_balance,
                connector_token2_weight)
            self.assertEqual(return_amount, result)

    def test_convert_cross_connector(self):
        connector_token2 = Address.from_string("cx" + os.urandom(20).hex())
        connector_token2_weight = 500000
        self.score.addConnector(connector_token2, connector_token2_weight,
                                False)

        return_amount = 100
        return_fee = 1
        connector_balance = 1000
        trader = Address.from_string("cx" + os.urandom(20).hex())
        amount = 100
        min_return = 100

        convert_return = {'amount': return_amount, 'fee': return_fee}
        self.score.get_cross_connector_return = Mock(
            return_value=convert_return)
        self.score.getConnectorBalance = Mock(return_value=connector_balance)
        inter_call_return = Mock()

        with patch.object(InternalCall,
                          'other_external_call',
                          return_value=inter_call_return):
            self.score._convert_cross_connector(trader,
                                                self.initial_connector_token,
                                                connector_token2, amount,
                                                min_return)
            assert_inter_call(self, self.score.address, connector_token2,
                              'transfer',
                              [trader, return_amount, TRANSFER_DATA])

            self.score.Conversion.assert_called_with(
                self.initial_connector_token, connector_token2, trader, amount,
                return_amount, return_fee)

            first_price_update_event = self.score.PriceDataUpdate.call_args_list[
                0]
            self.assertEqual(first_price_update_event[0][0],
                             self.initial_connector_token)
            self.assertEqual(first_price_update_event[0][1], inter_call_return)
            self.assertEqual(first_price_update_event[0][2], connector_balance)
            self.assertEqual(first_price_update_event[0][3],
                             self.initial_connector_weight)

            second_price_update_event = self.score.PriceDataUpdate.call_args_list[
                1]
            self.assertEqual(second_price_update_event[0][0], connector_token2)
            self.assertEqual(second_price_update_event[0][1],
                             inter_call_return)
            self.assertEqual(second_price_update_event[0][2],
                             connector_balance)
            self.assertEqual(second_price_update_event[0][3],
                             connector_token2_weight)

    def test_addConnector(self):
        self.score.require_owner_only.reset_mock()

        connector_token = Address.from_string("cx" + os.urandom(20).hex())
        connector_weight = 500000

        with patch_property(IconScoreBase, 'msg', Message(self.owner)):
            self.score.addConnector(connector_token, connector_weight, False)
            self.score.require_owner_only.assert_called()

            self.assertEqual(
                connector_weight,
                self.score._connectors[connector_token].weight.get())
            self.assertEqual(
                False, self.score._connectors[connector_token].
                is_virtual_balance_enabled.get())
            self.assertEqual(
                True, self.score._connectors[connector_token].is_set.get())

    def test_updateConnector(self):
        self.score.require_owner_only.reset_mock()

        connector_token = self.initial_connector_token
        connector_weight = 100000

        with patch_property(IconScoreBase, 'msg', Message(self.owner)):
            self.score.updateConnector(connector_token, connector_weight, True,
                                       10000)
            self.score.require_owner_only.assert_called()

            self.assertEqual(
                connector_weight,
                self.score._connectors[connector_token].weight.get())
            self.assertEqual(
                True, self.score._connectors[connector_token].
                is_virtual_balance_enabled.get())
            self.assertEqual(
                True, self.score._connectors[connector_token].is_set.get())

    def test_updateConnector_wrong_max_weight(self):
        self.score.require_owner_only.reset_mock()

        connector_token = self.initial_connector_token
        connector_weight = 1000001

        self.assertRaises(RevertException, self.score.updateConnector,
                          connector_token, connector_weight, True, 10000)

    def test_disableConnectorPurchases(self):
        self.score.require_owner_or_manager_only.reset_mock()

        self.score._conversions_enabled.set(True)

        with patch_property(IconScoreBase, 'msg', Message(self.owner)):
            self.score.disableConnectorPurchases(self.initial_connector_token,
                                                 True)
            self.score.require_owner_only.assert_called()

            connector_token = self.score._connectors[
                self.initial_connector_token]
            self.assertEqual(False, connector_token.is_purchase_enabled.get())
            self.score.disableConnectorPurchases(self.initial_connector_token,
                                                 False)
            self.assertEqual(True, connector_token.is_purchase_enabled.get())

    def test_updateRegistry(self):
        self.score._allow_registry_update.set(True)

        old_registry = self.score._registry.get()
        new_registry = Address.from_string("cx" + os.urandom(20).hex())

        with MultiPatch([
                patch_property(IconScoreBase, 'msg', Message(self.owner)),
                patch.object(InternalCall,
                             'other_external_call',
                             return_value=new_registry)
        ]):
            self.score.updateRegistry()

            assert_inter_call(self, self.score.address, old_registry,
                              'getAddress', [ScoreRegistry.SCORE_REGISTRY])

            self.assertEqual(old_registry, self.score._prev_registry.get())
            self.assertEqual(new_registry, self.score._registry.get())

    def test_restoreRegistry(self):
        self.score.require_owner_or_manager_only.reset_mock()

        prev_registry = self.score._prev_registry.get()

        with patch_property(IconScoreBase, 'msg', Message(self.owner)):
            self.score.restoreRegistry()
            self.score.require_owner_or_manager_only.assert_called()

            self.assertEqual(prev_registry, self.score._prev_registry.get())
            self.assertEqual(prev_registry, self.score._registry.get())

    def test_disableRegistryUpdate(self):
        self.score.require_owner_or_manager_only.reset_mock()

        with patch_property(IconScoreBase, 'msg', Message(self.owner)):
            self.score.disableRegistryUpdate(True)
            self.score.require_owner_or_manager_only.assert_called()

            self.assertEqual(False, self.score._allow_registry_update.get())
            self.score.disableRegistryUpdate(False)
            self.assertEqual(True, self.score._allow_registry_update.get())

    def test_disableConversions(self):
        self.score.require_owner_or_manager_only.reset_mock()

        self.score._conversions_enabled.set(True)

        with patch_property(IconScoreBase, 'msg', Message(self.owner)):
            self.score.disableConversions(True)
            self.score.require_owner_or_manager_only.assert_called()
            self.score.ConversionsEnable.assert_called_with(False)

            self.assertEqual(False, self.score._conversions_enabled.get())
            self.score.disableConversions(False)
            self.assertEqual(True, self.score._conversions_enabled.get())

    def test_setConversionFee(self):
        self.score.require_owner_or_manager_only.reset_mock()

        old_conversion_fee = self.score._conversion_fee.get()
        conversion_fee = 10000

        with patch_property(IconScoreBase, 'msg', Message(self.owner)):
            self.score.setConversionFee(conversion_fee)
            self.score.require_owner_or_manager_only.assert_called()
            self.score.ConversionFeeUpdate.assert_called_with(
                old_conversion_fee, conversion_fee)

            self.assertEqual(self.score._conversion_fee.get(), conversion_fee)

    def test_withdrawTokens(self):
        to = Address.from_string("hx" + os.urandom(20).hex())
        amount = 100

        self.score._is_active.return_value = True
        self.score._connectors[self.initial_connector_token].is_set.set(False)
        with patch_property(IconScoreBase, 'msg', Message(self.owner)):
            self.score.withdrawTokens(self.initial_connector_token, to, amount)
            self.score._is_active.assert_called()
            FlexibleTokenController.withdrawTokens.assert_called_with(
                self.initial_connector_token, to, amount)

        self.score._is_active.reset_mock()
        self.score._is_active.return_value = False
        self.score._connectors[self.initial_connector_token].is_set.set(True)
        with patch_property(IconScoreBase, 'msg', Message(self.owner)):
            self.score.withdrawTokens(self.initial_connector_token, to, amount)
            self.score._is_active.assert_called()
            FlexibleTokenController.withdrawTokens.assert_called_with(
                self.initial_connector_token, to, amount)

        self.score._is_active.reset_mock()
        self.score._is_active.return_value = False
        self.score._connectors[self.initial_connector_token].is_set.set(False)
        with patch_property(IconScoreBase, 'msg', Message(self.owner)):
            self.score.withdrawTokens(self.initial_connector_token, to, amount)
            self.score._is_active.assert_called()
            FlexibleTokenController.withdrawTokens.assert_called_with(
                self.initial_connector_token, to, amount)

    def test_withdrawTokens_failure(self):
        to = Address.from_string("hx" + os.urandom(20).hex())
        amount = 100

        self.score._is_active.return_value = True
        self.score._connectors[self.initial_connector_token].is_set.set(True)
        with patch_property(IconScoreBase, 'msg', Message(self.owner)):
            self.assertRaises(RevertException, self.score.withdrawTokens,
                              self.initial_connector_token, to, amount)
            self.score._is_active.assert_called()

    def test_getConnectorTokenCount(self):
        size_by_db = len(self.score._connector_tokens)
        self.assertEqual(size_by_db, self.score.getConnectorTokenCount())

    def test_getConversionFee(self):
        fee_by_db = self.score._conversion_fee.get()
        self.assertEqual(fee_by_db, self.score.getConversionFee())

    def test_getConnector(self):
        result_dict = self.score.getConnector(self.initial_connector_token)
        self.assertIn('virtualBalance', result_dict)
        self.assertIn('weight', result_dict)
        self.assertIn('isVirtualBalanceEnabled', result_dict)
        self.assertIn('isPurchaseEnabled', result_dict)
        self.assertIn('isSet', result_dict)

        result_dict = self.score.getConnector(ZERO_SCORE_ADDRESS)
        self.assertNotIn('virtualBalance', result_dict)
        self.assertNotIn('weight', result_dict)
        self.assertNotIn('isVirtualBalanceEnabled', result_dict)
        self.assertNotIn('isPurchaseEnabled', result_dict)
        self.assertNotIn('isSet', result_dict)

    def test_getConnectorBalance(self):
        balance = 100

        with MultiPatch([
                patch_property(IconScoreBase, 'msg', Message(self.owner)),
                patch.object(InternalCall,
                             'other_external_call',
                             return_value=balance)
        ]):
            result_balance = self.score.getConnectorBalance(
                self.initial_connector_token)

            assert_inter_call(self, self.score.address,
                              self.initial_connector_token, 'balanceOf',
                              [self.score.address])

            self.assertEqual(result_balance, balance)

    def test_getReturn(self):
        amount = 1000

        self.score.get_purchase_return = Mock()
        self.score.get_sale_return = Mock()
        self.score.get_cross_connector_return = Mock()

        from_token = Address.from_string("cx" + os.urandom(20).hex())
        to_token = Address.from_string("cx" + os.urandom(20).hex())

        with patch.object(IconScoreBase, 'msg', Message(self.owner)):
            self.score.getReturn(from_token, to_token, amount)
            self.score.get_cross_connector_return.assert_called_with(
                from_token, to_token, amount)

        from_token = self.token
        to_token = Address.from_string("cx" + os.urandom(20).hex())
        with patch.object(IconScoreBase, 'msg', Message(self.owner)):
            self.score.getReturn(from_token, to_token, amount)
            self.score.get_sale_return.assert_called_with(to_token, amount)

        from_token = Address.from_string("cx" + os.urandom(20).hex())
        to_token = self.token
        with patch.object(IconScoreBase, 'msg', Message(self.owner)):
            self.score.getReturn(from_token, to_token, amount)
            self.score.get_purchase_return.assert_called_with(
                from_token, amount)

    def test_getPurchaseReturn(self):
        amount = 1000

        self.score.getConnectorBalance = Mock(return_value=10000)

        with MultiPatch([
                patch_property(IconScoreBase, 'msg', Message(self.owner)),
                patch.object(InternalCall,
                             'other_external_call',
                             return_value=10000),
                patch.object(FixedMapFormula,
                             'calculate_purchase_return',
                             return_value=1000)
        ]):
            result = self.score.get_purchase_return(
                self.initial_connector_token, amount)
            self.assertEqual(1000, result['amount'])
            self.assertEqual(0, result['fee'])

        # sets the fee to 1%
        self.score._max_conversion_fee.set(1000000)
        self.score._conversion_fee.set(10000)

        with MultiPatch([
                patch_property(IconScoreBase, 'msg', Message(self.owner)),
                patch.object(InternalCall,
                             'other_external_call',
                             return_value=10000),
                patch.object(FixedMapFormula,
                             'calculate_purchase_return',
                             return_value=1000)
        ]):
            result = self.score.get_purchase_return(
                self.initial_connector_token, amount)
            self.assertEqual(990, result['amount'])
            self.assertEqual(10, result['fee'])

    def test_getSaleReturn(self):
        amount = 1000

        self.score.getConnectorBalance = Mock(return_value=10000)

        with MultiPatch([
                patch_property(IconScoreBase, 'msg', Message(self.owner)),
                patch.object(InternalCall,
                             'other_external_call',
                             return_value=10000),
                patch.object(FixedMapFormula,
                             'calculate_sale_return',
                             return_value=1000)
        ]):
            result = self.score.get_sale_return(self.initial_connector_token,
                                                amount)
            self.assertEqual(1000, result['amount'])
            self.assertEqual(0, result['fee'])

        # sets the fee to 1%
        self.score._max_conversion_fee.set(1000000)
        self.score._conversion_fee.set(10000)

        with MultiPatch([
                patch_property(IconScoreBase, 'msg', Message(self.owner)),
                patch.object(InternalCall,
                             'other_external_call',
                             return_value=10000),
                patch.object(FixedMapFormula,
                             'calculate_sale_return',
                             return_value=1000)
        ]):
            result = self.score.get_sale_return(self.initial_connector_token,
                                                amount)
            self.assertEqual(990, result['amount'])
            self.assertEqual(10, result['fee'])

    def test_getCrossConnectorReturn(self):
        connector_token2 = Address.from_string("cx" + os.urandom(20).hex())
        connector_token2_weight = 500000
        self.score.addConnector(connector_token2, connector_token2_weight,
                                False)

        amount = 1000

        self.score.getConnectorBalance = Mock(return_value=10000)

        with MultiPatch([
                patch_property(IconScoreBase, 'msg', Message(self.owner)),
                patch.object(FixedMapFormula,
                             'calculate_cross_connector_return',
                             return_value=1000)
        ]):
            result = self.score.get_cross_connector_return(
                self.initial_connector_token, connector_token2, amount)
            self.assertEqual(1000, result['amount'])
            self.assertEqual(0, result['fee'])

        # sets the fee to 1%
        self.score._max_conversion_fee.set(1000000)
        self.score._conversion_fee.set(10000)

        with MultiPatch([
                patch_property(IconScoreBase, 'msg', Message(self.owner)),
                patch.object(FixedMapFormula,
                             'calculate_cross_connector_return',
                             return_value=1000)
        ]):
            result = self.score.get_cross_connector_return(
                self.initial_connector_token, connector_token2, amount)
            self.assertEqual(980, result['amount'])
            self.assertEqual(20, result['fee'])
Example #16
0
class TestNetwork(unittest.TestCase):
    def setUp(self):
        self.patcher = ScorePatcher(Network)
        self.patcher.start()

        self.score_address = Address.from_string("cx" + "1" * 40)
        self.network_score = Network(create_db(self.score_address))

        self.network_owner = Address.from_string("hx" + "1" * 40)

        self.flexible_token_address_list = [
            Address.from_string("cx" + str(i) * 40) for i in range(0, 3)
        ]
        self.connector_token_list = [
            Address.from_string("cx" + str(i) * 40) for i in range(3, 7)
        ]
        self.icx_token = Address.from_string("cx" + "7" * 40)

        with patch_property(IconScoreBase, 'msg', Message(self.network_owner)):
            self.network_score.on_install()
            TokenHolder.on_install.assert_called_with(self.network_score)

    def tearDown(self):
        self.patcher.stop()

    def test_check_valid_path(self):
        # success case: input the valid path
        path = [
            self.connector_token_list[0], self.flexible_token_address_list[0],
            self.connector_token_list[1]
        ]
        self.network_score._require_valid_path(path)

        # failure case: input path whose length under 3
        invalid_path = [
            self.connector_token_list[0], self.flexible_token_address_list[0]
        ]
        self.assertRaises(RevertException,
                          self.network_score._require_valid_path, invalid_path)

        # failure case: input path whose length is more than 21
        random = SystemRandom()
        # use random data to avoid same address is made
        invalid_path = [
            Address.from_string("cx" + str(random.randrange(10)) +
                                str(random.randrange(10)) * 39)
            in range(0, 22)
        ]
        self.assertRaises(RevertException,
                          self.network_score._require_valid_path, invalid_path)

        # failure case: input path whose length is even
        invalid_path = [
            self.connector_token_list[0], self.flexible_token_address_list[0],
            self.connector_token_list[1], self.flexible_token_address_list[1]
        ]
        self.assertRaises(RevertException,
                          self.network_score._require_valid_path, invalid_path)

        # failure case: input path which has the same flexible token address in it
        invalid_path = [
            self.connector_token_list[0], self.flexible_token_address_list[0],
            self.connector_token_list[1], self.flexible_token_address_list[0],
            self.connector_token_list[1]
        ]
        self.assertRaises(RevertException,
                          self.network_score._require_valid_path, invalid_path)

        # success case: input path which has the same flexible token address in the 'from' or 'to' position in it
        # path: [connector0 - flexible token0 - flexible token1 - flexible token2, flexible token0]
        path = [
            self.connector_token_list[0], self.flexible_token_address_list[0],
            self.flexible_token_address_list[1],
            self.flexible_token_address_list[2],
            self.flexible_token_address_list[0]
        ]
        self.network_score._require_valid_path(path)

        # success case: input path which has the same flexible tokens that only exist in 'from' or 'to' in it
        path = [
            self.flexible_token_address_list[0],
            self.flexible_token_address_list[1], self.connector_token_list[0],
            self.flexible_token_address_list[2],
            self.flexible_token_address_list[0]
        ]
        self.network_score._require_valid_path(path)

    def test_check_and_convert_bytes_data(self):
        # failure case: input invalid JSON format data
        invalid_json_format = dict()
        invalid_json_format["path"] = "{0},{1},{2}".format(
            str(self.connector_token_list[0]),
            str(self.flexible_token_address_list[0]),
            str(self.connector_token_list[1]))
        invalid_json_format["for"] = str(self.network_owner)
        invalid_json_format["minReturn"] = 10
        stringed_invalid_json_format = "[" + json.dumps(
            invalid_json_format) + "}"
        encoded_invalid_json_format = stringed_invalid_json_format.encode(
            encoding="utf-8")
        self.assertRaises(RevertException,
                          self.network_score._convert_bytes_data,
                          encoded_invalid_json_format, self.network_owner)

        # failure case: input data which is not decoded to utf-8
        valid_json_format = dict()
        valid_json_format["path"] = "{0},{1},{2}".format(
            str(self.connector_token_list[0]),
            str(self.flexible_token_address_list[0]),
            str(self.connector_token_list[1]))

        valid_json_format["for"] = str(self.network_owner)
        valid_json_format["minReturn"] = 10
        stringed_valid_json_format = json.dumps(valid_json_format)
        cp037_encoded_valid_json_format = stringed_valid_json_format.encode(
            encoding='cp037')
        self.assertRaises(RevertException,
                          self.network_score._convert_bytes_data,
                          cp037_encoded_valid_json_format, self.network_owner)

        # failure case: input invalid path data (associated with '/')
        json_format = dict()
        json_format["path"] = "{0}/{1}/{2}".format(
            str(self.connector_token_list[0]),
            str(self.flexible_token_address_list[0]),
            str(self.connector_token_list[1]))
        json_format["for"] = str(self.network_owner)
        json_format["minReturn"] = 10
        stringed_valid_json_format = json.dumps(json_format)
        encoded_valid_json_format = stringed_valid_json_format.encode(
            encoding='utf-8')
        self.assertRaises(InvalidParamsException,
                          self.network_score._convert_bytes_data,
                          encoded_valid_json_format, self.network_owner)

        # failure case: input invalid path data ( has an invalid address as an element )
        json_format = dict()
        json_format["path"] = "{0},{1},{2}".format(
            str(self.connector_token_list[0]), str("invalid_address"),
            str(self.connector_token_list[1]))
        json_format["for"] = str(self.network_owner)
        json_format["minReturn"] = 10
        stringed_valid_json_format = json.dumps(json_format)
        encoded_valid_json_format = stringed_valid_json_format.encode(
            encoding='utf-8')
        self.assertRaises(InvalidParamsException,
                          self.network_score._convert_bytes_data,
                          encoded_valid_json_format, self.network_owner)

        # failure case: input string type minReturn
        json_format = dict()
        json_format["path"] = "{0},{1},{2}".format(
            str(self.connector_token_list[0]),
            str(self.flexible_token_address_list[0]),
            str(self.connector_token_list[1]))
        json_format["for"] = str(self.network_owner)
        json_format["minReturn"] = "10"
        stringed_valid_json_format = json.dumps(json_format)
        encoded_valid_json_format = stringed_valid_json_format.encode(
            encoding='utf-8')
        self.assertRaises(RevertException,
                          self.network_score._convert_bytes_data,
                          encoded_valid_json_format, self.network_owner)

        # success case: "for" key is not in the data
        # ( returned data should have token sender address as a "for" key's value )
        token_sender = Address.from_string("hx" + "a" * 40)
        json_format = dict()
        json_format["path"] = "{0},{1},{2}".format(
            str(self.connector_token_list[0]),
            str(self.flexible_token_address_list[0]),
            str(self.connector_token_list[1]))
        json_format["minReturn"] = 10
        stringed_valid_json_format = json.dumps(json_format)
        encoded_valid_json_format = stringed_valid_json_format.encode(
            encoding='utf-8')
        result_data = self.network_score._convert_bytes_data(
            encoded_valid_json_format, token_sender)
        self.assertEqual(token_sender, result_data["for"])

        # success case: "for" key is exist but value is None
        # ( returned dict data should have token sender address as a "for" key's value )
        json_format["for"] = None
        stringed_valid_json_format = json.dumps(json_format)
        encoded_valid_json_format = stringed_valid_json_format.encode(
            encoding='utf-8')
        result_data = self.network_score._convert_bytes_data(
            encoded_valid_json_format, token_sender)
        self.assertEqual(token_sender, result_data["for"])

        # success case: input data which has "for" key and it's value
        for_address = Address.from_string("hx" + "b" * 40)
        json_format["for"] = str(for_address)
        stringed_valid_json_format = json.dumps(json_format)
        encoded_valid_json_format = stringed_valid_json_format.encode(
            encoding='utf-8')
        result_data = self.network_score._convert_bytes_data(
            encoded_valid_json_format, token_sender)
        self.assertEqual(for_address, result_data["for"])

    def test_convertFor(self):
        icx_amount = 10

        # failure case: input wrong path format
        min_return = 10
        for_address = Address.from_string("hx" + "a" * 40)
        invalid_path = "{0}/{1}/{2}".format(
            str(self.icx_token), str(self.flexible_token_address_list[0]),
            str(self.connector_token_list[0]))
        with patch_property(IconScoreBase, 'msg',
                            Message(self.network_owner, value=icx_amount)):
            self.assertRaises(InvalidParamsException,
                              self.network_score.convertFor, invalid_path,
                              min_return, for_address)

        # failure case: input path whose has invalid address as an element
        min_return = 10
        for_address = Address.from_string("hx" + "a" * 40)
        invalid_path = "{0},{1},{2}".format(str(self.icx_token),
                                            str("invalid_address"),
                                            str(self.connector_token_list[1]))
        with patch_property(IconScoreBase, 'msg',
                            Message(self.network_owner, value=icx_amount)):
            self.assertRaises(InvalidParamsException,
                              self.network_score.convertFor, invalid_path,
                              min_return, for_address)

        # failure case: input min return less than 0
        invalid_min_return = -1
        for_address = Address.from_string("hx" + "a" * 40)
        path = "{0},{1},{2}".format(str(self.icx_token),
                                    str(self.flexible_token_address_list[0]),
                                    str(self.connector_token_list[0]))
        with patch_property(IconScoreBase, 'msg',
                            Message(self.network_owner, value=icx_amount)):
            self.assertRaises(RevertException, self.network_score.convertFor,
                              path, invalid_min_return, for_address)

        # failure case: input path whose first address is not Icx token address
        min_return = 10
        for_address = Address.from_string("hx" + "a" * 40)
        invalid_path = "{0},{1},{2}".format(
            str(self.connector_token_list[0]),
            str(self.flexible_token_address_list[0]),
            str(self.connector_token_list[1]))
        with patch_property(IconScoreBase, 'msg',
                            Message(self.network_owner, value=icx_amount)):
            self.assertRaises(RevertException, self.network_score.convertFor,
                              invalid_path, min_return, for_address)

        # success case: input valid path
        min_return = 10
        for_address = Address.from_string("hx" + "a" * 40)
        path = "{0},{1},{2}".format(str(self.icx_token),
                                    str(self.flexible_token_address_list[0]),
                                    str(self.connector_token_list[1]))
        self.network_score._icx_tokens[self.icx_token] = True
        with MultiPatch([
                patch_property(IconScoreBase, 'msg',
                               Message(self.network_owner, value=icx_amount)),
                patch.object(Network, '_convert_for_internal')
        ]):
            self.network_score.convertFor(path, min_return, for_address)
            self.network_score.icx.transfer.assert_called_with(
                self.icx_token, icx_amount)
            converted_path = [
                self.icx_token, self.flexible_token_address_list[0],
                self.connector_token_list[1]
            ]
            self.network_score._convert_for_internal.assert_called_with(
                converted_path, icx_amount, min_return, for_address)

    def test_tokenFallback(self):
        # success case: input 'conversionResult' to _data ( convert_for_internal should not be called )
        from_address = Address.from_string("hx" + "a" * 40)
        for_address = Address.from_string("hx" + "b" * 40)
        value = 10
        min_return = 5
        path = "{0},{1},{2}".format(str(self.connector_token_list[0]),
                                    str(self.flexible_token_address_list[0]),
                                    str(self.connector_token_list[1]))
        converted_path = [
            Address.from_string(address) for address in path.split(",")
        ]

        data = dict()
        data["path"] = path
        data["minReturn"] = min_return
        data["for"] = str(for_address)
        stringed_data = json.dumps(data)
        decoded_data = stringed_data.encode(encoding='utf-8')

        with MultiPatch([
                patch_property(IconScoreBase, 'msg',
                               Message(self.connector_token_list[0])),
                patch.object(Network, '_convert_for_internal')
        ]):
            self.network_score.tokenFallback(from_address, value,
                                             b'conversionResult')
            self.network_score._convert_for_internal.assert_not_called()

        # failure case: input None data to the _data
        with MultiPatch([
                patch_property(IconScoreBase, 'msg',
                               Message(self.connector_token_list[0])),
                patch.object(Network, '_convert_for_internal')
        ]):
            self.assertRaises(RevertException,
                              self.network_score.tokenFallback, from_address,
                              value, b'None')

            self.assertRaises(RevertException,
                              self.network_score.tokenFallback, from_address,
                              value, None)

        # failure case: msg.sender is not equal to path[0] ( should be equal )
        msg_sender = Address.from_string("cx" + "c" * 40)
        with MultiPatch([
                patch_property(IconScoreBase, 'msg', Message(msg_sender)),
                patch.object(Network, '_convert_for_internal')
        ]):
            self.assertRaises(RevertException,
                              self.network_score.tokenFallback, from_address,
                              value, decoded_data)

        # success case: input valid data
        with MultiPatch([
                patch_property(IconScoreBase, 'msg',
                               Message(self.connector_token_list[0])),
                patch.object(Network, '_convert_for_internal'),
                patch.object(Network, '_require_valid_path'),
                patch('contracts.network.network.require_positive_value'),
                patch('contracts.network.network.require_valid_address'),
        ]) as mocks:
            self.network_score.tokenFallback(from_address, value, decoded_data)
            mocks[3].assert_called()  # patched mock of require_positive_value
            mocks[4].assert_called()  # patched mock of require_valid_address
            self.network_score._require_valid_path.assert_called_with(
                converted_path)
            self.network_score._convert_for_internal. \
                assert_called_with(converted_path, value, min_return, for_address)

    def test_convert_for_internal(self):
        min_return = 10
        amount_to_convert = 5
        convert_result_amount = 10
        for_address = Address.from_string("hx" + "a" * 40)

        icx_token_score_interface = \
            self.network_score.create_interface_score(self.icx_token, ProxyScore(ABCIcxToken))
        icx_token_score_interface.withdrawTo = PropertyMock()

        irc_token_score_interface = \
            self.network_score.create_interface_score(self.connector_token_list[0], ProxyScore(ABCIRCToken))
        irc_token_score_interface.transfer = PropertyMock()

        # success case: finally converted token is Icx token ( Icx token SCORE's 'withdrawTo' method should be called )
        converted_path = [
            self.connector_token_list[0], self.flexible_token_address_list[0],
            self.icx_token
        ]
        # '_convert_by_path' method returns 'to' token Address, and converted amount
        with MultiPatch([
                patch_property(IconScoreBase, 'msg',
                               Message(self.network_owner)),
                patch.object(Network,
                             '_convert_by_path',
                             return_value=(self.icx_token,
                                           convert_result_amount)),
                patch.object(Network,
                             'create_interface_score',
                             return_value=icx_token_score_interface)
        ]):
            # register icx_token
            self.network_score._icx_tokens[self.icx_token] = True

            self.network_score._convert_for_internal(converted_path,
                                                     amount_to_convert,
                                                     min_return, for_address)
            icx_token_score_interface.withdrawTo.assert_called_with(
                convert_result_amount, for_address)

        # success case: finally converted token is irc token ( token SCORE's 'transfer' method should be called )
        converted_path = [
            self.icx_token, self.flexible_token_address_list[0],
            self.connector_token_list[1]
        ]
        # '_convert_by_path' method returns 'to' token Address, and converted amount
        with MultiPatch([
                patch_property(IconScoreBase, 'msg',
                               Message(self.network_owner)),
                patch.object(Network,
                             '_convert_by_path',
                             return_value=(self.connector_token_list[1],
                                           convert_result_amount)),
                patch.object(Network,
                             'create_interface_score',
                             return_value=irc_token_score_interface)
        ]):
            self.network_score._convert_for_internal(converted_path,
                                                     amount_to_convert,
                                                     min_return, for_address)
            irc_token_score_interface.transfer.assert_called_with(
                for_address, convert_result_amount, b'None')

    def test_convert_by_path(self):
        # success case: check all other SCORE's methods called correctly
        # in this path, 'flexible token' is 1, 3, 'from token' is 0, 2, 'to token' is 2, 4 ( index )
        converted_path = [
            self.connector_token_list[0], self.flexible_token_address_list[0],
            self.connector_token_list[1], self.flexible_token_address_list[1],
            self.connector_token_list[2]
        ]
        for_address = Address.from_string("hx" + "a" * 40)

        # this method substitutes 'create_interface_score' method of IconScoreBase
        def create_interface_score_mock(token_address, interface_score):
            if interface_score.__name__ == 'ProxyScore(ABCFlexibleToken)':
                # add getOwner method to the token address instance
                # this token instance operates as a converter interface score
                token_address.getOwner = Mock(
                    return_value="{0} converter address".format(token_address))
            else:
                # add 'transfer' and 'balanceOf' methods to token address
                # this token instance operates as an IRC token interface score
                token_address.transfer = PropertyMock()
                token_address.balanceOf = PropertyMock(return_value=0)
            return token_address

        with patch_property(IconScoreBase, 'msg', Message(self.network_owner)):
            # the amount is set to 0. in this unit test, do not check the exact return value
            # just check whether if specific methods have been called or not
            amount = 0
            min_return = 10

            self.network_score.create_interface_score = create_interface_score_mock
            actual_to_token, actual_amount = self.network_score.\
                _convert_by_path(converted_path, amount, min_return, for_address)
            self.assertEqual(converted_path[-1], actual_to_token)
            self.assertEqual(amount, actual_amount)
            # check flexible token's 'getOwner' method have been called ( flexible token's index is 1, 3 )
            for i, flexible_token in enumerate(converted_path):
                if i % 2 == 1:
                    flexible_token.getOwner.assert_called_once()

            # check 'from' token's 'transfer' method have been called ( from token's index is 0, 2 )
            for i, from_token in enumerate(converted_path):
                if i % 2 == 0 and i != len(converted_path) - 1:
                    data = dict()
                    data["toToken"] = str(converted_path[i + 2])
                    # in the last converting, minReturn should be user inputted data (10)
                    data["minReturn"] = min_return if i == len(
                        converted_path) - 3 else 1
                    encoded_data = json_dumps(data).encode()
                    from_token.transfer.assert_called_once_with(
                        "{0} converter address".format(converted_path[i + 1]),
                        amount, encoded_data)

            # check 'to' token's getBalanceOf method have been called ( to token's index is 2, 4 )
            for i, from_token in enumerate(converted_path):
                if i % 2 == 0 and i != 0:
                    # 'balanceOf' method should be called twice
                    from_token.balanceOf.assert_called()

    def test_getExpectedReturnByPath(self):
        # failure case: input invalid path data ( associated with '/' )
        amount = 10
        invalid_path = "{0}/{1}/{2}".format(
            str(self.connector_token_list[0]),
            str(self.flexible_token_address_list[0]),
            str(self.connector_token_list[1]))
        self.assertRaises(InvalidParamsException,
                          self.network_score.getExpectedReturnByPath,
                          invalid_path, amount)

        # failure case: input invalid path ( has a invalid address as an element )
        amount = 10
        invalid_path = "{0},{1},{2}".format(str(self.connector_token_list[0]),
                                            str("invalid_address"),
                                            str(self.connector_token_list[1]))
        self.assertRaises(InvalidParamsException,
                          self.network_score.getExpectedReturnByPath,
                          invalid_path, amount)

        # failure case: amount is less than 0
        invalid_amount = -1
        path = "{0},{1},{2}".format(str(self.connector_token_list[0]),
                                    str(self.flexible_token_address_list[0]),
                                    str(self.connector_token_list[1]))
        self.assertRaises(RevertException,
                          self.network_score.getExpectedReturnByPath, path,
                          invalid_amount)

        # success case: input valid path and amount
        converted_path = [
            self.connector_token_list[0], self.flexible_token_address_list[0],
            self.connector_token_list[1], self.flexible_token_address_list[1],
            self.connector_token_list[2]
        ]
        stringed_path = ",".join([str(address) for address in converted_path])
        amount = 10

        def create_interface_score_mock(token_address, interface_score):
            if interface_score.__name__ == "ProxyScore(ABCFlexibleToken)":
                token_address.getOwner = Mock(return_value=token_address)
                return token_address

            # return converted_path's address instance ( to verify getReturn called or not )
            for index, address in enumerate(converted_path):
                if token_address == address:
                    address.getReturn = Mock(return_value={
                        "amount": amount + index,
                        "fee": 0
                    })
                    return address

        with patch_property(IconScoreBase, 'msg', Message(self.network_owner)):
            self.network_score.create_interface_score = create_interface_score_mock

            actual_amount = self.network_score.getExpectedReturnByPath(
                stringed_path, amount)
            expected_final_amount = amount + len(converted_path) - 2
            # finally converted amount should be initial amount(10) + last flexible token index number(3)
            self.assertEqual(expected_final_amount, actual_amount)
            expected_intermediate_amount = 10
            for i in range(1, len(converted_path), 2):
                converted_path[i].getReturn.\
                    assert_called_with(converted_path[i-1], converted_path[i+1], expected_intermediate_amount)
                expected_intermediate_amount += i