Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement liquidity pool #2261

Merged
merged 18 commits into from
Sep 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions libraries/app/application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,9 @@ void application_impl::set_api_limit() {
if(_options->count("api-limit-get-withdraw-permissions-by-recipient")) {
_app_options.api_limit_get_withdraw_permissions_by_recipient = _options->at("api-limit-get-withdraw-permissions-by-recipient").as<uint64_t>();
}
if(_options->count("api-limit-get-liquidity-pools")) {
_app_options.api_limit_get_liquidity_pools = _options->at("api-limit-get-liquidity-pools").as<uint64_t>();
}
}

void application_impl::startup()
Expand Down Expand Up @@ -1054,6 +1057,8 @@ void application::set_program_options(boost::program_options::options_descriptio
"For database_api_impl::get_withdraw_permissions_by_giver to set max limit value")
("api-limit-get-withdraw-permissions-by-recipient",boost::program_options::value<uint64_t>()->default_value(101),
"For database_api_impl::get_withdraw_permissions_by_recipient to set max limit value")
("api-limit-get-liquidity-pools",boost::program_options::value<uint64_t>()->default_value(101),
"For database_api_impl::get_liquidity_pools_* to set max limit value")
;
command_line_options.add(configuration_file_options);
command_line_options.add_options()
Expand Down
135 changes: 135 additions & 0 deletions libraries/app/database_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1736,6 +1736,141 @@ vector<market_trade> database_api_impl::get_trade_history_by_sequence(
return result;
}

//////////////////////////////////////////////////////////////////////
// //
// Liquidity pools //
// //
//////////////////////////////////////////////////////////////////////

vector<liquidity_pool_object> database_api::get_liquidity_pools_by_asset_a(
std::string asset_symbol_or_id,
optional<uint32_t> limit,
optional<liquidity_pool_id_type> start_id )const
{
return my->get_liquidity_pools_by_asset_a(
asset_symbol_or_id,
limit,
start_id );
}

vector<liquidity_pool_object> database_api_impl::get_liquidity_pools_by_asset_a(
std::string asset_symbol_or_id,
optional<uint32_t> limit,
optional<liquidity_pool_id_type> start_id )const
{
return get_liquidity_pools_by_asset_x<by_asset_a>(
asset_symbol_or_id,
limit,
start_id );
}

vector<liquidity_pool_object> database_api::get_liquidity_pools_by_asset_b(
std::string asset_symbol_or_id,
optional<uint32_t> limit,
optional<liquidity_pool_id_type> start_id )const
{
return my->get_liquidity_pools_by_asset_b(
asset_symbol_or_id,
limit,
start_id );
}

vector<liquidity_pool_object> database_api_impl::get_liquidity_pools_by_asset_b(
std::string asset_symbol_or_id,
optional<uint32_t> limit,
optional<liquidity_pool_id_type> start_id )const
{
return get_liquidity_pools_by_asset_x<by_asset_b>(
asset_symbol_or_id,
limit,
start_id );
}

vector<liquidity_pool_object> database_api::get_liquidity_pools_by_both_assets(
std::string asset_symbol_or_id_a,
std::string asset_symbol_or_id_b,
optional<uint32_t> limit,
optional<liquidity_pool_id_type> start_id )const
{
return my->get_liquidity_pools_by_both_assets(
asset_symbol_or_id_a,
asset_symbol_or_id_b,
limit,
start_id );
}

vector<liquidity_pool_object> database_api_impl::get_liquidity_pools_by_both_assets(
std::string asset_symbol_or_id_a,
std::string asset_symbol_or_id_b,
optional<uint32_t> olimit,
optional<liquidity_pool_id_type> ostart_id )const
{
uint32_t limit = olimit.valid() ? *olimit : 101;

FC_ASSERT( _app_options, "Internal error" );
const auto configured_limit = _app_options->api_limit_get_liquidity_pools;
FC_ASSERT( limit <= configured_limit,
"limit can not be greater than ${configured_limit}",
("configured_limit", configured_limit) );

vector<liquidity_pool_object> results;

asset_id_type asset_id_a = get_asset_from_string(asset_symbol_or_id_a)->id;
asset_id_type asset_id_b = get_asset_from_string(asset_symbol_or_id_b)->id;
if( asset_id_a > asset_id_b )
std::swap( asset_id_a, asset_id_b );

liquidity_pool_id_type start_id = ostart_id.valid() ? *ostart_id : liquidity_pool_id_type();

const auto& idx = _db.get_index_type<liquidity_pool_index>().indices().get<by_asset_ab>();
auto lower_itr = idx.lower_bound( std::make_tuple( asset_id_a, asset_id_b, start_id ) );
auto upper_itr = idx.upper_bound( std::make_tuple( asset_id_a, asset_id_b ) );

results.reserve( limit );
uint32_t count = 0;
for ( ; lower_itr != upper_itr && count < limit; ++lower_itr, ++count)
{
results.emplace_back( *lower_itr );
}

return results;
}

