#include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace eosio; using namespace eosio::chain; using namespace eosio::chain::contracts; using namespace eosio::testing; using namespace fc; struct margin_state { extended_asset total_lendable; extended_asset total_lent; double least_collateralized = 0; extended_asset interest_pool; double interest_shares = 0; }; FC_REFLECT( margin_state, (total_lendable)(total_lent)(least_collateralized)(interest_pool)(interest_shares) ) struct exchange_state { account_name manager; extended_asset supply; uint32_t fee = 0; struct connector { extended_asset balance; uint32_t weight = 500; margin_state peer_margin; }; connector base; connector quote; }; FC_REFLECT( exchange_state::connector, (balance)(weight)(peer_margin) ); FC_REFLECT( exchange_state, (manager)(supply)(fee)(base)(quote) ); class exchange_tester : public tester { public: auto push_action(account_name contract, const account_name& signer, const action_name &name, const variant_object &data ) { string action_type_name = abi_ser.get_action_type(name); action act; act.account = contract; act.name = name; act.authorization = vector{{signer, config::active_name}}; act.data = abi_ser.variant_to_binary(action_type_name, data); signed_transaction trx; trx.actions.emplace_back(std::move(act)); set_tapos(trx); trx.sign(get_private_key(signer, "active"), chain_id_type()); return push_transaction(trx); } asset get_balance(const account_name& account) const { return get_currency_balance(N(currency), symbol(SY(4,CUR)), account); } exchange_state get_market_state( account_name exchange, symbol sym ) { uint64_t s = sym.value() >> 8; const auto& db = control->get_database(); const auto* tbl = db.find( boost::make_tuple(exchange, s, N(markets))); if (tbl) { const auto *obj = db.find( boost::make_tuple(tbl->id, s)); if( obj ) { fc::datastream ds(obj->value.data(), obj->value.size()); exchange_state result; fc::raw::unpack( ds, result ); return result; } } FC_ASSERT( false, "unknown market state" ); } extended_asset get_exchange_balance( account_name exchange, account_name currency, symbol sym, account_name owner ) { const auto& db = control->get_database(); const auto* tbl = db.find( boost::make_tuple(exchange, owner, N(exaccounts))); if (tbl) { const auto *obj = db.find( boost::make_tuple(tbl->id, owner)); if( obj ) { fc::datastream ds(obj->value.data(), obj->value.size()); account_name own; flat_map, int64_t> balances; fc::raw::unpack( ds, own ); fc::raw::unpack( ds, balances); // wdump((balances)); auto b = balances[ make_pair( sym, currency ) ]; return extended_asset( asset( b, sym ), currency ); } } return extended_asset(); } void deploy_currency( account_name ac ) { create_account( ac ); set_code( ac, currency_wast ); } void deploy_exchange( account_name ac ) { create_account( ac ); set_code( ac, exchange_wast ); } void create_currency( name contract, name signer, asset maxsupply ) { push_action(contract, signer, N(create), mutable_variant_object() ("issuer", contract ) ("maximum_supply", maxsupply ) ("can_freeze", 0) ("can_recall", 0) ("can_whitelist", 0) ); } void issue( name contract, name signer, name to, asset amount ) { push_action( contract, signer, N(issue), mutable_variant_object() ("to", to ) ("quantity", amount ) ("memo", "") ); } auto trade( name ex_contract, name signer, symbol market, extended_asset sell, extended_asset min_receive ) { wdump((market)(sell)(min_receive)); wdump((market.to_string())); wdump((fc::variant(market).as_string())); wdump((fc::variant(market).as())); return push_action( ex_contract, signer, N(trade), mutable_variant_object() ("seller", signer ) ("market", market ) ("sell", sell) ("min_receive", min_receive) ("expire", 0) ("fill_or_kill", 1) ); } auto deposit( name contract, name signer, extended_asset amount ) { return push_action( contract, signer, N(deposit), mutable_variant_object() ("from", signer ) ("quantity", amount ) ); } auto create_exchange( name contract, name signer, extended_asset base_deposit, extended_asset quote_deposit, asset exchange_supply ) { return push_action( contract, signer, N(createx), mutable_variant_object() ("creator", signer) ("initial_supply", exchange_supply) ("fee", 0) ("base_deposit", base_deposit) ("quote_deposit", quote_deposit) ); } exchange_tester() :tester(),abi_ser(json::from_string(exchange_abi).as()) { /* create_account( N(currency) ); set_code( N(currency), currency_wast ); auto result = push_action(N(currency), N(create), mutable_variant_object() ("issuer", "currency") ("maximum_supply", "1000000000.0000 CUR") ("can_freeze", 0) ("can_recall", 0) ("can_whitelist", 0) ); wdump((result)); result = push_action(N(currency), N(issue), mutable_variant_object() ("to", "currency") ("quantity", "1000000.0000 CUR") ("memo", "gggggggggggg") ); wdump((result)); */ produce_block(); } abi_serializer abi_ser; }; BOOST_AUTO_TEST_SUITE(exchange_tests) BOOST_AUTO_TEST_CASE( bootstrap ) try { auto expected = asset::from_string( "1000000.0000 CUR" ); exchange_tester t; t.deploy_exchange( N(exchange) ); t.deploy_currency( N(currency) ); t.create_currency( N(currency), N(currency), expected ); t.issue( N(currency), N(currency), N(currency), expected ); auto actual = t.get_currency_balance(N(currency), expected.get_symbol(), N(currency)); BOOST_REQUIRE_EQUAL(expected, actual); } FC_LOG_AND_RETHROW() /// test_api_bootstrap BOOST_AUTO_TEST_CASE( exchange_issue ) try { auto expected = asset::from_string( "1000000.0000 CUR" ); exchange_tester t; t.deploy_exchange( N(exchange) ); t.deploy_currency( N(currency) ); t.create_currency( N(currency), N(currency), expected ); t.create_currency( N(exchange), N(exchange), asset::from_string( "1000000.000 TEST") ); t.issue( N(currency), N(currency), N(currency), expected ); t.issue( N(exchange), N(exchange), N(exchange), asset::from_string("1000.000 TEST") ); auto actual = t.get_currency_balance(N(currency), expected.get_symbol(), N(currency)); BOOST_REQUIRE_EQUAL(expected, actual); } FC_LOG_AND_RETHROW() /// test_api_bootstrap #define A(X) asset::from_string( #X ) BOOST_AUTO_TEST_CASE( exchange_create ) try { auto expected = asset::from_string( "1000000.0000 CUR" ); exchange_tester t; t.create_account( N(dan) ); t.create_account( N(trader) ); t.deploy_exchange( N(exchange) ); t.deploy_currency( N(currency) ); t.create_currency( N(currency), N(currency), A(1000000.00 USD) ); t.create_currency( N(currency), N(currency), A(1000000.00 BTC) ); t.issue( N(currency), N(currency), N(dan), A(1000.00 USD) ); t.issue( N(currency), N(currency), N(dan), A(1000.00 BTC) ); t.issue( N(currency), N(currency), N(trader), A(2000.00 BTC) ); t.issue( N(currency), N(currency), N(trader), A(2000.00 USD) ); auto dan_usd = t.get_currency_balance(N(currency), symbol(2,"USD"), N(dan)); auto dan_btc = t.get_currency_balance(N(currency), symbol(2,"BTC"), N(dan)); edump((dan_usd)(dan_btc)); t.deposit( N(exchange), N(dan), extended_asset( A(500.00 USD), N(currency) ) ); t.deposit( N(exchange), N(dan), extended_asset( A(500.00 BTC), N(currency) ) ); t.deposit( N(exchange), N(trader), extended_asset( A(1500.00 USD), N(currency) ) ); t.deposit( N(exchange), N(trader), extended_asset( A(1500.00 BTC), N(currency) ) ); auto trader_ex_usd = t.get_exchange_balance( N(exchange), N(currency), symbol(2,"USD"), N(trader) ); auto trader_ex_btc = t.get_exchange_balance( N(exchange), N(currency), symbol(2,"BTC"), N(trader) ); auto dan_ex_usd = t.get_exchange_balance( N(exchange), N(currency), symbol(2,"USD"), N(dan) ); auto dan_ex_btc = t.get_exchange_balance( N(exchange), N(currency), symbol(2,"BTC"), N(dan) ); wdump((dan_ex_usd.quantity)); wdump((dan_ex_btc.quantity)); wdump((trader_ex_btc.quantity)); wdump((trader_ex_usd.quantity)); //t.issue( N(exchange), N(exchange), N(exchange), asset::from_string("1000.000 TEST") ); auto r = t.create_exchange( N(exchange), N(dan), extended_asset( A(400.00 USD), N(currency) ), extended_asset( A(400.00 BTC), N(currency) ), A(10000000.00 EXC) ); auto dan_ex_exc = t.get_exchange_balance( N(exchange), N(exchange), symbol(2,"EXC"), N(dan) ); wdump((dan_ex_exc)); auto result = t.trade( N(exchange), N(trader), symbol(2,"EXC"), extended_asset( A(10.00 BTC), N(currency) ), extended_asset( A(0.01 USD), N(currency) ) ); for( const auto& at : result.action_traces ) ilog( "\r${s}", ("s",at.console) ); trader_ex_usd = t.get_exchange_balance( N(exchange), N(currency), symbol(2,"USD"), N(trader) ); trader_ex_btc = t.get_exchange_balance( N(exchange), N(currency), symbol(2,"BTC"), N(trader) ); wdump((trader_ex_btc.quantity)); wdump((trader_ex_usd.quantity)); result = t.trade( N(exchange), N(trader), symbol(2,"EXC"), extended_asset( A(9.75 USD), N(currency) ), extended_asset( A(0.01 BTC), N(currency) ) ); trader_ex_usd = t.get_exchange_balance( N(exchange), N(currency), symbol(2,"USD"), N(trader) ); trader_ex_btc = t.get_exchange_balance( N(exchange), N(currency), symbol(2,"BTC"), N(trader) ); for( const auto& at : result.action_traces ) ilog( "\r${s}", ("s",at.console) ); wdump((trader_ex_btc.quantity)); wdump((trader_ex_usd.quantity)); BOOST_REQUIRE_EQUAL( trader_ex_usd.quantity, A(1500.00 USD) ); BOOST_REQUIRE_EQUAL( trader_ex_btc.quantity, A(1499.99 BTC) ); wdump((t.get_market_state( N(exchange), symbol(2,"EXC") ) )); //BOOST_REQUIRE_EQUAL(expected, actual); } FC_LOG_AND_RETHROW() /// test_api_bootstrap BOOST_AUTO_TEST_SUITE_END()