peerplays_migrated/libraries/chain/bond_evaluator.cpp
2015-06-15 15:24:58 -04:00

218 lines
8.2 KiB
C++

/*
* Copyright (c) 2015, Cryptonomex, Inc.
* All rights reserved.
*
* This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and
* the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification,
* are permitted until September 8, 2015, provided that the following conditions are met:
*
* 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/bond_evaluator.hpp>
#include <graphene/chain/bond_object.hpp>
#include <graphene/chain/database.hpp>
namespace graphene { namespace chain {
object_id_type bond_create_offer_evaluator::do_evaluate( const bond_create_offer_operation& op )
{
const auto& d = db();
const auto& creator_account = op.creator( d );
const auto& base_asset = op.collateral_rate.base.asset_id( d );
const auto& quote_asset = op.collateral_rate.quote.asset_id( d );
// TODO: Check asset authorizations and withdrawals
const auto& amount_asset = (op.amount.asset_id == op.collateral_rate.base.asset_id) ? base_asset : quote_asset;
FC_ASSERT( !base_asset.is_transfer_restricted() && !quote_asset.is_transfer_restricted() );
if( base_asset.options.whitelist_markets.size() )
FC_ASSERT( base_asset.options.whitelist_markets.find( quote_asset.id ) != base_asset.options.whitelist_markets.end() );
if( base_asset.options.blacklist_markets.size() )
FC_ASSERT( base_asset.options.blacklist_markets.find( quote_asset.id ) == base_asset.options.blacklist_markets.end() );
FC_ASSERT( d.get_balance( creator_account, amount_asset ) >= op.amount );
return object_id_type();
}
object_id_type bond_create_offer_evaluator::do_apply( const bond_create_offer_operation& op )
{
db().adjust_balance( op.creator, -op.amount );
db().adjust_core_in_orders( op.creator(db()), op.amount );
const auto& offer = db().create<bond_offer_object>( [&]( bond_offer_object& obj )
{
obj.offered_by_account = op.creator;
obj.offer_to_borrow = op.offer_to_borrow;
obj.amount = op.amount;
obj.min_match = op.min_match;
obj.collateral_rate = op.collateral_rate;
obj.min_loan_period_sec = op.min_loan_period_sec;
obj.loan_period_sec = op.loan_period_sec;
obj.interest_apr = op.interest_apr;
} );
return offer.id;
}
void_result bond_cancel_offer_evaluator::do_evaluate( const bond_cancel_offer_operation& op )
{
_offer = &op.offer_id(db());
FC_ASSERT( op.creator == _offer->offered_by_account );
FC_ASSERT( _offer->amount == op.refund );
return void_result();
}
void_result bond_cancel_offer_evaluator::do_apply( const bond_cancel_offer_operation& op )
{
assert( _offer != nullptr );
db().adjust_balance( op.creator, op.refund );
db().adjust_core_in_orders( op.creator(db()), -op.refund );
db().remove( *_offer );
return void_result();
}
object_id_type bond_accept_offer_evaluator::do_evaluate( const bond_accept_offer_operation& op )
{ try {
_offer = &op.offer_id(db());
if( _offer->offer_to_borrow )
FC_ASSERT( op.amount_borrowed.amount >= _offer->min_match );
else
FC_ASSERT( op.amount_collateral.amount >= _offer->min_match );
FC_ASSERT( (op.amount_borrowed / op.amount_collateral == _offer->collateral_rate) ||
(op.amount_collateral / op.amount_borrowed == _offer->collateral_rate) );
return object_id_type();
} FC_CAPTURE_AND_RETHROW((op)) }
object_id_type bond_accept_offer_evaluator::do_apply( const bond_accept_offer_operation& op )
{ try {
if( op.claimer == op.lender )
{
db().adjust_balance( op.lender, -op.amount_borrowed );
}
else // claimer == borrower
{
db().adjust_balance( op.borrower, -op.amount_collateral );
db().adjust_core_in_orders( op.borrower(db()), op.amount_collateral );
}
db().adjust_balance( op.borrower, op.amount_borrowed );
const auto& bond = db().create<bond_object>( [&]( bond_object& obj )
{
obj.borrowed = op.amount_borrowed;
obj.collateral = op.amount_collateral;
obj.borrower = op.borrower;
obj.lender = op.lender;
auto head_time = db().get_dynamic_global_properties().time;
obj.interest_apr = _offer->interest_apr;
obj.start_date = head_time;
obj.due_date = head_time + fc::seconds( _offer->loan_period_sec );
obj.earliest_payoff_date = head_time + fc::seconds( _offer->min_loan_period_sec );
} );
if( _offer->offer_to_borrow && op.amount_borrowed < _offer->amount )
{
db().modify( *_offer, [&]( bond_offer_object& offer ){
offer.amount -= op.amount_borrowed;
});
}
else if( !_offer->offer_to_borrow && op.amount_collateral < _offer->amount )
{
db().modify( *_offer, [&]( bond_offer_object& offer ){
offer.amount -= op.amount_collateral;
});
}
else
{
db().remove( *_offer );
}
return bond.id;
} FC_CAPTURE_AND_RETHROW((op)) }
void_result bond_claim_collateral_evaluator::do_evaluate( const bond_claim_collateral_operation& op )
{
_bond = &op.bond_id(db());
auto head_time = db().get_dynamic_global_properties().time;
FC_ASSERT( head_time > _bond->earliest_payoff_date );
FC_ASSERT( op.collateral_claimed <= _bond->collateral );
if( _bond->borrower == op.claimer )
{
auto elapsed_time = head_time - _bond->start_date;
auto elapsed_days = 1 + elapsed_time.to_seconds() / (60*60*24);
fc::uint128 tmp = _bond->borrowed.amount.value;
tmp *= elapsed_days;
tmp *= _bond->interest_apr;
tmp /= (365 * GRAPHENE_100_PERCENT);
FC_ASSERT( tmp < GRAPHENE_MAX_SHARE_SUPPLY );
_interest_due = asset(tmp.to_uint64(), _bond->borrowed.asset_id);
FC_ASSERT( _interest_due + _bond->borrowed <= op.payoff_amount );
auto total_debt = _interest_due + _bond->borrowed;
fc::uint128 max_claim = _bond->collateral.amount.value;
max_claim *= op.payoff_amount.amount.value;
max_claim /= total_debt.amount.value;
FC_ASSERT( op.collateral_claimed.amount.value == max_claim.to_uint64() );
}
else
{
FC_ASSERT( _bond->lender == op.claimer );
FC_ASSERT( head_time > _bond->due_date );
FC_ASSERT( _bond->collateral == op.collateral_claimed );
FC_ASSERT( op.payoff_amount == asset(0,_bond->borrowed.asset_id ) );
}
return void_result();
}
void_result bond_claim_collateral_evaluator::do_apply( const bond_claim_collateral_operation& op )
{
assert( _bond != nullptr );
const account_object& claimer = op.claimer(db());
db().adjust_core_in_orders( _bond->borrower(db()), -op.collateral_claimed );
if( op.payoff_amount.amount > 0 )
{
db().adjust_balance( claimer, -op.payoff_amount );
db().adjust_balance( op.lender, op.payoff_amount );
}
db().adjust_balance( claimer, op.collateral_claimed );
if( op.collateral_claimed == _bond->collateral )
db().remove(*_bond);
else
db().modify( *_bond, [&]( bond_object& bond ){
bond.borrowed -= op.payoff_amount + _interest_due;
bond.collateral -= op.collateral_claimed;
bond.start_date = db().get_dynamic_global_properties().time;
});
return void_result();
}
} } // graphene::chain