vector<optional<liquidity_pool_object>> database_api::get_liquidity_pools_by_share_asset(
const vector<std::string>& asset_symbols_or_ids,
optional<bool> subscribe )const
{
return my->get_liquidity_pools_by_share_asset(
asset_symbols_or_ids,
subscribe );
}

vector<optional<liquidity_pool_object>> database_api_impl::get_liquidity_pools_by_share_asset(
const vector<std::string>& asset_symbols_or_ids,
optional<bool> subscribe )const
{
FC_ASSERT( _app_options, "Internal error" );
const auto configured_limit = _app_options->api_limit_get_liquidity_pools;
FC_ASSERT( asset_symbols_or_ids.size() <= configured_limit,
"size of the querying list can not be greater than ${configured_limit}",
("configured_limit", configured_limit) );

bool to_subscribe = get_whether_to_subscribe( subscribe );
vector<optional<liquidity_pool_object>> result; result.reserve(asset_symbols_or_ids.size());
std::transform(asset_symbols_or_ids.begin(), asset_symbols_or_ids.end(), std::back_inserter(result),
[this,to_subscribe](std::string id_or_name) -> optional<liquidity_pool_object> {

const asset_object* asset_obj = get_asset_from_string( id_or_name, false );
if( asset_obj == nullptr || !asset_obj->is_liquidity_pool_share_asset() )
return {};
const liquidity_pool_object& lp_obj = (*asset_obj->for_liquidity_pool)(_db);
if( to_subscribe )
subscribe_to_item( lp_obj.id );
return lp_obj;
});
return result;
}

//////////////////////////////////////////////////////////////////////
// //
// Witnesses //
Expand Down
57 changes: 57 additions & 0 deletions libraries/app/database_api_impl.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,24 @@ class database_api_impl : public std::enable_shared_from_this<database_api_impl>
int64_t start, fc::time_point_sec stop,
unsigned limit = 100 )const;

// Liquidity pools
vector<liquidity_pool_object> get_liquidity_pools_by_asset_a(
std::string asset_symbol_or_id,
optional<uint32_t> limit = 101,
optional<liquidity_pool_id_type> start_id = optional<liquidity_pool_id_type>() )const;
vector<liquidity_pool_object> get_liquidity_pools_by_asset_b(
std::string asset_symbol_or_id,
optional<uint32_t> limit = 101,
optional<liquidity_pool_id_type> start_id = optional<liquidity_pool_id_type>() )const;
vector<liquidity_pool_object> get_liquidity_pools_by_both_assets(
std::string asset_symbol_or_id_a,
std::string asset_symbol_or_id_b,
optional<uint32_t> limit = 101,
optional<liquidity_pool_id_type> start_id = optional<liquidity_pool_id_type>() )const;
vector<optional<liquidity_pool_object>> get_liquidity_pools_by_share_asset(
const vector<std::string>& asset_symbols_or_ids,
optional<bool> subscribe = optional<bool>() )const;

// Witnesses
vector<optional<witness_object>> get_witnesses(const vector<witness_id_type>& witness_ids)const;
fc::optional<witness_object> get_witness_by_account(const std::string account_id_or_name)const;
Expand Down Expand Up @@ -239,6 +257,45 @@ class database_api_impl : public std::enable_shared_from_this<database_api_impl>
vector<limit_order_object> get_limit_orders( const asset_id_type a, const asset_id_type b,
const uint32_t limit )const;

////////////////////////////////////////////////
// Liquidity pools
////////////////////////////////////////////////

