BitShares-Core  6.1.0
BitShares blockchain implementation and command-line interface software
liquidity_pool_evaluator.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020 Abit More, and contributors.
3  *
4  * The MIT License
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
26 
28 
31 #include <graphene/chain/hardfork.hpp>
33 
35 
36 namespace graphene { namespace chain {
37 
39 { try {
40  const database& d = db();
41  const auto block_time = d.head_block_time();
42 
43  FC_ASSERT( HARDFORK_LIQUIDITY_POOL_PASSED(block_time), "Not allowed until the LP hardfork" );
44 
45  op.asset_a(d); // Make sure it exists
46  op.asset_b(d); // Make sure it exists
47  _share_asset = &op.share_asset(d);
48 
50  "Only the asset owner can set an asset as the share asset of a liquidity pool" );
51 
53  "Can not specify a market-issued asset as the share asset of a liquidity pool" );
54 
56  "The share asset is already bound to another liquidity pool" );
57 
59  "Current supply of the share asset needs to be zero" );
60 
61  return void_result();
62 } FC_CAPTURE_AND_RETHROW( (op) ) }
63 
65 { try {
66  database& d = db();
68 
69  const auto& new_liquidity_pool_object = d.create<liquidity_pool_object>([&op](liquidity_pool_object& obj){
70  obj.asset_a = op.asset_a;
71  obj.asset_b = op.asset_b;
72  obj.share_asset = op.share_asset;
73  obj.taker_fee_percent = op.taker_fee_percent;
74  obj.withdrawal_fee_percent = op.withdrawal_fee_percent;
75  });
76  result.new_objects.insert( new_liquidity_pool_object.id );
77 
78  result.updated_objects.insert( _share_asset->id );
79  d.modify( *_share_asset, [&new_liquidity_pool_object](asset_object& ao) {
80  ao.for_liquidity_pool = new_liquidity_pool_object.id;
81  });
82 
83  return result;
84 } FC_CAPTURE_AND_RETHROW( (op) ) }
85 
87 { try {
88  const database& d = db();
89 
90  _pool = &op.pool(d);
91 
92  FC_ASSERT( _pool->balance_a == 0 && _pool->balance_b == 0, "Can not delete a non-empty pool" );
93 
94  _share_asset = &_pool->share_asset(d);
95 
96  FC_ASSERT( _share_asset->issuer == op.account, "The account is not the owner of the liquidity pool" );
97 
98  return void_result();
99 } FC_CAPTURE_AND_RETHROW( (op) ) }
100 
102 { try {
103  database& d = db();
105 
106  result.updated_objects.insert( _share_asset->id );
107  d.modify( *_share_asset, [](asset_object& ao) {
109  });
110 
111  result.removed_objects.insert( _pool->id );
112  d.remove( *_pool );
113 
114  return result;
115 } FC_CAPTURE_AND_RETHROW( (op) ) }
116 
118 { try {
119  const database& d = db();
120 
121  _pool = &op.pool(d);
122 
123  FC_ASSERT( op.amount_a.asset_id == _pool->asset_a, "Asset type A mismatch" );
124  FC_ASSERT( op.amount_b.asset_id == _pool->asset_b, "Asset type B mismatch" );
125 
126  FC_ASSERT( (_pool->balance_a == 0) == (_pool->balance_b == 0), "Internal error" );
127 
128  const asset_object& share_asset_obj = _pool->share_asset(d);
129 
130  FC_ASSERT( share_asset_obj.can_create_new_supply(), "Can not create new supply for the share asset" );
131 
132  if( _pool->balance_a == 0 ) // which implies that _pool->balance_b == 0
133  {
134  FC_ASSERT( share_asset_obj.issuer == op.account, "The initial deposit can only be done by the pool owner" );
135  }
136 
137  _share_asset_dyn_data = &share_asset_obj.dynamic_data(d);
138 
139  FC_ASSERT( (_pool->balance_a == 0) == (_share_asset_dyn_data->current_supply == 0), "Internal error" );
140 
141  FC_ASSERT( _share_asset_dyn_data->current_supply < share_asset_obj.options.max_supply,
142  "Can not create new supply for the share asset" );
143 
144  FC_ASSERT( is_authorized_asset( d, *fee_paying_account, share_asset_obj ),
145  "The account is unauthorized by the share asset" );
146  FC_ASSERT( is_authorized_asset( d, *fee_paying_account, _pool->asset_a(d) ),
147  "The account is unauthorized by asset A" );
148  FC_ASSERT( is_authorized_asset( d, *fee_paying_account, _pool->asset_b(d) ),
149  "The account is unauthorized by asset B" );
150 
151  if( _pool->balance_a == 0 )
152  {
153  share_type share_amount = std::max( op.amount_a.amount.value, op.amount_b.amount.value );
154  FC_ASSERT( share_amount <= share_asset_obj.options.max_supply,
155  "For initial deposit, each amount of the two assets in the pool should not be greater than "
156  "the maximum supply of the share asset" );
157  _pool_receives_a = op.amount_a;
158  _pool_receives_b = op.amount_b;
159  _account_receives = asset( share_amount, _pool->share_asset );
160  }
161  else
162  {
163  share_type max_new_supply = share_asset_obj.options.max_supply - _share_asset_dyn_data->current_supply;
164  fc::uint128_t max128( max_new_supply.value );
165  fc::uint128_t supply128( _share_asset_dyn_data->current_supply.value );
166  fc::uint128_t new_supply_if_a = supply128 * op.amount_a.amount.value / _pool->balance_a.value;
167  fc::uint128_t new_supply_if_b = supply128 * op.amount_b.amount.value / _pool->balance_b.value;
168  fc::uint128_t new_supply = std::min( { new_supply_if_a, new_supply_if_b, max128 } );
169 
170  FC_ASSERT( new_supply > 0, "Aborting due to zero outcome" );
171 
172  fc::uint128_t a128 = ( new_supply * _pool->balance_a.value + supply128 - 1 ) / supply128; // round up
173  FC_ASSERT( a128 <= fc::uint128_t( op.amount_a.amount.value ), "Internal error" );
174  _pool_receives_a = asset( static_cast<int64_t>( a128 ), _pool->asset_a );
175 
176  fc::uint128_t b128 = ( new_supply * _pool->balance_b.value + supply128 - 1 ) / supply128; // round up
177  FC_ASSERT( b128 <= fc::uint128_t( op.amount_b.amount.value ), "Internal error" );
178  _pool_receives_b = asset( static_cast<int64_t>( b128 ), _pool->asset_b );
179 
180  _account_receives = asset( static_cast<int64_t>( new_supply ), _pool->share_asset );
181  }
182 
183  return void_result();
184 } FC_CAPTURE_AND_RETHROW( (op) ) }
185 
188 { try {
189  database& d = db();
191 
192  d.adjust_balance( op.account, -_pool_receives_a );
193  d.adjust_balance( op.account, -_pool_receives_b );
194  d.adjust_balance( op.account, _account_receives );
195 
196  d.modify( *_pool, [this]( liquidity_pool_object& lpo ){
197  lpo.balance_a += _pool_receives_a.amount;
198  lpo.balance_b += _pool_receives_b.amount;
199  lpo.update_virtual_value();
200  });
201 
202  d.modify( *_share_asset_dyn_data, [this]( asset_dynamic_data_object& data ){
203  data.current_supply += _account_receives.amount;
204  });
205 
206  FC_ASSERT( _pool->balance_a > 0 && _pool->balance_b > 0, "Internal error" );
207  FC_ASSERT( _share_asset_dyn_data->current_supply > 0, "Internal error" );
208 
209  result.paid.emplace_back( _pool_receives_a );
210  result.paid.emplace_back( _pool_receives_b );
211  result.received.emplace_back( _account_receives );
212 
213  return result;
214 } FC_CAPTURE_AND_RETHROW( (op) ) }
215 
217 { try {
218  const database& d = db();
219 
220  _pool = &op.pool(d);
221 
222  FC_ASSERT( op.share_amount.asset_id == _pool->share_asset, "Share asset type mismatch" );
223 
224  FC_ASSERT( _pool->balance_a > 0 && _pool->balance_b > 0, "The pool has not been initialized" );
225 
226  const asset_object& share_asset_obj = _pool->share_asset(d);
227 
228  FC_ASSERT( is_authorized_asset( d, *fee_paying_account, share_asset_obj ),
229  "The account is unauthorized by the share asset" );
230  FC_ASSERT( is_authorized_asset( d, *fee_paying_account, _pool->asset_a(d) ),
231  "The account is unauthorized by asset A" );
232  FC_ASSERT( is_authorized_asset( d, *fee_paying_account, _pool->asset_b(d) ),
233  "The account is unauthorized by asset B" );
234 
235  _share_asset_dyn_data = &share_asset_obj.dynamic_data(d);
236 
237  FC_ASSERT( _share_asset_dyn_data->current_supply >= op.share_amount.amount,
238  "Can not withdraw an amount that is more than the current supply" );
239 
240  if( _share_asset_dyn_data->current_supply == op.share_amount.amount )
241  {
242  _pool_pays_a = asset( _pool->balance_a, _pool->asset_a );
243  _pool_pays_b = asset( _pool->balance_b, _pool->asset_b );
244  _fee_a = asset( 0, _pool->asset_a );
245  _fee_b = asset( 0, _pool->asset_b );
246  }
247  else
248  {
249  fc::uint128_t share128( op.share_amount.amount.value );
250  fc::uint128_t a128 = share128 * _pool->balance_a.value / _share_asset_dyn_data->current_supply.value;
251  FC_ASSERT( a128 < fc::uint128_t( _pool->balance_a.value ), "Internal error" );
252  fc::uint128_t fee_a = a128 * _pool->withdrawal_fee_percent / GRAPHENE_100_PERCENT;
253  FC_ASSERT( fee_a <= a128, "Withdrawal fee percent of the pool is too high" );
254  a128 -= fee_a;
255  fc::uint128_t b128 = share128 * _pool->balance_b.value / _share_asset_dyn_data->current_supply.value;
256  FC_ASSERT( b128 < fc::uint128_t( _pool->balance_b.value ), "Internal error" );
257  fc::uint128_t fee_b = b128 * _pool->withdrawal_fee_percent / GRAPHENE_100_PERCENT;
258  FC_ASSERT( fee_b <= b128, "Withdrawal fee percent of the pool is too high" );
259  b128 -= fee_b;
260  FC_ASSERT( a128 > 0 || b128 > 0, "Aborting due to zero outcome" );
261  _pool_pays_a = asset( static_cast<int64_t>( a128 ), _pool->asset_a );
262  _pool_pays_b = asset( static_cast<int64_t>( b128 ), _pool->asset_b );
263  _fee_a = asset( static_cast<int64_t>( fee_a ), _pool->asset_a );
264  _fee_b = asset( static_cast<int64_t>( fee_b ), _pool->asset_b );
265  }
266 
267  return void_result();
268 } FC_CAPTURE_AND_RETHROW( (op) ) }
269 
272 { try {
273  database& d = db();
275 
276  d.adjust_balance( op.account, -op.share_amount );
277 
278  if( _pool_pays_a.amount > 0 )
279  d.adjust_balance( op.account, _pool_pays_a );
280  if( _pool_pays_b.amount > 0 )
281  d.adjust_balance( op.account, _pool_pays_b );
282 
283  d.modify( *_share_asset_dyn_data, [&op]( asset_dynamic_data_object& data ){
285  });
286 
287  d.modify( *_pool, [this]( liquidity_pool_object& lpo ){
288  lpo.balance_a -= _pool_pays_a.amount;
289  lpo.balance_b -= _pool_pays_b.amount;
290  lpo.update_virtual_value();
291  });
292 
293  FC_ASSERT( (_pool->balance_a == 0) == (_pool->balance_b == 0), "Internal error" );
294  FC_ASSERT( (_pool->balance_a == 0) == (_share_asset_dyn_data->current_supply == 0), "Internal error" );
295 
296  result.paid.emplace_back( op.share_amount );
297  result.received.emplace_back( _pool_pays_a );
298  result.received.emplace_back( _pool_pays_b );
299  result.fees.emplace_back( _fee_a );
300  result.fees.emplace_back( _fee_b );
301 
302  return result;
303 } FC_CAPTURE_AND_RETHROW( (op) ) }
304 
306 { try {
307  const database& d = db();
308 
309  _pool = &op.pool(d);
310 
311  FC_ASSERT( _pool->balance_a > 0 && _pool->balance_b > 0, "The pool has not been initialized" );
312 
313  FC_ASSERT( ( op.amount_to_sell.asset_id == _pool->asset_a && op.min_to_receive.asset_id == _pool->asset_b )
314  || ( op.amount_to_sell.asset_id == _pool->asset_b && op.min_to_receive.asset_id == _pool->asset_a ),
315  "Asset type mismatch" );
316 
317 
318  const asset_object& asset_obj_a = _pool->asset_a(d);
319  FC_ASSERT( is_authorized_asset( d, *fee_paying_account, asset_obj_a ),
320  "The account is unauthorized by asset A" );
321 
322  const asset_object& asset_obj_b = _pool->asset_b(d);
323  FC_ASSERT( is_authorized_asset( d, *fee_paying_account, asset_obj_b ),
324  "The account is unauthorized by asset B" );
325 
326  if( HARDFORK_CORE_2350_PASSED( d.head_block_time() ) )
327  {
328  if( !asset_obj_a.options.whitelist_markets.empty() )
329  {
330  FC_ASSERT( asset_obj_a.options.whitelist_markets.find(_pool->asset_b)
331  != asset_obj_a.options.whitelist_markets.end(),
332  "The ${a}:${b} market has not been whitelisted by asset ${a}",
333  ("a", asset_obj_a.symbol) ("b", asset_obj_b.symbol) );
334  }
335  if( !asset_obj_a.options.blacklist_markets.empty() )
336  {
337  FC_ASSERT( asset_obj_a.options.blacklist_markets.find(_pool->asset_b)
338  == asset_obj_a.options.blacklist_markets.end(),
339  "The ${a}:${b} market has been blacklisted by asset ${a}",
340  ("a", asset_obj_a.symbol) ("b", asset_obj_b.symbol) );
341  }
342  if( !asset_obj_b.options.whitelist_markets.empty() )
343  {
344  FC_ASSERT( asset_obj_b.options.whitelist_markets.find(_pool->asset_a)
345  != asset_obj_b.options.whitelist_markets.end(),
346  "The ${a}:${b} market has not been whitelisted by asset ${b}",
347  ("a", asset_obj_a.symbol) ("b", asset_obj_b.symbol) );
348  }
349  if( !asset_obj_b.options.blacklist_markets.empty() )
350  {
351  FC_ASSERT( asset_obj_b.options.blacklist_markets.find(_pool->asset_a)
352  == asset_obj_b.options.blacklist_markets.end(),
353  "The ${a}:${b} market has been blacklisted by asset ${b}",
354  ("a", asset_obj_a.symbol) ("b", asset_obj_b.symbol) );
355  }
356  }
357 
358  _pool_receives_asset = ( op.amount_to_sell.asset_id == _pool->asset_a ? &asset_obj_a : &asset_obj_b );
359 
360  _maker_market_fee = d.calculate_market_fee( *_pool_receives_asset, op.amount_to_sell, true );
361  FC_ASSERT( _maker_market_fee < op.amount_to_sell,
362  "Aborting since the maker market fee of the selling asset is too high" );
363  _pool_receives = op.amount_to_sell - _maker_market_fee;
364 
365  fc::uint128_t delta;
366  if( op.amount_to_sell.asset_id == _pool->asset_a )
367  {
368  share_type new_balance_a = _pool->balance_a + _pool_receives.amount;
369  // round up
370  fc::uint128_t new_balance_b = ( _pool->virtual_value + new_balance_a.value - 1 ) / new_balance_a.value;
371  FC_ASSERT( new_balance_b <= _pool->balance_b, "Internal error" );
372  delta = fc::uint128_t( _pool->balance_b.value ) - new_balance_b;
373  _pool_pays_asset = &asset_obj_b;
374  }
375  else
376  {
377  share_type new_balance_b = _pool->balance_b + _pool_receives.amount;
378  // round up
379  fc::uint128_t new_balance_a = ( _pool->virtual_value + new_balance_b.value - 1 ) / new_balance_b.value;
380  FC_ASSERT( new_balance_a <= _pool->balance_a, "Internal error" );
381  delta = fc::uint128_t( _pool->balance_a.value ) - new_balance_a;
382  _pool_pays_asset = &asset_obj_a;
383  }
384 
385  fc::uint128_t pool_taker_fee = delta * _pool->taker_fee_percent / GRAPHENE_100_PERCENT;
386  FC_ASSERT( pool_taker_fee <= delta, "Taker fee percent of the pool is too high" );
387 
388  _pool_pays = asset( static_cast<int64_t>( delta - pool_taker_fee ), op.min_to_receive.asset_id );
389 
390  _taker_market_fee = d.calculate_market_fee( *_pool_pays_asset, _pool_pays, false );
391  FC_ASSERT( _taker_market_fee <= _pool_pays, "Market fee should not be greater than the amount to receive" );
392  _account_receives = _pool_pays - _taker_market_fee;
393 
394  GRAPHENE_ASSERT( _account_receives.amount >= op.min_to_receive.amount,
395  liquidity_pool_exchange_unfillable_price,
396  "Unable to exchange at expected price" );
397 
398  _pool_taker_fee = asset( static_cast<int64_t>( pool_taker_fee ), op.min_to_receive.asset_id );
399 
400  return void_result();
401 } FC_CAPTURE_AND_RETHROW( (op) ) }
402 
405 { try {
406  database& d = db();
408 
410  d.adjust_balance( op.account, _account_receives );
411 
412  // For _pool_receives_asset, if market fee sharing is enabled,
413  // the share asset owner's registrar and referrer will get the shared maker market fee.
414  // For _pool_pays_asset, if market fee sharing is enabled,
415  // the trader's registrar and referrer will get the shared taker market fee.
416  d.pay_market_fees( &_pool->share_asset(d).issuer(d), *_pool_receives_asset, op.amount_to_sell, true,
417  _maker_market_fee );
418  d.pay_market_fees( fee_paying_account, *_pool_pays_asset, _pool_pays, false, _taker_market_fee );
419 
420  const auto old_virtual_value = _pool->virtual_value;
421  if( op.amount_to_sell.asset_id == _pool->asset_a )
422  {
423  d.modify( *_pool, [this]( liquidity_pool_object& lpo ){
424  lpo.balance_a += _pool_receives.amount;
425  lpo.balance_b -= _pool_pays.amount;
426  lpo.update_virtual_value();
427  });
428  }
429  else
430  {
431  d.modify( *_pool, [this]( liquidity_pool_object& lpo ){
432  lpo.balance_b += _pool_receives.amount;
433  lpo.balance_a -= _pool_pays.amount;
434  lpo.update_virtual_value();
435  });
436  }
437 
438  FC_ASSERT( _pool->balance_a > 0 && _pool->balance_b > 0, "Internal error" );
439  FC_ASSERT( _pool->virtual_value >= old_virtual_value, "Internal error" );
440 
441  result.paid.emplace_back( op.amount_to_sell );
442  result.received.emplace_back( _account_receives );
443  result.fees.emplace_back( _maker_market_fee );
444  result.fees.emplace_back( _taker_market_fee );
445  result.fees.emplace_back( _pool_taker_fee );
446 
447  return result;
448 } FC_CAPTURE_AND_RETHROW( (op) ) }
449 
450 } } // graphene::chain
flat_set< object_id_type > new_objects
Definition: base.hpp:90
asset share_amount
The amount of the share asset to use.
void modify(const T &obj, const Lambda &m)
liquidity_pool_id_type pool
ID of the liquidity pool.
generic_exchange_operation_result do_apply(const liquidity_pool_exchange_operation &op)
liquidity_pool_id_type pool
ID of the liquidity pool.
void adjust_balance(account_id_type account, asset delta)
Adjust a particular account&#39;s balance in a given asset by a delta.
Definition: db_balance.cpp:54
bool is_authorized_asset(const database &d, const account_object &acct, const asset_object &asset_obj)
void_result do_evaluate(const liquidity_pool_withdraw_operation &op)
void_result do_evaluate(const liquidity_pool_exchange_operation &op)
time_point_sec head_block_time() const
Definition: db_getter.cpp:67
account_id_type account
The account who withdraws from the liquidity pool.
tracks the blockchain state in an extensible manner
Definition: database.hpp:70
Definition: api.cpp:48
void_result do_evaluate(const liquidity_pool_delete_operation &op)
asset min_to_receive
The minimum amount of the other asset type to receive.
uint16_t withdrawal_fee_percent
Withdrawal fee percent.
liquidity_pool_id_type pool
ID of the liquidity pool.
const account_object * fee_paying_account
Definition: evaluator.hpp:116
const asset_dynamic_data_object & dynamic_data(const DB &db) const
flat_set< asset_id_type > blacklist_markets
Definition: asset_ops.hpp:86
account_id_type issuer
ID of the account which issued this asset.
optional< liquidity_pool_id_type > for_liquidity_pool
The ID of the liquidity pool if the asset is the share asset of a liquidity pool. ...
generic_exchange_operation_result do_apply(const liquidity_pool_withdraw_operation &op)
asset pay_market_fees(const account_object *seller, const asset_object &recv_asset, const asset &receives, const bool &is_maker, const optional< asset > &calculated_market_fees={})
Definition: db_market.cpp:2205
asset_id_type asset_a
Type of the first asset in the pool.
account_id_type account
The account who exchanges with the liquidity pool.
flat_set< asset_id_type > whitelist_markets
Definition: asset_ops.hpp:84
object_id_type id
Definition: object.hpp:69
#define GRAPHENE_100_PERCENT
Definition: config.hpp:102
liquidity_pool_id_type pool
ID of the liquidity pool.
string symbol
Ticker symbol for this asset, i.e. "USD".
asset_id_type share_asset
Type of the share asset aka the LP token.
void_result do_evaluate(const liquidity_pool_create_operation &op)
#define FC_CAPTURE_AND_RETHROW(...)
Definition: exception.hpp:479
#define FC_ASSERT(TEST,...)
Checks a condition and throws an assert_exception if the test is FALSE.
Definition: exception.hpp:345
share_type balance_a
The balance of the first asset in the pool.
account_id_type account
The account who creates the liquidity pool.
asset amount_to_sell
The amount of one asset type to sell.
tracks the asset information that changes frequentlyBecause the asset_object is very large it doesn&#39;t...
account_id_type account
The account who deposits to the liquidity pool.
void reset()
Definition: optional.hpp:224
flat_set< object_id_type > removed_objects
Definition: base.hpp:92
tracks the parameters of an assetAll assets have a globally unique symbol name that controls how they...
asset calculate_market_fee(const asset_object &trade_asset, const asset &trade_amount, const bool &is_maker) const
Calculate the market fee that is to be taken.
Definition: db_market.cpp:2170
asset_id_type asset_id
Definition: asset.hpp:37
flat_set< object_id_type > updated_objects
Definition: base.hpp:91
share_type balance_b
The balance of the second asset in the pool.
void remove(const object &obj)
#define GRAPHENE_ASSERT(expr, exc_type, FORMAT,...)
Definition: exceptions.hpp:28
bool is_liquidity_pool_share_asset() const
share_type current_supply
The number of shares currently in existence.
asset amount_b
The amount of the second asset to deposit.
account_id_type account
The account who owns the liquidity pool.
asset amount_a
The amount of the first asset to deposit.
const T & create(F &&constructor)
generic_exchange_operation_result do_apply(const liquidity_pool_deposit_operation &op)
void_result do_evaluate(const liquidity_pool_deposit_operation &op)
generic_operation_result do_apply(const liquidity_pool_delete_operation &op)
T value
Definition: safe.hpp:22
generic_operation_result do_apply(const liquidity_pool_create_operation &op)
asset_id_type asset_b
Type of the second asset in the pool.