Skip to content

Commit

Permalink
Merge pull request #2261 from abitmore/liquidity-pool
Browse files Browse the repository at this point in the history
Implement liquidity pool
  • Loading branch information
abitmore authored Sep 17, 2020
2 parents a439c43 + 2b1c774 commit e3fb038
Show file tree
Hide file tree
Showing 33 changed files with 2,376 additions and 12 deletions.
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

0 comments on commit e3fb038

Please sign in to comment.