// template function to reduce duplicate code
template <typename X>
vector<liquidity_pool_object> get_liquidity_pools_by_asset_x(
std::string asset_symbol_or_id,
optional<uint32_t> olimit,
optional<liquidity_pool_id_type> ostart_id )const
{
uint32_t limit = olimit.valid() ? *olimit : 101;

FC_ASSERT( _app_options, "Internal error" );
const auto configured_limit = _app_options->api_limit_get_liquidity_pools;
FC_ASSERT( limit <= configured_limit,
"limit can not be greater than ${configured_limit}",
("configured_limit", configured_limit) );

vector<liquidity_pool_object> results;

const asset_id_type asset_id = get_asset_from_string(asset_symbol_or_id)->id;

liquidity_pool_id_type start_id = ostart_id.valid() ? *ostart_id : liquidity_pool_id_type();

const auto& idx = _db.get_index_type<liquidity_pool_index>().indices().get<X>();
auto lower_itr = idx.lower_bound( std::make_tuple( asset_id, start_id ) );
auto upper_itr = idx.upper_bound( asset_id );

results.reserve( limit );
uint32_t count = 0;
for ( ; lower_itr != upper_itr && count < limit; ++lower_itr, ++count)
{
results.emplace_back( *lower_itr );
}

return results;
}

////////////////////////////////////////////////
// Subscription
////////////////////////////////////////////////
Expand Down
1 change: 1 addition & 0 deletions libraries/app/include/graphene/app/application.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ namespace graphene { namespace app {
uint64_t api_limit_get_trade_history_by_sequence = 100;
uint64_t api_limit_get_withdraw_permissions_by_giver = 101;
uint64_t api_limit_get_withdraw_permissions_by_recipient = 101;
uint64_t api_limit_get_liquidity_pools = 101;
};

class application
Expand Down
83 changes: 83 additions & 0 deletions libraries/app/include/graphene/app/database_api.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include <graphene/chain/chain_property_object.hpp>
#include <graphene/chain/committee_member_object.hpp>
#include <graphene/chain/confidential_object.hpp>
#include <graphene/chain/liquidity_pool_object.hpp>
#include <graphene/chain/operation_history_object.hpp>
#include <graphene/chain/worker_object.hpp>
#include <graphene/chain/witness_object.hpp>
Expand Down Expand Up @@ -118,6 +119,7 @@ class database_api
* - lookup_accounts
* - get_full_accounts
* - get_htlc
* - get_liquidity_pools_by_share_asset
*
* Note: auto-subscription is enabled by default
*
Expand Down Expand Up @@ -625,6 +627,81 @@ class database_api
unsigned limit = 100 )const;


/////////////////////
// Liquidity pools //
/////////////////////

/**
* @brief Get a list of liquidity pools by the symbol or ID of the first asset in the pool
* @param asset_symbol_or_id symbol name or ID of the asset
* @param limit The limitation of items each query can fetch, not greater than a configured value
* @param start_id Start liquidity pool id, fetch pools whose IDs are greater than or equal to this ID
* @return The liquidity pools
*
* @note
* 1. if @p asset_symbol_or_id cannot be tied to an asset, an error will be returned
* 2. @p limit can be omitted or be null, if so the default value 101 will be used
* 3. @p start_id can be omitted or be null, if so the api will return the "first page" of pools
* 4. can only omit one or more arguments in the end of the list, but not one or more in the middle
*/
vector<liquidity_pool_object> get_liquidity_pools_by_asset_a(
std::string asset_symbol_or_id,
optional<uint32_t> limit = 101,
optional<liquidity_pool_id_type> start_id = optional<liquidity_pool_id_type>() )const;

/**
* @brief Get a list of liquidity pools by the symbol or ID of the second asset in the pool
* @param asset_symbol_or_id symbol name or ID of the asset
* @param limit The limitation of items each query can fetch, not greater than a configured value
* @param start_id Start liquidity pool id, fetch pools whose IDs are greater than or equal to this ID
* @return The liquidity pools
*
* @note
* 1. if @p asset_symbol_or_id cannot be tied to an asset, an error will be returned
* 2. @p limit can be omitted or be null, if so the default value 101 will be used
* 3. @p start_id can be omitted or be null, if so the api will return the "first page" of pools
* 4. can only omit one or more arguments in the end of the list, but not one or more in the middle
*/
vector<liquidity_pool_object> get_liquidity_pools_by_asset_b(
std::string asset_symbol_or_id,
optional<uint32_t> limit = 101,
optional<liquidity_pool_id_type> start_id = optional<liquidity_pool_id_type>() )const;

