diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 527902aa5..d64c4abc7 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -133,6 +133,7 @@ void database::initialize_evaluators() register_evaluator(); register_evaluator(); register_evaluator(); + register_evaluator(); register_evaluator(); register_evaluator(); register_evaluator(); diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index 1e43bcdc0..c33b4f331 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -326,6 +326,10 @@ struct get_impacted_account_visitor { _impacted.insert( op.fee_payer() ); // account } + void operator()( const liquidity_pool_update_operation& op ) + { + _impacted.insert( op.fee_payer() ); // account + } void operator()( const liquidity_pool_deposit_operation& op ) { _impacted.insert( op.fee_payer() ); // account diff --git a/libraries/chain/hardfork.d/CORE_2604.hf b/libraries/chain/hardfork.d/CORE_2604.hf new file mode 100644 index 000000000..b71dc3955 --- /dev/null +++ b/libraries/chain/hardfork.d/CORE_2604.hf @@ -0,0 +1,6 @@ +// bitshares-core issue #2604 Allow updating liquidity pool fee rates with certain restrictions +#ifndef HARDFORK_CORE_2604_TIME +// Jan 1 2030, midnight; this is a dummy date until a hardfork date is scheduled +#define HARDFORK_CORE_2604_TIME (fc::time_point_sec( 1893456000 )) +#define HARDFORK_CORE_2604_PASSED(head_block_time) (head_block_time >= HARDFORK_CORE_2604_TIME) +#endif diff --git a/libraries/chain/include/graphene/chain/hardfork_visitor.hpp b/libraries/chain/include/graphene/chain/hardfork_visitor.hpp index fb284f78d..58af05349 100644 --- a/libraries/chain/include/graphene/chain/hardfork_visitor.hpp +++ b/libraries/chain/include/graphene/chain/hardfork_visitor.hpp @@ -54,6 +54,7 @@ struct hardfork_visitor { protocol::liquidity_pool_deposit_operation, protocol::liquidity_pool_withdraw_operation, protocol::liquidity_pool_exchange_operation >; + using liquidity_pool_update_op = fc::typelist::list< protocol::liquidity_pool_update_operation >; using samet_fund_ops = fc::typelist::list< protocol::samet_fund_create_operation, protocol::samet_fund_delete_operation, protocol::samet_fund_update_operation, @@ -90,6 +91,9 @@ struct hardfork_visitor { template std::enable_if_t(), bool> visit() { return HARDFORK_CORE_2362_PASSED(now); } + template + std::enable_if_t(), bool> + visit() { return HARDFORK_CORE_2604_PASSED(now); } /// @} /// typelist::runtime::dispatch adaptor diff --git a/libraries/chain/include/graphene/chain/liquidity_pool_evaluator.hpp b/libraries/chain/include/graphene/chain/liquidity_pool_evaluator.hpp index e7de47be7..ad9a65322 100644 --- a/libraries/chain/include/graphene/chain/liquidity_pool_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/liquidity_pool_evaluator.hpp @@ -55,6 +55,17 @@ namespace graphene { namespace chain { const asset_object* _share_asset = nullptr; }; + class liquidity_pool_update_evaluator : public evaluator + { + public: + using operation_type = liquidity_pool_update_operation; + + void_result do_evaluate( const liquidity_pool_update_operation& op ); + void_result do_apply( const liquidity_pool_update_operation& op ) const; + + const liquidity_pool_object* _pool = nullptr; + }; + class liquidity_pool_deposit_evaluator : public evaluator { public: diff --git a/libraries/chain/liquidity_pool_evaluator.cpp b/libraries/chain/liquidity_pool_evaluator.cpp index d0c6d14e9..553910b05 100644 --- a/libraries/chain/liquidity_pool_evaluator.cpp +++ b/libraries/chain/liquidity_pool_evaluator.cpp @@ -114,6 +114,44 @@ generic_operation_result liquidity_pool_delete_evaluator::do_apply(const liquidi return result; } FC_CAPTURE_AND_RETHROW( (op) ) } +void_result liquidity_pool_update_evaluator::do_evaluate(const liquidity_pool_update_operation& op) +{ try { + const database& d = db(); + const auto block_time = d.head_block_time(); + + FC_ASSERT( HARDFORK_CORE_2604_PASSED(block_time), "Not allowed until the core-2604 hardfork" ); + + _pool = &op.pool(d); + + const asset_object* _share_asset = &_pool->share_asset(d); + + FC_ASSERT( _share_asset->issuer == op.account, "The account is not the owner of the liquidity pool" ); + + if( op.taker_fee_percent.valid() ) + { + FC_ASSERT( 0 == _pool->withdrawal_fee_percent + || ( op.withdrawal_fee_percent.valid() && 0 == *op.withdrawal_fee_percent ), + "Taker fee percent can only be updated if withdrawal fee percent is zero or " + "withdrawal fee percent is to be updated to zero at the same time" ); + } + + return void_result(); +} FC_CAPTURE_AND_RETHROW( (op) ) } + +void_result liquidity_pool_update_evaluator::do_apply(const liquidity_pool_update_operation& op) const +{ try { + database& d = db(); + + d.modify( *_pool, [&op](liquidity_pool_object& obj) { + if( op.taker_fee_percent.valid() ) + obj.taker_fee_percent = *op.taker_fee_percent; + if( op.withdrawal_fee_percent.valid() ) + obj.withdrawal_fee_percent = *op.withdrawal_fee_percent; + }); + + return void_result(); +} FC_CAPTURE_AND_RETHROW( (op) ) } + void_result liquidity_pool_deposit_evaluator::do_evaluate(const liquidity_pool_deposit_operation& op) { try { const database& d = db(); diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index 0e16e1d90..1e39c0253 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -205,6 +205,10 @@ struct proposal_operation_hardfork_visitor FC_ASSERT(!op.new_parameters.current_fees->exists(), "Unable to define fees for credit offer operations prior to the core-2362 hardfork"); } + if (!HARDFORK_CORE_2604_PASSED(block_time)) { + FC_ASSERT(!op.new_parameters.current_fees->exists(), + "Unable to define fees for liquidity pool update operation prior to the core-2604 hardfork"); + } } void operator()(const graphene::chain::htlc_create_operation &op) const { FC_ASSERT( block_time >= HARDFORK_CORE_1468_TIME, "Not allowed until hardfork 1468" ); @@ -246,6 +250,9 @@ struct proposal_operation_hardfork_visitor void operator()(const graphene::chain::liquidity_pool_delete_operation&) const { FC_ASSERT( HARDFORK_LIQUIDITY_POOL_PASSED(block_time), "Not allowed until the LP hardfork" ); } + void operator()(const graphene::chain::liquidity_pool_update_operation&) const { + FC_ASSERT( HARDFORK_CORE_2604_PASSED(block_time), "Not allowed until the core-2604 hardfork" ); + } void operator()(const graphene::chain::liquidity_pool_deposit_operation&) const { FC_ASSERT( HARDFORK_LIQUIDITY_POOL_PASSED(block_time), "Not allowed until the LP hardfork" ); } diff --git a/libraries/plugins/market_history/market_history_plugin.cpp b/libraries/plugins/market_history/market_history_plugin.cpp index 839117de8..b4dfa9dba 100644 --- a/libraries/plugins/market_history/market_history_plugin.cpp +++ b/libraries/plugins/market_history/market_history_plugin.cpp @@ -511,6 +511,11 @@ struct get_liquidity_pool_id_visitor return o.pool; } + result_type operator()( const liquidity_pool_update_operation& o )const + { + return o.pool; + } + result_type operator()( const liquidity_pool_deposit_operation& o )const { return o.pool; diff --git a/libraries/protocol/include/graphene/protocol/liquidity_pool.hpp b/libraries/protocol/include/graphene/protocol/liquidity_pool.hpp index 712090de3..0bbed4a28 100644 --- a/libraries/protocol/include/graphene/protocol/liquidity_pool.hpp +++ b/libraries/protocol/include/graphene/protocol/liquidity_pool.hpp @@ -67,6 +67,26 @@ namespace graphene { namespace protocol { void validate()const; }; + /** + * @brief Update a liquidity pool + * @ingroup operations + */ + struct liquidity_pool_update_operation : public base_operation + { + struct fee_parameters_type { uint64_t fee = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; }; + + asset fee; ///< Operation fee + account_id_type account; ///< The account who owns the liquidity pool + liquidity_pool_id_type pool; ///< ID of the liquidity pool + optional taker_fee_percent; ///< Taker fee percent + optional withdrawal_fee_percent; ///< Withdrawal fee percent + + extensions_type extensions; ///< Unused. Reserved for future use. + + account_id_type fee_payer()const { return account; } + void validate()const; + }; + /** * @brief Deposit to a liquidity pool * @ingroup operations @@ -135,6 +155,7 @@ namespace graphene { namespace protocol { FC_REFLECT( graphene::protocol::liquidity_pool_create_operation::fee_parameters_type, (fee) ) FC_REFLECT( graphene::protocol::liquidity_pool_delete_operation::fee_parameters_type, (fee) ) +FC_REFLECT( graphene::protocol::liquidity_pool_update_operation::fee_parameters_type, (fee) ) FC_REFLECT( graphene::protocol::liquidity_pool_deposit_operation::fee_parameters_type, (fee) ) FC_REFLECT( graphene::protocol::liquidity_pool_withdraw_operation::fee_parameters_type, (fee) ) FC_REFLECT( graphene::protocol::liquidity_pool_exchange_operation::fee_parameters_type, (fee) ) @@ -144,6 +165,8 @@ FC_REFLECT( graphene::protocol::liquidity_pool_create_operation, (taker_fee_percent)(withdrawal_fee_percent)(extensions) ) FC_REFLECT( graphene::protocol::liquidity_pool_delete_operation, (fee)(account)(pool)(extensions) ) +FC_REFLECT( graphene::protocol::liquidity_pool_update_operation, + (fee)(account)(pool)(taker_fee_percent)(withdrawal_fee_percent)(extensions) ) FC_REFLECT( graphene::protocol::liquidity_pool_deposit_operation, (fee)(account)(pool)(amount_a)(amount_b)(extensions) ) FC_REFLECT( graphene::protocol::liquidity_pool_withdraw_operation, @@ -153,12 +176,14 @@ FC_REFLECT( graphene::protocol::liquidity_pool_exchange_operation, GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::liquidity_pool_create_operation::fee_parameters_type ) GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::liquidity_pool_delete_operation::fee_parameters_type ) +GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::liquidity_pool_update_operation::fee_parameters_type ) GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::liquidity_pool_deposit_operation::fee_parameters_type ) GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::liquidity_pool_withdraw_operation::fee_parameters_type ) GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::liquidity_pool_exchange_operation::fee_parameters_type ) GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::liquidity_pool_create_operation ) GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::liquidity_pool_delete_operation ) +GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::liquidity_pool_update_operation ) GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::liquidity_pool_deposit_operation ) GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::liquidity_pool_withdraw_operation ) GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::liquidity_pool_exchange_operation ) diff --git a/libraries/protocol/include/graphene/protocol/operations.hpp b/libraries/protocol/include/graphene/protocol/operations.hpp index 0b32f7e5d..798c40059 100644 --- a/libraries/protocol/include/graphene/protocol/operations.hpp +++ b/libraries/protocol/include/graphene/protocol/operations.hpp @@ -127,7 +127,8 @@ namespace graphene { namespace protocol { /* 71 */ credit_offer_update_operation, /* 72 */ credit_offer_accept_operation, /* 73 */ credit_deal_repay_operation, - /* 74 */ credit_deal_expired_operation // VIRTUAL + /* 74 */ credit_deal_expired_operation, // VIRTUAL + /* 75 */ liquidity_pool_update_operation >; /// @} // operations group diff --git a/libraries/protocol/liquidity_pool.cpp b/libraries/protocol/liquidity_pool.cpp index 956273609..a2e290227 100644 --- a/libraries/protocol/liquidity_pool.cpp +++ b/libraries/protocol/liquidity_pool.cpp @@ -42,6 +42,16 @@ void liquidity_pool_delete_operation::validate()const FC_ASSERT( fee.amount >= 0, "Fee should not be negative" ); } +void liquidity_pool_update_operation::validate()const +{ + FC_ASSERT( fee.amount >= 0, "Fee should not be negative" ); + FC_ASSERT( taker_fee_percent.valid() || withdrawal_fee_percent.valid(), "Should update something" ); + if( taker_fee_percent.valid() ) + FC_ASSERT( *taker_fee_percent <= GRAPHENE_100_PERCENT, "Taker fee percent should not exceed 100%" ); + if( withdrawal_fee_percent.valid() ) + FC_ASSERT( 0 == *withdrawal_fee_percent, "Withdrawal fee percent can only be updated to zero" ); +} + void liquidity_pool_deposit_operation::validate()const { FC_ASSERT( fee.amount >= 0, "Fee should not be negative" ); @@ -69,12 +79,14 @@ void liquidity_pool_exchange_operation::validate()const GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::liquidity_pool_create_operation::fee_parameters_type ) GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::liquidity_pool_delete_operation::fee_parameters_type ) +GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::liquidity_pool_update_operation::fee_parameters_type ) GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::liquidity_pool_deposit_operation::fee_parameters_type ) GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::liquidity_pool_withdraw_operation::fee_parameters_type ) GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::liquidity_pool_exchange_operation::fee_parameters_type ) GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::liquidity_pool_create_operation ) GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::liquidity_pool_delete_operation ) +GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::liquidity_pool_update_operation ) GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::liquidity_pool_deposit_operation ) GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::liquidity_pool_withdraw_operation ) GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::liquidity_pool_exchange_operation ) diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 8a8997390..714e4eded 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -1533,6 +1533,35 @@ generic_operation_result database_fixture_base::delete_liquidity_pool( account_i return op_result.get(); } +liquidity_pool_update_operation database_fixture_base::make_liquidity_pool_update_op( account_id_type account, + liquidity_pool_id_type pool, + optional taker_fee_percent, + optional withdrawal_fee_percent )const +{ + liquidity_pool_update_operation op; + op.account = account; + op.pool = pool; + op.taker_fee_percent = taker_fee_percent; + op.withdrawal_fee_percent = withdrawal_fee_percent; + return op; +} + +void database_fixture_base::update_liquidity_pool( account_id_type account, + liquidity_pool_id_type pool, + optional taker_fee_percent, + optional withdrawal_fee_percent ) +{ + liquidity_pool_update_operation op = make_liquidity_pool_update_op( account, pool, taker_fee_percent, + withdrawal_fee_percent ); + trx.operations.clear(); + trx.operations.push_back( op ); + + for( auto& o : trx.operations ) db.current_fee_schedule().set_fee(o); + trx.validate(); + set_expiration( db, trx ); + PUSH_TX(db, trx, ~0); +} + liquidity_pool_deposit_operation database_fixture_base::make_liquidity_pool_deposit_op( account_id_type account, liquidity_pool_id_type pool, const asset& amount_a, const asset& amount_b )const diff --git a/tests/common/database_fixture.hpp b/tests/common/database_fixture.hpp index 340031b38..0ee63be25 100644 --- a/tests/common/database_fixture.hpp +++ b/tests/common/database_fixture.hpp @@ -440,6 +440,13 @@ struct database_fixture_base { liquidity_pool_delete_operation make_liquidity_pool_delete_op( account_id_type account, liquidity_pool_id_type pool )const; generic_operation_result delete_liquidity_pool( account_id_type account, liquidity_pool_id_type pool ); + liquidity_pool_update_operation make_liquidity_pool_update_op( account_id_type account, + liquidity_pool_id_type pool, + optional taker_fee_percent, + optional withdrawal_fee_percent )const; + void update_liquidity_pool( account_id_type account, liquidity_pool_id_type pool, + optional taker_fee_percent, + optional withdrawal_fee_percent ); liquidity_pool_deposit_operation make_liquidity_pool_deposit_op( account_id_type account, liquidity_pool_id_type pool, const asset& amount_a, const asset& amount_b )const; diff --git a/tests/tests/liquidity_pool_tests.cpp b/tests/tests/liquidity_pool_tests.cpp index d0c9d1c08..2f40bf744 100644 --- a/tests/tests/liquidity_pool_tests.cpp +++ b/tests/tests/liquidity_pool_tests.cpp @@ -94,6 +94,164 @@ BOOST_AUTO_TEST_CASE( liquidity_pool_hardfork_time_test ) } } +BOOST_AUTO_TEST_CASE( liquidity_pool_update_hardfork_time_test ) +{ + try { + + // Proceeds to a recent hard fork + generate_blocks( HARDFORK_LIQUIDITY_POOL_TIME ); + set_expiration( db, trx ); + + ACTORS((sam)); + + auto init_amount = 10000000 * GRAPHENE_BLOCKCHAIN_PRECISION; + fund( sam, asset(init_amount) ); + + const asset_object& core = asset_id_type()(db); + const asset_object& usd = create_user_issued_asset( "MYUSD" ); + const asset_object& lpa = create_user_issued_asset( "LPATEST", sam, charge_market_fee ); + + const liquidity_pool_object& lpo = create_liquidity_pool( sam_id, core.get_id(), usd.get_id(), lpa.get_id(), + 0, 0 ); + + // Before the hard fork, unable to update a liquidity pool + // or update with proposals + BOOST_CHECK_THROW( update_liquidity_pool( sam_id, lpo.get_id(), 1, 0 ), fc::exception ); + + liquidity_pool_update_operation updop = make_liquidity_pool_update_op( sam_id, lpo.get_id(), 1, 0 ); + BOOST_CHECK_THROW( propose( updop ), fc::exception ); + + } catch (fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} + +BOOST_AUTO_TEST_CASE( liquidity_pool_update_test ) +{ + try { + + // Pass the hard fork time + generate_blocks( HARDFORK_CORE_2604_TIME ); + set_expiration( db, trx ); + + ACTORS((sam)(ted)); + + auto init_amount = 10000000 * GRAPHENE_BLOCKCHAIN_PRECISION; + fund( sam, asset(init_amount) ); + fund( ted, asset(init_amount) ); + + const asset_object& core = asset_id_type()(db); + const asset_object& usd = create_user_issued_asset( "MYUSD" ); + issue_uia( sam, usd.amount(init_amount) ); + issue_uia( ted, usd.amount(init_amount) ); + + const asset_object& lpa1 = create_user_issued_asset( "LPATEST1", sam, charge_market_fee ); + const asset_object& lpa2 = create_user_issued_asset( "LPATEST2", ted, charge_market_fee ); + + const liquidity_pool_object& lpo1 = create_liquidity_pool( sam_id, core.get_id(), usd.get_id(), lpa1.get_id(), + 0, 0 ); + + BOOST_CHECK( lpo1.asset_a == core.id ); + BOOST_CHECK( lpo1.asset_b == usd.id ); + BOOST_CHECK( lpo1.balance_a == 0 ); + BOOST_CHECK( lpo1.balance_b == 0 ); + BOOST_CHECK( lpo1.share_asset == lpa1.id ); + BOOST_CHECK( lpo1.taker_fee_percent == 0 ); + BOOST_CHECK( lpo1.withdrawal_fee_percent == 0 ); + BOOST_CHECK( lpo1.virtual_value == 0 ); + + deposit_to_liquidity_pool( sam_id, lpo1.get_id(), asset(10), asset( 20, usd.get_id() ) ); + + BOOST_CHECK( lpo1.asset_a == core.id ); + BOOST_CHECK( lpo1.asset_b == usd.id ); + BOOST_CHECK( lpo1.balance_a == 10 ); + BOOST_CHECK( lpo1.balance_b == 20 ); + BOOST_CHECK( lpo1.share_asset == lpa1.id ); + BOOST_CHECK( lpo1.taker_fee_percent == 0 ); + BOOST_CHECK( lpo1.withdrawal_fee_percent == 0 ); + BOOST_CHECK( lpo1.virtual_value == 200 ); + + const liquidity_pool_object& lpo2 = create_liquidity_pool( ted_id, core.get_id(), usd.get_id(), lpa2.get_id(), + 1, 2 ); + + BOOST_CHECK( lpo2.asset_a == core.id ); + BOOST_CHECK( lpo2.asset_b == usd.id ); + BOOST_CHECK( lpo2.balance_a == 0 ); + BOOST_CHECK( lpo2.balance_b == 0 ); + BOOST_CHECK( lpo2.share_asset == lpa2.id ); + BOOST_CHECK( lpo2.taker_fee_percent == 1 ); + BOOST_CHECK( lpo2.withdrawal_fee_percent == 2 ); + BOOST_CHECK( lpo2.virtual_value == 0 ); + + // Able to propose + { + liquidity_pool_update_operation updop = make_liquidity_pool_update_op( sam_id, lpo1.get_id(), 1, 0 ); + propose( updop ); + } + + // Unable to update a liquidity pool with invalid data + // update nothing + BOOST_CHECK_THROW( update_liquidity_pool( sam_id, lpo1.get_id(), {}, {} ), fc::exception ); + BOOST_CHECK_THROW( propose( make_liquidity_pool_update_op( sam_id, lpo1.get_id(), {}, {} ) ), fc::exception ); + // non-zero withdrawal fee + BOOST_CHECK_THROW( update_liquidity_pool( sam_id, lpo1.get_id(), {}, 1 ), fc::exception ); + BOOST_CHECK_THROW( propose( make_liquidity_pool_update_op( sam_id, lpo1.get_id(), {}, 1 ) ), fc::exception ); + BOOST_CHECK_THROW( update_liquidity_pool( sam_id, lpo1.get_id(), 0, 1 ), fc::exception ); + BOOST_CHECK_THROW( propose( make_liquidity_pool_update_op( sam_id, lpo1.get_id(), 0, 1 ) ), fc::exception ); + // taker fee exceeds 100% + BOOST_CHECK_THROW( update_liquidity_pool( sam_id, lpo1.get_id(), 10001, {} ), fc::exception ); + BOOST_CHECK_THROW( update_liquidity_pool( sam_id, lpo1.get_id(), 10001, 0 ), fc::exception ); + BOOST_CHECK_THROW( propose( make_liquidity_pool_update_op( sam_id, lpo1.get_id(), 10001, {} ) ), fc::exception); + BOOST_CHECK_THROW( propose( make_liquidity_pool_update_op( sam_id, lpo1.get_id(), 10001, 0 ) ), fc::exception ); + // Owner mismatch (able to propose) + BOOST_CHECK_THROW( update_liquidity_pool( ted_id, lpo1.get_id(), 1, {} ), fc::exception ); + propose( make_liquidity_pool_update_op( ted_id, lpo1.get_id(), 1, {} ) ); + // Updating taker fee when withdrawal fee is non-zero (able to propose) + BOOST_CHECK_THROW( update_liquidity_pool( ted_id, lpo2.get_id(), 1, {} ), fc::exception ); + propose( make_liquidity_pool_update_op( ted_id, lpo2.get_id(), 1, {} ) ); + + // Sam is able to update lpo1 + update_liquidity_pool( sam_id, lpo1.get_id(), 2, 0 ); + BOOST_CHECK( lpo1.asset_a == core.id ); + BOOST_CHECK( lpo1.asset_b == usd.id ); + BOOST_CHECK( lpo1.balance_a == 10 ); + BOOST_CHECK( lpo1.balance_b == 20 ); + BOOST_CHECK( lpo1.share_asset == lpa1.id ); + BOOST_CHECK( lpo1.taker_fee_percent == 2 ); + BOOST_CHECK( lpo1.withdrawal_fee_percent == 0 ); + BOOST_CHECK( lpo1.virtual_value == 200 ); + + update_liquidity_pool( sam_id, lpo1.get_id(), 1, {} ); + BOOST_CHECK( lpo1.asset_a == core.id ); + BOOST_CHECK( lpo1.asset_b == usd.id ); + BOOST_CHECK( lpo1.balance_a == 10 ); + BOOST_CHECK( lpo1.balance_b == 20 ); + BOOST_CHECK( lpo1.share_asset == lpa1.id ); + BOOST_CHECK( lpo1.taker_fee_percent == 1 ); + BOOST_CHECK( lpo1.withdrawal_fee_percent == 0 ); + BOOST_CHECK( lpo1.virtual_value == 200 ); + + // Sam is able to update lpo2 if to update its withdrawal fee to 0 + update_liquidity_pool( ted_id, lpo2.get_id(), 2, 0 ); + + BOOST_CHECK( lpo2.asset_a == core.id ); + BOOST_CHECK( lpo2.asset_b == usd.id ); + BOOST_CHECK( lpo2.balance_a == 0 ); + BOOST_CHECK( lpo2.balance_b == 0 ); + BOOST_CHECK( lpo2.share_asset == lpa2.id ); + BOOST_CHECK( lpo2.taker_fee_percent == 2 ); + BOOST_CHECK( lpo2.withdrawal_fee_percent == 0 ); + BOOST_CHECK( lpo2.virtual_value == 0 ); + + generate_block(); + + } catch (fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} + BOOST_AUTO_TEST_CASE( liquidity_pool_create_delete_proposal_test ) { try {