From 3506d267f192548aeb6366e6ce5f2166ec77649d Mon Sep 17 00:00:00 2001 From: Reto Trinkler Date: Fri, 29 Jan 2016 15:02:16 +0100 Subject: [PATCH 1/5] exchange contract --- .../native/exchange/exchange_contract.py | 210 ++++++++++++++++++ .../native/exchange/test_exchange_contract.py | 196 ++++++++++++++++ 2 files changed, 406 insertions(+) create mode 100755 hydrachain/examples/native/exchange/exchange_contract.py create mode 100755 hydrachain/examples/native/exchange/test_exchange_contract.py diff --git a/hydrachain/examples/native/exchange/exchange_contract.py b/hydrachain/examples/native/exchange/exchange_contract.py new file mode 100755 index 0000000..d8aef08 --- /dev/null +++ b/hydrachain/examples/native/exchange/exchange_contract.py @@ -0,0 +1,210 @@ +import ethereum.utils as utils +import ethereum.slogging as slogging +import hydrachain.native_contracts as nc +from hydrachain.nc_utils import isaddress, STATUS, FORBIDDEN, OK, INSUFFICIENTFUNDS +from hydrachain.examples.native.fungible.fungible_contract import Fungible +log = slogging.get_logger('contracts.exchange') + + +class Partial(nc.ABIEvent): + + "Triggerd when tokens are traded on the Exchange" + args = [dict(name='_currencyPair', type='bytes32', indexed=True), + dict(name='_seller', type='address', indexed=True), + dict(name='_offerValue', type='uint256', indexed=False), + dict(name='_buyer', type='address', indexed=True), + dict(name='_wantValue', type='uint256', indexed=False)] + + +class Traded(nc.ABIEvent): + + "Triggerd when tokens are traded on the Exchange" + args = [dict(name='_currencyPair', type='bytes32', indexed=True), + dict(name='_seller', type='address', indexed=True), + dict(name='_offerValue', type='uint256', indexed=False), + dict(name='_buyer', type='address', indexed=True), + dict(name='_wantValue', type='uint256', indexed=False)] + + +class Exchange(nc.NativeContract): + + """ + Exchange to trade token based on the Ethereum Token standard + https://github.com/ethereum/wiki/wiki/Standardized_Contract_APIs + """ + + address = utils.int_to_addr(4000) + events = [Partial, Traded] + + # Struct + order_creator = nc.List('address') + order_offerCurrency = nc.List('address') + order_offerValue = nc.List('uint256') + order_wantCurrency = nc.List('address') + order_wantValue = nc.List('uint256') + order_id = nc.Scalar('uint256') + + owner = nc.Scalar('address') + nextOrderId = nc.Scalar('uint256') + + def init(ctx, returns=STATUS): + log.DEV('In Exchange.init') + if isaddress(ctx.owner): + return FORBIDDEN + ctx.owner = ctx.tx_origin + # Initialize + ctx.nextOrderId = 1 + return OK + + @nc.constant + def get_nextOrderId(ctx, returns='uint256'): + return ctx.nextOrderId + + @nc.constant + def get_orderCreator(ctx, _offerId='uint256', returns='address'): + return ctx.order_creator[_offerId - 1] + + def place_order(ctx, _offerCurrency='address', _offerValue='uint256', _wantCurrency='address', _wantValue='uint256', returns='uint256'): + offer_id = nc.Scalar('uint256') + offer_id = 0 + # Deposit, coins at address: _offerCurrency + # from: Msg Sender + # to: Exchange + # Value: _offerValue + r = ctx.call_abi( + _offerCurrency, + coinc.Coin.transferFrom, + ctx.msg_sender, + ctx.address, + _offerValue) + if r is OK: + # Store Offer + offer_id = ctx.nextOrderId + ctx.nextOrderId += 1 + ctx.order_creator.append(ctx.msg_sender) + ctx.order_offerCurrency.append(_offerCurrency) + ctx.order_offerValue.append(_offerValue) + ctx.order_wantCurrency.append(_wantCurrency) + ctx.order_wantValue.append(_wantValue) + return offer_id + elif r is INSUFFICIENTFUNDS: + return offer_id + else: + raise Exception() + + def claim_order_partial(ctx, _offerId='uint256', _offerValue='uint256', _wantValue='uint256', returns=STATUS): + # Check for the right rate + if _offerValue >= ctx.order_offerValue[_offerId - 1] or _wantValue >= ctx.order_wantValue[_offerId - 1]: + return FORBIDDEN + # Assert that: offerValue/order_offerValue == wantValue/order_wantValue + if _offerValue * ctx.order_wantValue[_offerId - 1] != _wantValue * ctx.order_offerValue[_offerId - 1]: + return FORBIDDEN + + # Send, coins at address: _wantCurrency + # from: Msg Sender + # to: Order Creator + # Value: _wantValue + r = ctx.call_abi( + ctx.order_wantCurrency[_offerId - 1], + coinc.Coin.transferFrom, + ctx.msg_sender, + ctx.order_creator[_offerId - 1], + _wantValue) + + if r is OK: + # Send, coins at address: _offerValue + # from: Exchange + # to: Msg Sender + # Value: _offerValue + # i.e. execute 2nd side of the trade + r1 = ctx.call_abi( + ctx.order_offerCurrency[_offerId - 1], + coinc.Coin.transfer, + ctx.msg_sender, + _offerValue) + # TODO CurrencyPair + # currencyPair = (self.orders[_offerId].offerCurrency / 2**32) * 2**128 + (self.orders[_offerId].wantCurrency / 2**32) + # currencyPair = ctx.order_offerCurrency[_offerId - 1] + ctx.order_wantCurrency[_offerId - 1] + currencyPair = "" + ctx.Partial( + currencyPair, + ctx.order_creator[_offerId - 1], + _offerValue, + ctx.msg_sender, + _wantValue) + # Update order as partially matched + ctx.order_offerValue[_offerId - 1] -= _offerValue + ctx.order_wantValue[_offerId - 1] -= _wantValue + return OK + elif r is INSUFFICIENTFUNDS: + return INSUFFICIENTFUNDS + else: + raise Exception() + + + def claim_order(ctx, _offerId='uint256', returns=STATUS): + # Send, coins at address: _wantCurrency + # from: Msg Sender + # to: Order Creator + # Value: _wantValue + r = ctx.call_abi( + ctx.order_wantCurrency[_offerId - 1], + coinc.Coin.transferFrom, + ctx.msg_sender, + ctx.order_creator[_offerId - 1], + ctx.order_wantValue[_offerId - 1]) + + if r is OK: + # Send, coins at address: _offerValue + # from: Exchange + # to: Msg Sender + # Value: _offerValue + # i.e. execute 2nd side of the trade + r1 = ctx.call_abi( + ctx.order_offerCurrency[_offerId - 1], + coinc.Coin.transfer, + ctx.msg_sender, + ctx.order_offerValue[_offerId - 1]) + # TODO CurrencyPair + # currencyPair = (self.orders[_offerId].offerCurrency / 2**32) * 2**128 + (self.orders[_offerId].wantCurrency / 2**32) + # currencyPair = ctx.order_offerCurrency[_offerId - 1] + ctx.order_wantCurrency[_offerId - 1] + currencyPair = "" + ctx.Traded( + currencyPair, + ctx.order_creator[_offerId - 1], + ctx.order_offerValue[_offerId - 1], + ctx.msg_sender, + ctx.order_wantValue[_offerId - 1]) + # Update order as executed + ctx.order_creator[_offerId - 1] = '\0' * 20 + ctx.order_offerCurrency[_offerId - 1] = '\0' * 20 + ctx.order_offerValue[_offerId - 1] = 0 + ctx.order_wantCurrency[_offerId - 1] = '\0' * 20 + ctx.order_wantValue[_offerId - 1] = 0 + return OK + elif r is INSUFFICIENTFUNDS: + return INSUFFICIENTFUNDS + else: + raise Exception() + + + def delete_order(ctx, _offerId='uint256', returns=STATUS): + # Only creator can delete its order + if ctx.msg_sender != ctx.order_creator[_offerId - 1]: + return FORBIDDEN + # Return, coins at address: _offerCurrency + # from: Exchange + # to: Order Creator + # Value: _offerValue + r = ctx.call_abi( + ctx.order_offerCurrency[_offerId - 1], + coinc.Coin.transfer, + ctx.order_creator[_offerId - 1], + ctx.order_offerValue[_offerId - 1]) + # Update order as deleted + ctx.order_creator[_offerId - 1] = '\0' * 20 + ctx.order_offerCurrency[_offerId - 1] = '\0' * 20 + ctx.order_offerValue[_offerId - 1] = 0 + ctx.order_wantCurrency[_offerId - 1] = '\0' * 20 + ctx.order_wantValue[_offerId - 1] = 0 + return OK diff --git a/hydrachain/examples/native/exchange/test_exchange_contract.py b/hydrachain/examples/native/exchange/test_exchange_contract.py new file mode 100755 index 0000000..a2760d7 --- /dev/null +++ b/hydrachain/examples/native/exchange/test_exchange_contract.py @@ -0,0 +1,196 @@ +from ethereum import tester +from ethereum import utils +from hydrachain import native_contracts as nc +import logging +import pytest +import lib.coin_contract as coinc +import exchange_contract as exchangec + +logging.NOTSET = logging.ERROR + +class Coin_Alice(coinc.Coin): + + address = utils.int_to_addr(5001) + events = [coinc.Transfer, coinc.Approval] + + owner = nc.Scalar('address') + supply = nc.Scalar('uint256') + # Mapping addresses with balances + accounts = nc.IterableDict('uint256') + allowances = nc.Dict(nc.Dict('uint256')) + + +class Coin_Bob(coinc.Coin): + + address = utils.int_to_addr(5002) + events = [coinc.Transfer, coinc.Approval] + + owner = nc.Scalar('address') + supply = nc.Scalar('uint256') + # Mapping addresses with balances + accounts = nc.IterableDict('uint256') + allowances = nc.Dict(nc.Dict('uint256')) + + +@pytest.fixture(scope="module") +def register_coins(request): + nc.registry.register(Coin_Alice) + nc.registry.register(Coin_Bob) + def fin(): + print ("teardown ") + nc.registry.unregister(Coin_Alice) + nc.registry.unregister(Coin_Bob) + request.addfinalizer(fin) + return None + +@pytest.fixture(scope="module") +def register_exchange(request): + nc.registry.register(exchangec.Exchange) + def fin(): + print ("teardown ") + nc.registry.unregister(exchangec.Exchange) + request.addfinalizer(fin) + return None + + +class User(object): + + def __init__(self, state, address, key): + self.state = state + self.address = address + self.key = key + + def add_proxy(self, name, address): + setattr(self, name, nc.tester_nac(self.state, self.key, address)) + + +def test_exchange_template(register_coins, register_exchange): + + state = tester.state() + logs = [] + exchange = User(state, tester.a0, tester.k0) + coin = User(state, tester.a1, tester.k1) + alice = User(state, tester.a2, tester.k2) + bob = User(state, tester.a3, tester.k3) + + # Listen to Events + nc.listen_logs(state, coinc.Transfer, callback=lambda e: logs.append(e)) + nc.listen_logs(state, exchangec.Traded, callback=lambda e: logs.append(e)) + + # Create proxy + exchange.add_proxy('ex', exchangec.Exchange.address) + + # Initalize exchange + exchange.ex.init() + assert exchange.ex.get_nextOrderId() == 1 + + # Initialize Alice as a proxy of the Exchange + alice.add_proxy('ex', exchangec.Exchange.address) + bob.add_proxy('ex', exchangec.Exchange.address) + + # Initialize Alice as a coin (=ca) creator + alice.add_proxy('ca', Coin_Alice.address) + # initalize ca with a fixed quantity of coins. + ca_total = 1000000 + assert alice.ca.init(ca_total) == 200 + assert alice.ca.balanceOf(alice.address) == ca_total + + # Initialize Bob as a coin (=cb) creator + bob.add_proxy('cb', Coin_Bob.address) + # initalize cb with a fixed quantity of coins. + cb_total = 1000000 + assert bob.cb.init(cb_total) == 200 + assert bob.cb.balanceOf(bob.address) == cb_total + + # Initialize Proxies + exchange.add_proxy('ca', Coin_Alice.address) + exchange.add_proxy('cb', Coin_Bob.address) + + # Alice does a direct Debit for Exchange address + alice.ca.approve(exchangec.Exchange.address, 1000) + + # Alice does an IPO of her coin at the exchange + # Remark, + # Price is set by the division of the two quantities. + alice_coin_quantity = 100 + bob_coin_quantity = 100 + alice_offer_id = alice.ex.placeOrder( + Coin_Alice.address, + alice_coin_quantity, + Coin_Bob.address, + bob_coin_quantity) + # Check log data of Transfer Event + assert len(logs) == 1 + l = logs[0] + assert l['event_type'] == 'Transfer' + assert l['_from'] == alice.address + assert l['_to'] == exchangec.Exchange.address + assert str(l['args']) == '[' + str(alice_coin_quantity) + ']' + assert alice_offer_id == 1 + assert alice.ex.get_orderCreator(alice_offer_id) == alice.address + assert alice.ex.get_nextOrderId() == 2 + # Check balances + assert alice.ca.balanceOf(alice.address) == ca_total - alice_coin_quantity + assert alice.ca.balanceOf(exchangec.Exchange.address) == alice_coin_quantity + + # Alice deletes her offer at the exchange + alice.ca.unapprove(exchangec.Exchange.address) + r = alice.ex.deleteOrder(alice_offer_id) + assert r == 200 + # Check log data of Transfer Event + assert len(logs) == 2 + l = logs[1] + assert l['event_type'] == 'Transfer' + assert l['_from'] == exchangec.Exchange.address + assert l['_to'] == alice.address + assert str(l['args']) == '[' + str(alice_coin_quantity) + ']' + + assert alice_offer_id == 1 + assert alice.ex.get_orderCreator(alice_offer_id) == '\0' * 20 + assert alice.ex.get_nextOrderId() == 2 + # Check balances + assert alice.ca.balanceOf(alice.address) == ca_total + assert alice.ca.balanceOf(exchangec.Exchange.address) == 0 + assert alice.ca.allowance(exchangec.Exchange.address) == 0 + + # Alice tries again, willing to lower her price. + alice.ca.approve(exchangec.Exchange.address, 1000) + alice_coin_quantity = 100 + bob_coin_quantity = 50 + alice_offer_id = alice.ex.placeOrder( + Coin_Alice.address, + alice_coin_quantity, + Coin_Bob.address, + bob_coin_quantity) + # check logs data of Transfer Event + assert alice_offer_id == 2 + assert alice.ex.get_orderCreator(alice_offer_id) == alice.address + assert alice.ex.get_nextOrderId() == 3 + + # Now Bob wants to share coins + # Bob does a direct Debit for Exchange address + bob.cb.approve(exchangec.Exchange.address, 1000) + r = bob.ex.claimOrder(alice_offer_id) + assert r == 200 + # Check balances + assert alice.ca.balanceOf(alice.address) == ca_total - alice_coin_quantity + assert alice.ca.balanceOf(exchangec.Exchange.address) == 0 + assert alice.ca.allowance(exchangec.Exchange.address) == 1000 - alice_coin_quantity + assert bob.cb.balanceOf(bob.address) == cb_total - bob_coin_quantity + assert bob.cb.balanceOf(exchangec.Exchange.address) == 0 + assert bob.cb.allowance(exchangec.Exchange.address) == 1000 - bob_coin_quantity + # Check balances - Cross Currency + assert alice.ca.balanceOf(bob.address) == alice_coin_quantity + assert bob.cb.balanceOf(alice.address) == bob_coin_quantity + # Check log data of Traded Event + #TODO adjust currencyPair + # print logs + # assert len(logs) == 6 + # l = logs[1] + # assert l['event_type'] == 'Traded' + # assert l['_currencyPair'] == '' + + + print logs + while logs and logs.pop(): + pass From 8a6760d882a59551e38f483a071afc16c314b72c Mon Sep 17 00:00:00 2001 From: Reto Trinkler Date: Fri, 29 Jan 2016 15:12:12 +0100 Subject: [PATCH 2/5] Imports --- .../native/exchange/exchange_contract.py | 2 +- .../native/exchange/test_exchange_contract.py | 56 ++----------------- 2 files changed, 5 insertions(+), 53 deletions(-) diff --git a/hydrachain/examples/native/exchange/exchange_contract.py b/hydrachain/examples/native/exchange/exchange_contract.py index d8aef08..58596b8 100755 --- a/hydrachain/examples/native/exchange/exchange_contract.py +++ b/hydrachain/examples/native/exchange/exchange_contract.py @@ -8,7 +8,7 @@ class Partial(nc.ABIEvent): - "Triggerd when tokens are traded on the Exchange" + "Triggerd when tokens are partially traded on the Exchange" args = [dict(name='_currencyPair', type='bytes32', indexed=True), dict(name='_seller', type='address', indexed=True), dict(name='_offerValue', type='uint256', indexed=False), diff --git a/hydrachain/examples/native/exchange/test_exchange_contract.py b/hydrachain/examples/native/exchange/test_exchange_contract.py index a2760d7..10f51b3 100755 --- a/hydrachain/examples/native/exchange/test_exchange_contract.py +++ b/hydrachain/examples/native/exchange/test_exchange_contract.py @@ -1,56 +1,8 @@ -from ethereum import tester -from ethereum import utils from hydrachain import native_contracts as nc -import logging -import pytest -import lib.coin_contract as coinc -import exchange_contract as exchangec - -logging.NOTSET = logging.ERROR - -class Coin_Alice(coinc.Coin): - - address = utils.int_to_addr(5001) - events = [coinc.Transfer, coinc.Approval] - - owner = nc.Scalar('address') - supply = nc.Scalar('uint256') - # Mapping addresses with balances - accounts = nc.IterableDict('uint256') - allowances = nc.Dict(nc.Dict('uint256')) - - -class Coin_Bob(coinc.Coin): - - address = utils.int_to_addr(5002) - events = [coinc.Transfer, coinc.Approval] - - owner = nc.Scalar('address') - supply = nc.Scalar('uint256') - # Mapping addresses with balances - accounts = nc.IterableDict('uint256') - allowances = nc.Dict(nc.Dict('uint256')) - - -@pytest.fixture(scope="module") -def register_coins(request): - nc.registry.register(Coin_Alice) - nc.registry.register(Coin_Bob) - def fin(): - print ("teardown ") - nc.registry.unregister(Coin_Alice) - nc.registry.unregister(Coin_Bob) - request.addfinalizer(fin) - return None - -@pytest.fixture(scope="module") -def register_exchange(request): - nc.registry.register(exchangec.Exchange) - def fin(): - print ("teardown ") - nc.registry.unregister(exchangec.Exchange) - request.addfinalizer(fin) - return None +from hydrachain.examples.native.fungible.fungible_contract import Fungible +from ethereum import tester +import ethereum.slogging as slogging +log = slogging.get_logger('test.exchange') class User(object): From f44f2f357843bb6b3e4e06a66740af42fffea53a Mon Sep 17 00:00:00 2001 From: Reto Trinkler Date: Fri, 29 Jan 2016 16:54:07 +0100 Subject: [PATCH 3/5] Exchange testing --- .../examples/native/exchange/__init__.py | 0 .../native/exchange/exchange_contract.py | 98 ++++----- .../native/exchange/test_exchange_contract.py | 203 ++++++++++-------- 3 files changed, 164 insertions(+), 137 deletions(-) create mode 100644 hydrachain/examples/native/exchange/__init__.py diff --git a/hydrachain/examples/native/exchange/__init__.py b/hydrachain/examples/native/exchange/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hydrachain/examples/native/exchange/exchange_contract.py b/hydrachain/examples/native/exchange/exchange_contract.py index 58596b8..6b34758 100755 --- a/hydrachain/examples/native/exchange/exchange_contract.py +++ b/hydrachain/examples/native/exchange/exchange_contract.py @@ -9,21 +9,21 @@ class Partial(nc.ABIEvent): "Triggerd when tokens are partially traded on the Exchange" - args = [dict(name='_currencyPair', type='bytes32', indexed=True), - dict(name='_seller', type='address', indexed=True), - dict(name='_offerValue', type='uint256', indexed=False), - dict(name='_buyer', type='address', indexed=True), - dict(name='_wantValue', type='uint256', indexed=False)] + args = [dict(name='currencyPair', type='bytes32', indexed=True), + dict(name='seller', type='address', indexed=True), + dict(name='offerAmount', type='uint256', indexed=False), + dict(name='buyer', type='address', indexed=True), + dict(name='wantAmount', type='uint256', indexed=False)] class Traded(nc.ABIEvent): "Triggerd when tokens are traded on the Exchange" - args = [dict(name='_currencyPair', type='bytes32', indexed=True), - dict(name='_seller', type='address', indexed=True), - dict(name='_offerValue', type='uint256', indexed=False), - dict(name='_buyer', type='address', indexed=True), - dict(name='_wantValue', type='uint256', indexed=False)] + args = [dict(name='currencyPair', type='bytes32', indexed=True), + dict(name='seller', type='address', indexed=True), + dict(name='offerAmount', type='uint256', indexed=False), + dict(name='buyer', type='address', indexed=True), + dict(name='wantAmount', type='uint256', indexed=False)] class Exchange(nc.NativeContract): @@ -39,9 +39,9 @@ class Exchange(nc.NativeContract): # Struct order_creator = nc.List('address') order_offerCurrency = nc.List('address') - order_offerValue = nc.List('uint256') + order_offerAmount = nc.List('uint256') order_wantCurrency = nc.List('address') - order_wantValue = nc.List('uint256') + order_wantAmount = nc.List('uint256') order_id = nc.Scalar('uint256') owner = nc.Scalar('address') @@ -64,64 +64,64 @@ def get_nextOrderId(ctx, returns='uint256'): def get_orderCreator(ctx, _offerId='uint256', returns='address'): return ctx.order_creator[_offerId - 1] - def place_order(ctx, _offerCurrency='address', _offerValue='uint256', _wantCurrency='address', _wantValue='uint256', returns='uint256'): + def place_order(ctx, _offerCurrency='address', _offerAmount='uint256', _wantCurrency='address', _wantAmount='uint256', returns='uint256'): offer_id = nc.Scalar('uint256') offer_id = 0 # Deposit, coins at address: _offerCurrency # from: Msg Sender # to: Exchange - # Value: _offerValue + # Value: _offerAmount r = ctx.call_abi( _offerCurrency, - coinc.Coin.transferFrom, + Fungible.transferFrom, ctx.msg_sender, ctx.address, - _offerValue) + _offerAmount) if r is OK: # Store Offer offer_id = ctx.nextOrderId ctx.nextOrderId += 1 ctx.order_creator.append(ctx.msg_sender) ctx.order_offerCurrency.append(_offerCurrency) - ctx.order_offerValue.append(_offerValue) + ctx.order_offerAmount.append(_offerAmount) ctx.order_wantCurrency.append(_wantCurrency) - ctx.order_wantValue.append(_wantValue) + ctx.order_wantAmount.append(_wantAmount) return offer_id elif r is INSUFFICIENTFUNDS: return offer_id else: raise Exception() - def claim_order_partial(ctx, _offerId='uint256', _offerValue='uint256', _wantValue='uint256', returns=STATUS): + def claim_order_partial(ctx, _offerId='uint256', _offerAmount='uint256', _wantAmount='uint256', returns=STATUS): # Check for the right rate - if _offerValue >= ctx.order_offerValue[_offerId - 1] or _wantValue >= ctx.order_wantValue[_offerId - 1]: + if _offerAmount >= ctx.order_offerAmount[_offerId - 1] or _wantAmount >= ctx.order_wantAmount[_offerId - 1]: return FORBIDDEN - # Assert that: offerValue/order_offerValue == wantValue/order_wantValue - if _offerValue * ctx.order_wantValue[_offerId - 1] != _wantValue * ctx.order_offerValue[_offerId - 1]: + # Assert that: offerAmount/order_offerAmount == wantAmount/order_wantAmount + if _offerAmount * ctx.order_wantAmount[_offerId - 1] != _wantAmount * ctx.order_offerAmount[_offerId - 1]: return FORBIDDEN # Send, coins at address: _wantCurrency # from: Msg Sender # to: Order Creator - # Value: _wantValue + # Value: _wantAmount r = ctx.call_abi( ctx.order_wantCurrency[_offerId - 1], - coinc.Coin.transferFrom, + Fungible.transferFrom, ctx.msg_sender, ctx.order_creator[_offerId - 1], - _wantValue) + _wantAmount) if r is OK: - # Send, coins at address: _offerValue + # Send, coins at address: _offerAmount # from: Exchange # to: Msg Sender - # Value: _offerValue + # Value: _offerAmount # i.e. execute 2nd side of the trade r1 = ctx.call_abi( ctx.order_offerCurrency[_offerId - 1], - coinc.Coin.transfer, + Fungible.transfer, ctx.msg_sender, - _offerValue) + _offerAmount) # TODO CurrencyPair # currencyPair = (self.orders[_offerId].offerCurrency / 2**32) * 2**128 + (self.orders[_offerId].wantCurrency / 2**32) # currencyPair = ctx.order_offerCurrency[_offerId - 1] + ctx.order_wantCurrency[_offerId - 1] @@ -129,12 +129,12 @@ def claim_order_partial(ctx, _offerId='uint256', _offerValue='uint256', _wantVal ctx.Partial( currencyPair, ctx.order_creator[_offerId - 1], - _offerValue, + _offerAmount, ctx.msg_sender, - _wantValue) + _wantAmount) # Update order as partially matched - ctx.order_offerValue[_offerId - 1] -= _offerValue - ctx.order_wantValue[_offerId - 1] -= _wantValue + ctx.order_offerAmount[_offerId - 1] -= _offerAmount + ctx.order_wantAmount[_offerId - 1] -= _wantAmount return OK elif r is INSUFFICIENTFUNDS: return INSUFFICIENTFUNDS @@ -146,25 +146,25 @@ def claim_order(ctx, _offerId='uint256', returns=STATUS): # Send, coins at address: _wantCurrency # from: Msg Sender # to: Order Creator - # Value: _wantValue + # Value: _wantAmount r = ctx.call_abi( ctx.order_wantCurrency[_offerId - 1], - coinc.Coin.transferFrom, + Fungible.transferFrom, ctx.msg_sender, ctx.order_creator[_offerId - 1], - ctx.order_wantValue[_offerId - 1]) + ctx.order_wantAmount[_offerId - 1]) if r is OK: - # Send, coins at address: _offerValue + # Send, coins at address: _offerAmount # from: Exchange # to: Msg Sender - # Value: _offerValue + # Value: _offerAmount # i.e. execute 2nd side of the trade r1 = ctx.call_abi( ctx.order_offerCurrency[_offerId - 1], - coinc.Coin.transfer, + Fungible.transfer, ctx.msg_sender, - ctx.order_offerValue[_offerId - 1]) + ctx.order_offerAmount[_offerId - 1]) # TODO CurrencyPair # currencyPair = (self.orders[_offerId].offerCurrency / 2**32) * 2**128 + (self.orders[_offerId].wantCurrency / 2**32) # currencyPair = ctx.order_offerCurrency[_offerId - 1] + ctx.order_wantCurrency[_offerId - 1] @@ -172,15 +172,15 @@ def claim_order(ctx, _offerId='uint256', returns=STATUS): ctx.Traded( currencyPair, ctx.order_creator[_offerId - 1], - ctx.order_offerValue[_offerId - 1], + ctx.order_offerAmount[_offerId - 1], ctx.msg_sender, - ctx.order_wantValue[_offerId - 1]) + ctx.order_wantAmount[_offerId - 1]) # Update order as executed ctx.order_creator[_offerId - 1] = '\0' * 20 ctx.order_offerCurrency[_offerId - 1] = '\0' * 20 - ctx.order_offerValue[_offerId - 1] = 0 + ctx.order_offerAmount[_offerId - 1] = 0 ctx.order_wantCurrency[_offerId - 1] = '\0' * 20 - ctx.order_wantValue[_offerId - 1] = 0 + ctx.order_wantAmount[_offerId - 1] = 0 return OK elif r is INSUFFICIENTFUNDS: return INSUFFICIENTFUNDS @@ -195,16 +195,16 @@ def delete_order(ctx, _offerId='uint256', returns=STATUS): # Return, coins at address: _offerCurrency # from: Exchange # to: Order Creator - # Value: _offerValue + # Value: _offerAmount r = ctx.call_abi( ctx.order_offerCurrency[_offerId - 1], - coinc.Coin.transfer, + Fungible.transfer, ctx.order_creator[_offerId - 1], - ctx.order_offerValue[_offerId - 1]) + ctx.order_offerAmount[_offerId - 1]) # Update order as deleted ctx.order_creator[_offerId - 1] = '\0' * 20 ctx.order_offerCurrency[_offerId - 1] = '\0' * 20 - ctx.order_offerValue[_offerId - 1] = 0 + ctx.order_offerAmount[_offerId - 1] = 0 ctx.order_wantCurrency[_offerId - 1] = '\0' * 20 - ctx.order_wantValue[_offerId - 1] = 0 + ctx.order_wantAmount[_offerId - 1] = 0 return OK diff --git a/hydrachain/examples/native/exchange/test_exchange_contract.py b/hydrachain/examples/native/exchange/test_exchange_contract.py index 10f51b3..924580c 100755 --- a/hydrachain/examples/native/exchange/test_exchange_contract.py +++ b/hydrachain/examples/native/exchange/test_exchange_contract.py @@ -1,5 +1,7 @@ from hydrachain import native_contracts as nc +from hydrachain.nc_utils import FORBIDDEN, OK, INSUFFICIENTFUNDS from hydrachain.examples.native.fungible.fungible_contract import Fungible +from hydrachain.examples.native.exchange.exchange_contract import Exchange from ethereum import tester import ethereum.slogging as slogging log = slogging.get_logger('test.exchange') @@ -16,133 +18,158 @@ def add_proxy(self, name, address): setattr(self, name, nc.tester_nac(self.state, self.key, address)) -def test_exchange_template(register_coins, register_exchange): +def _prepare_env(state, logs, admin, alice, bob, eur_address, usd_address, exc_address): + # Create proxy EUR + admin.add_proxy('eur', eur_address) + assert OK == admin.eur.init(2**32) + assert OK == admin.eur.transfer(alice.address, 2**30) + assert OK == admin.eur.transfer(bob.address, 2**30) + alice.add_proxy('eur', eur_address) + bob.add_proxy('eur', eur_address) + assert admin.eur.balanceOf(admin.address) == 2**31 + + # Create proxy USD + admin.add_proxy('usd', usd_address) + assert OK == admin.usd.init(2**32) + assert OK == admin.usd.transfer(alice.address, 2**30) + assert OK == admin.usd.transfer(bob.address, 2**30) + alice.add_proxy('usd', usd_address) + bob.add_proxy('usd', usd_address) + assert admin.usd.balanceOf(admin.address) == 2**31 + + # Checking Total Sum + accounts = admin.eur.get_accounts() + assert accounts == [admin.address, alice.address, bob.address] + total_sum = 0 + for account in accounts: + total_sum += admin.eur.balanceOf(account) + assert total_sum == 2**32 + accounts = admin.usd.get_accounts() + assert accounts == [admin.address, alice.address, bob.address] + total_sum = 0 + for account in accounts: + total_sum += admin.usd.balanceOf(account) + assert total_sum == 2**32 + + # Create proxy Exchange + admin.add_proxy('exc', exc_address) + assert OK == admin.exc.init() + alice.add_proxy('exc', exc_address) + bob.add_proxy('exc', exc_address) + + return True + + +def test_exchange_template(): + + # Register Contract Fungible + nc.registry.register(Fungible) + nc.registry.register(Exchange) state = tester.state() logs = [] - exchange = User(state, tester.a0, tester.k0) - coin = User(state, tester.a1, tester.k1) + admin = User(state, tester.a0, tester.k0) alice = User(state, tester.a2, tester.k2) bob = User(state, tester.a3, tester.k3) - # Listen to Events - nc.listen_logs(state, coinc.Transfer, callback=lambda e: logs.append(e)) - nc.listen_logs(state, exchangec.Traded, callback=lambda e: logs.append(e)) - - # Create proxy - exchange.add_proxy('ex', exchangec.Exchange.address) - - # Initalize exchange - exchange.ex.init() - assert exchange.ex.get_nextOrderId() == 1 - - # Initialize Alice as a proxy of the Exchange - alice.add_proxy('ex', exchangec.Exchange.address) - bob.add_proxy('ex', exchangec.Exchange.address) - - # Initialize Alice as a coin (=ca) creator - alice.add_proxy('ca', Coin_Alice.address) - # initalize ca with a fixed quantity of coins. - ca_total = 1000000 - assert alice.ca.init(ca_total) == 200 - assert alice.ca.balanceOf(alice.address) == ca_total - - # Initialize Bob as a coin (=cb) creator - bob.add_proxy('cb', Coin_Bob.address) - # initalize cb with a fixed quantity of coins. - cb_total = 1000000 - assert bob.cb.init(cb_total) == 200 - assert bob.cb.balanceOf(bob.address) == cb_total - - # Initialize Proxies - exchange.add_proxy('ca', Coin_Alice.address) - exchange.add_proxy('cb', Coin_Bob.address) + # create listeners + for evt_class in Fungible.events + Exchange.events: + nc.listen_logs(state, evt_class, callback=lambda e: logs.append(e)) + + # Initialization + eur_address = nc.tester_create_native_contract_instance(state, admin.key, Fungible) + usd_address = nc.tester_create_native_contract_instance(state, admin.key, Fungible) + exc_address = nc.tester_create_native_contract_instance(state, admin.key, Exchange) + _prepare_env(state, logs, admin, alice, bob, eur_address, usd_address, exc_address) + assert admin.eur.balanceOf(alice.address) == 2**30 + alice_eur_total = 2**30 + assert admin.usd.balanceOf(bob.address) == 2**30 + bob_usd_total = 2**30 + assert admin.exc.get_nextOrderId() == 1 # Alice does a direct Debit for Exchange address - alice.ca.approve(exchangec.Exchange.address, 1000) + alice.eur.approve(exc_address, 1000) # Alice does an IPO of her coin at the exchange # Remark, # Price is set by the division of the two quantities. alice_coin_quantity = 100 bob_coin_quantity = 100 - alice_offer_id = alice.ex.placeOrder( - Coin_Alice.address, + alice_offer_id = alice.exc.place_order( + eur_address, alice_coin_quantity, - Coin_Bob.address, + usd_address, bob_coin_quantity) + # Check log data of Transfer Event - assert len(logs) == 1 - l = logs[0] + assert len(logs) == 6 + l = logs[5] assert l['event_type'] == 'Transfer' - assert l['_from'] == alice.address - assert l['_to'] == exchangec.Exchange.address - assert str(l['args']) == '[' + str(alice_coin_quantity) + ']' + assert l['from'] == alice.address + assert l['to'] == exc_address + assert l['value'] == alice_coin_quantity assert alice_offer_id == 1 - assert alice.ex.get_orderCreator(alice_offer_id) == alice.address - assert alice.ex.get_nextOrderId() == 2 + assert alice.exc.get_orderCreator(alice_offer_id) == alice.address + assert alice.exc.get_nextOrderId() == 2 # Check balances - assert alice.ca.balanceOf(alice.address) == ca_total - alice_coin_quantity - assert alice.ca.balanceOf(exchangec.Exchange.address) == alice_coin_quantity + assert admin.eur.balanceOf(alice.address) == alice_eur_total - alice_coin_quantity + assert admin.eur.balanceOf(exc_address) == alice_coin_quantity # Alice deletes her offer at the exchange - alice.ca.unapprove(exchangec.Exchange.address) - r = alice.ex.deleteOrder(alice_offer_id) + # alice.eur.unapprove(exc_address) + r = alice.exc.delete_order(alice_offer_id) assert r == 200 # Check log data of Transfer Event - assert len(logs) == 2 - l = logs[1] + assert len(logs) == 7 + l = logs[6] assert l['event_type'] == 'Transfer' - assert l['_from'] == exchangec.Exchange.address - assert l['_to'] == alice.address - assert str(l['args']) == '[' + str(alice_coin_quantity) + ']' + assert l['from'] == exc_address + assert l['to'] == alice.address + assert l['value'] == alice_coin_quantity assert alice_offer_id == 1 - assert alice.ex.get_orderCreator(alice_offer_id) == '\0' * 20 - assert alice.ex.get_nextOrderId() == 2 + assert alice.exc.get_orderCreator(alice_offer_id) == '\0' * 20 + assert alice.exc.get_nextOrderId() == 2 # Check balances - assert alice.ca.balanceOf(alice.address) == ca_total - assert alice.ca.balanceOf(exchangec.Exchange.address) == 0 - assert alice.ca.allowance(exchangec.Exchange.address) == 0 + assert admin.eur.balanceOf(alice.address) == alice_eur_total + assert admin.eur.balanceOf(exc_address) == 0 + assert alice.eur.allowance(exc_address) == 1000 - alice_coin_quantity # Alice tries again, willing to lower her price. - alice.ca.approve(exchangec.Exchange.address, 1000) + alice.eur.approve(exc_address, 100) alice_coin_quantity = 100 bob_coin_quantity = 50 - alice_offer_id = alice.ex.placeOrder( - Coin_Alice.address, + alice_offer_id = alice.exc.place_order( + eur_address, alice_coin_quantity, - Coin_Bob.address, + usd_address, bob_coin_quantity) # check logs data of Transfer Event assert alice_offer_id == 2 - assert alice.ex.get_orderCreator(alice_offer_id) == alice.address - assert alice.ex.get_nextOrderId() == 3 + assert alice.exc.get_orderCreator(alice_offer_id) == alice.address + assert alice.exc.get_nextOrderId() == 3 # Now Bob wants to share coins # Bob does a direct Debit for Exchange address - bob.cb.approve(exchangec.Exchange.address, 1000) - r = bob.ex.claimOrder(alice_offer_id) + bob.usd.approve(exc_address, 1000) + r = bob.exc.claim_order(alice_offer_id) assert r == 200 # Check balances - assert alice.ca.balanceOf(alice.address) == ca_total - alice_coin_quantity - assert alice.ca.balanceOf(exchangec.Exchange.address) == 0 - assert alice.ca.allowance(exchangec.Exchange.address) == 1000 - alice_coin_quantity - assert bob.cb.balanceOf(bob.address) == cb_total - bob_coin_quantity - assert bob.cb.balanceOf(exchangec.Exchange.address) == 0 - assert bob.cb.allowance(exchangec.Exchange.address) == 1000 - bob_coin_quantity + assert admin.eur.balanceOf(alice.address) == alice_eur_total - alice_coin_quantity + assert admin.eur.balanceOf(exc_address) == 0 + assert alice.eur.allowance(exc_address) == 1000 - alice_coin_quantity + assert admin.usd.balanceOf(bob.address) == bob_usd_total - bob_coin_quantity + assert admin.usd.balanceOf(exc_address) == 0 + assert bob.usd.allowance(exc_address) == 1000 - bob_coin_quantity # Check balances - Cross Currency - assert alice.ca.balanceOf(bob.address) == alice_coin_quantity - assert bob.cb.balanceOf(alice.address) == bob_coin_quantity - # Check log data of Traded Event - #TODO adjust currencyPair - # print logs - # assert len(logs) == 6 - # l = logs[1] - # assert l['event_type'] == 'Traded' - # assert l['_currencyPair'] == '' - - - print logs - while logs and logs.pop(): - pass + assert admin.eur.balanceOf(alice.address) == 2**30 - alice_coin_quantity + assert admin.eur.balanceOf(bob.address) == 2**30 + alice_coin_quantity + assert admin.usd.balanceOf(alice.address) == 2**30 + bob_coin_quantity + assert admin.usd.balanceOf(bob.address) == 2**30 - bob_coin_quantity + + # Print Logs + for l in logs: + print l['event_type'] + + nc.registry.unregister(Fungible) + nc.registry.unregister(Exchange) From 30b933ad08b6f73dcbea5aba00c00612effaa558 Mon Sep 17 00:00:00 2001 From: Reto Trinkler Date: Fri, 29 Jan 2016 17:42:20 +0100 Subject: [PATCH 4/5] Exchange testing partial order --- .../native/exchange/test_exchange_contract.py | 72 ++++++++++--------- 1 file changed, 38 insertions(+), 34 deletions(-) diff --git a/hydrachain/examples/native/exchange/test_exchange_contract.py b/hydrachain/examples/native/exchange/test_exchange_contract.py index 924580c..f17b825 100755 --- a/hydrachain/examples/native/exchange/test_exchange_contract.py +++ b/hydrachain/examples/native/exchange/test_exchange_contract.py @@ -22,8 +22,7 @@ def _prepare_env(state, logs, admin, alice, bob, eur_address, usd_address, exc_a # Create proxy EUR admin.add_proxy('eur', eur_address) assert OK == admin.eur.init(2**32) - assert OK == admin.eur.transfer(alice.address, 2**30) - assert OK == admin.eur.transfer(bob.address, 2**30) + assert OK == admin.eur.transfer(alice.address, 2**31) alice.add_proxy('eur', eur_address) bob.add_proxy('eur', eur_address) assert admin.eur.balanceOf(admin.address) == 2**31 @@ -31,21 +30,20 @@ def _prepare_env(state, logs, admin, alice, bob, eur_address, usd_address, exc_a # Create proxy USD admin.add_proxy('usd', usd_address) assert OK == admin.usd.init(2**32) - assert OK == admin.usd.transfer(alice.address, 2**30) - assert OK == admin.usd.transfer(bob.address, 2**30) + assert OK == admin.usd.transfer(bob.address, 2**31) alice.add_proxy('usd', usd_address) bob.add_proxy('usd', usd_address) assert admin.usd.balanceOf(admin.address) == 2**31 # Checking Total Sum accounts = admin.eur.get_accounts() - assert accounts == [admin.address, alice.address, bob.address] + assert accounts == [admin.address, alice.address] total_sum = 0 for account in accounts: total_sum += admin.eur.balanceOf(account) assert total_sum == 2**32 accounts = admin.usd.get_accounts() - assert accounts == [admin.address, alice.address, bob.address] + assert accounts == [admin.address, bob.address] total_sum = 0 for account in accounts: total_sum += admin.usd.balanceOf(account) @@ -81,20 +79,22 @@ def test_exchange_template(): usd_address = nc.tester_create_native_contract_instance(state, admin.key, Fungible) exc_address = nc.tester_create_native_contract_instance(state, admin.key, Exchange) _prepare_env(state, logs, admin, alice, bob, eur_address, usd_address, exc_address) - assert admin.eur.balanceOf(alice.address) == 2**30 - alice_eur_total = 2**30 - assert admin.usd.balanceOf(bob.address) == 2**30 - bob_usd_total = 2**30 + assert admin.eur.balanceOf(alice.address) == 2**31 + alice_eur_balance = admin.eur.balanceOf(alice.address) + assert admin.usd.balanceOf(bob.address) == 2**31 + bob_usd_balance = admin.usd.balanceOf(bob.address) assert admin.exc.get_nextOrderId() == 1 - # Alice does a direct Debit for Exchange address - alice.eur.approve(exc_address, 1000) + # Alice does an IPO of her coin at the exchange # Remark, # Price is set by the division of the two quantities. alice_coin_quantity = 100 bob_coin_quantity = 100 + # Alice does a direct Debit for Exchange address + alice.eur.approve(exc_address, alice_coin_quantity) + # Place Order on Exchange alice_offer_id = alice.exc.place_order( eur_address, alice_coin_quantity, @@ -102,8 +102,8 @@ def test_exchange_template(): bob_coin_quantity) # Check log data of Transfer Event - assert len(logs) == 6 - l = logs[5] + assert len(logs) == 4 + l = logs[3] assert l['event_type'] == 'Transfer' assert l['from'] == alice.address assert l['to'] == exc_address @@ -112,16 +112,14 @@ def test_exchange_template(): assert alice.exc.get_orderCreator(alice_offer_id) == alice.address assert alice.exc.get_nextOrderId() == 2 # Check balances - assert admin.eur.balanceOf(alice.address) == alice_eur_total - alice_coin_quantity + assert admin.eur.balanceOf(alice.address) == alice_eur_balance - alice_coin_quantity assert admin.eur.balanceOf(exc_address) == alice_coin_quantity # Alice deletes her offer at the exchange - # alice.eur.unapprove(exc_address) - r = alice.exc.delete_order(alice_offer_id) - assert r == 200 + assert OK == alice.exc.delete_order(alice_offer_id) # Check log data of Transfer Event - assert len(logs) == 7 - l = logs[6] + assert len(logs) == 5 + l = logs[4] assert l['event_type'] == 'Transfer' assert l['from'] == exc_address assert l['to'] == alice.address @@ -131,9 +129,9 @@ def test_exchange_template(): assert alice.exc.get_orderCreator(alice_offer_id) == '\0' * 20 assert alice.exc.get_nextOrderId() == 2 # Check balances - assert admin.eur.balanceOf(alice.address) == alice_eur_total + assert admin.eur.balanceOf(alice.address) == alice_eur_balance assert admin.eur.balanceOf(exc_address) == 0 - assert alice.eur.allowance(exc_address) == 1000 - alice_coin_quantity + assert alice.eur.allowance(exc_address) == 0 # Alice tries again, willing to lower her price. alice.eur.approve(exc_address, 100) @@ -151,21 +149,27 @@ def test_exchange_template(): # Now Bob wants to share coins # Bob does a direct Debit for Exchange address - bob.usd.approve(exc_address, 1000) - r = bob.exc.claim_order(alice_offer_id) - assert r == 200 - # Check balances - assert admin.eur.balanceOf(alice.address) == alice_eur_total - alice_coin_quantity + bob.usd.approve(exc_address, bob_coin_quantity) + # First with partial amounts + alice_coin_partial = 10 + bob_coin_partial = 5 + assert OK == bob.exc.claim_order_partial(alice_offer_id, alice_coin_partial, bob_coin_partial) + assert admin.eur.balanceOf(alice.address) == alice_eur_balance - alice_coin_quantity + assert admin.usd.balanceOf(alice.address) == bob_coin_partial + assert admin.eur.balanceOf(bob.address) == alice_coin_partial + assert admin.usd.balanceOf(bob.address) == bob_usd_balance - bob_coin_partial + # Then the remaining amounts + assert OK == bob.exc.claim_order(alice_offer_id) + # Check balances of Exchange assert admin.eur.balanceOf(exc_address) == 0 - assert alice.eur.allowance(exc_address) == 1000 - alice_coin_quantity - assert admin.usd.balanceOf(bob.address) == bob_usd_total - bob_coin_quantity assert admin.usd.balanceOf(exc_address) == 0 - assert bob.usd.allowance(exc_address) == 1000 - bob_coin_quantity + assert alice.eur.allowance(exc_address) == 0 + assert bob.usd.allowance(exc_address) == 0 # Check balances - Cross Currency - assert admin.eur.balanceOf(alice.address) == 2**30 - alice_coin_quantity - assert admin.eur.balanceOf(bob.address) == 2**30 + alice_coin_quantity - assert admin.usd.balanceOf(alice.address) == 2**30 + bob_coin_quantity - assert admin.usd.balanceOf(bob.address) == 2**30 - bob_coin_quantity + assert admin.eur.balanceOf(alice.address) == alice_eur_balance - alice_coin_quantity + assert admin.usd.balanceOf(alice.address) == bob_coin_quantity + assert admin.eur.balanceOf(bob.address) == alice_coin_quantity + assert admin.usd.balanceOf(bob.address) == bob_usd_balance - bob_coin_quantity # Print Logs for l in logs: From 8f2e1e24443407a48ed1dcb84d1589871e890a84 Mon Sep 17 00:00:00 2001 From: Reto Trinkler Date: Fri, 29 Jan 2016 17:46:46 +0100 Subject: [PATCH 5/5] Clean up --- .../native/exchange/test_exchange_contract.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/hydrachain/examples/native/exchange/test_exchange_contract.py b/hydrachain/examples/native/exchange/test_exchange_contract.py index f17b825..76631a7 100755 --- a/hydrachain/examples/native/exchange/test_exchange_contract.py +++ b/hydrachain/examples/native/exchange/test_exchange_contract.py @@ -59,6 +59,16 @@ def _prepare_env(state, logs, admin, alice, bob, eur_address, usd_address, exc_a def test_exchange_template(): + """ + Tests; + Initialization, + Create orders, + Delete orders, + Partially claim orders, + Fully Claim orders. + Events; + Checking logs + """ # Register Contract Fungible nc.registry.register(Fungible) @@ -85,8 +95,6 @@ def test_exchange_template(): bob_usd_balance = admin.usd.balanceOf(bob.address) assert admin.exc.get_nextOrderId() == 1 - - # Alice does an IPO of her coin at the exchange # Remark, # Price is set by the division of the two quantities.