/**
* @brief Get a list of liquidity pools by the symbols or IDs of the two assets in the pool
* @param asset_symbol_or_id_a symbol name or ID of one asset
* @param asset_symbol_or_id_b symbol name or ID of the other asset
* @param limit The limitation of items each query can fetch, not greater than a configured value
* @param start_id Start liquidity pool id, fetch pools whose IDs are greater than or equal to this ID
* @return The liquidity pools
*
* @note
* 1. if @p asset_symbol_or_id_a or @p asset_symbol_or_id_b cannot be tied to an asset,
* an error will be returned
* 2. @p limit can be omitted or be null, if so the default value 101 will be used
* 3. @p start_id can be omitted or be null, if so the api will return the "first page" of pools
* 4. can only omit one or more arguments in the end of the list, but not one or more in the middle
*/
vector<liquidity_pool_object> get_liquidity_pools_by_both_assets(
std::string asset_symbol_or_id_a,
std::string asset_symbol_or_id_b,
optional<uint32_t> limit = 101,
optional<liquidity_pool_id_type> start_id = optional<liquidity_pool_id_type>() )const;

/**
* @brief Get a list of liquidity pools by their share asset symbols or IDs
* @param asset_symbols_or_ids symbol names or IDs of the share assets
* @param subscribe @a true to subscribe to the queried objects; @a false to not subscribe;
* @a null to subscribe or not subscribe according to current auto-subscription setting
* (see @ref set_auto_subscription)
* @return The liquidity pools that the assets are for
*
* @note if an asset in the list can not be found or is not a share asset of any liquidity pool,
* the corresponding data in the returned list is null.
*/
vector<optional<liquidity_pool_object>> get_liquidity_pools_by_share_asset(
const vector<std::string>& asset_symbols_or_ids,
optional<bool> subscribe = optional<bool>() )const;

///////////////
// Witnesses //
Expand Down Expand Up @@ -996,6 +1073,12 @@ FC_API(graphene::app::database_api,
(get_trade_history)
(get_trade_history_by_sequence)

// Liquidity pools
(get_liquidity_pools_by_asset_a)
(get_liquidity_pools_by_asset_b)
(get_liquidity_pools_by_both_assets)
(get_liquidity_pools_by_share_asset)

// Witnesses
(get_witnesses)
(get_witness_by_account)
Expand Down
1 change: 1 addition & 0 deletions libraries/chain/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ add_library( graphene_chain
exceptions.cpp

evaluator.cpp
liquidity_pool_evaluator.cpp
balance_evaluator.cpp
account_evaluator.cpp
assert_evaluator.cpp
Expand Down
16 changes: 14 additions & 2 deletions libraries/chain/asset_evaluator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,8 @@ void_result asset_issue_evaluator::do_evaluate( const asset_issue_operation& o )
FC_ASSERT( o.issuer == a.issuer );
FC_ASSERT( !a.is_market_issued(), "Cannot manually issue a market-issued asset." );

FC_ASSERT( !a.is_liquidity_pool_share_asset(), "Cannot manually issue a liquidity pool share asset." );

FC_ASSERT( a.can_create_new_supply(), "Can not create new supply" );

to_account = &o.issue_to_account(d);
Expand Down Expand Up @@ -324,11 +326,21 @@ void_result asset_reserve_evaluator::do_evaluate( const asset_reserve_operation&
("sym", a.symbol)
);

from_account = &o.payer(d);
from_account = fee_paying_account;
FC_ASSERT( is_authorized_asset( d, *from_account, a ) );

asset_dyn_data = &a.dynamic_asset_data_id(d);
FC_ASSERT( (asset_dyn_data->current_supply - o.amount_to_reserve.amount) >= 0 );
if( !a.is_liquidity_pool_share_asset() )
{
FC_ASSERT( asset_dyn_data->current_supply >= o.amount_to_reserve.amount,
"Can not reserve an amount that is more than the current supply" );
}
else
{
FC_ASSERT( asset_dyn_data->current_supply > o.amount_to_reserve.amount,
"The asset is a liquidity pool share asset thus can only reserve an amount "
"that is less than the current supply" );
}

return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }
Expand Down
Loading