提交 b4ea9ec1 编写于 作者: D Daniel Larimer

progress on margin

上级 9eb12ffd
......@@ -185,6 +185,27 @@ namespace eosio {
return result;
}
friend extended_asset operator - ( const extended_asset& a, const extended_asset& b ) {
eosio_assert( a.symbol == b.symbol, "type mismatch" );
eosio_assert( a.contract == b.contract, "type mismatch" );
extended_asset result( asset(a.amount - b.amount, a.symbol), a.contract );
if( b.amount > 0 )
eosio_assert( a.amount > result.amount, "underflow" );
if( b.amount < 0 )
eosio_assert( a.amount < result.amount, "overflow" );
return result;
}
friend extended_asset operator + ( const extended_asset& a, const extended_asset& b ) {
eosio_assert( a.symbol == b.symbol, "type mismatch" );
eosio_assert( a.contract == b.contract, "type mismatch" );
extended_asset result( asset(a.amount + b.amount, a.symbol), a.contract );
return result;
}
EOSLIB_SERIALIZE( extended_asset, (amount)(symbol)(contract) )
};
......
......@@ -291,10 +291,11 @@ inline datastream<Stream>& operator>>(datastream<Stream>& ds, int64_t& d) {
* @param d value to serialize
*/
template<typename Stream>
inline datastream<Stream>& operator<<(datastream<Stream>& ds, const uint64_t d) {
inline datastream<Stream>& operator<<(datastream<Stream>& ds, const uint64_t& d) {
ds.write( (const char*)&d, sizeof(d) );
return ds;
}
/**
* Deserialize a uint64_t from a stream
* @brief Deserialize a uint64_t
......@@ -307,6 +308,18 @@ inline datastream<Stream>& operator>>(datastream<Stream>& ds, uint64_t& d) {
return ds;
}
template<typename Stream>
inline datastream<Stream>& operator<<(datastream<Stream>& ds, const double& d) {
ds.write( (const char*)&d, sizeof(d) );
return ds;
}
template<typename Stream>
inline datastream<Stream>& operator>>(datastream<Stream>& ds, double& d) {
ds.read((char*)&d, sizeof(d) );
return ds;
}
/**
* Serialize a int16_t into a stream
* @brief Serialize a int16_t
......
......@@ -7,6 +7,17 @@ namespace eosio {
typedef double real_type;
using boost::container::flat_map;
struct margin_state {
extended_asset total_lendable;
extended_asset total_lent;
double least_collateralized = 0;
extended_asset interest_pool;
double interest_shares = 0;
EOSLIB_SERIALIZE( margin_state, (total_lendable)(total_lent)(least_collateralized)(interest_pool)(interest_shares) )
};
struct exchange_state {
account_name manager;
extended_asset supply;
......@@ -16,7 +27,9 @@ namespace eosio {
extended_asset balance;
uint32_t weight = 500;
EOSLIB_SERIALIZE( connector, (balance)(weight) )
margin_state peer_margin; /// peer_connector collateral lending balance
EOSLIB_SERIALIZE( connector, (balance)(weight)(peer_margin) )
};
connector base;
......@@ -120,6 +133,28 @@ namespace eosio {
currency _excurrencies;
public:
/**
* We calculate a unique scope for each market/borrowed_symbol/collateral_symbol and then
* instantiate a table of margin positions... with in this table each user has exactly
* one position and therefore the owner can serve as the primary key.
*/
struct margin_position {
account_name owner;
extended_asset borrowed;
extended_asset collateral;
double call_price = 0;
uint64_t get_call()const { return uint64_t(call_price); }
uint64_t primary_key()const { return owner; }
EOSLIB_SERIALIZE( margin_position, (owner)(borrowed)(collateral)(call_price) )
};
typedef eosio::multi_index<N(margins), margin_position,
indexed_by<N(callprice), eosio::const_mem_fun<margin_position, uint64_t, &margin_position::get_call> >
> margins;
exchange( account_name self )
:_this_contract(self),_excurrencies(self){}
......@@ -158,6 +193,9 @@ namespace eosio {
EOSLIB_SERIALIZE( withdraw, (from)(quantity) )
};
void on( const withdraw& w ) {
require_auth( w.from );
eosio_assert( w.quantity.amount >= 0, "cannot withdraw negative balance" );
......@@ -165,6 +203,124 @@ namespace eosio {
currency::inline_transfer( _this_contract, w.from, w.quantity, "withdraw" );
}
struct upmargin {
symbol_type market;
account_name borrower;
extended_asset delta_borrow;
extended_asset delta_collateral;
EOSLIB_SERIALIZE( upmargin, (market)(borrower)(delta_borrow)(delta_collateral) )
};
void margin_call( exchange_state& state, exchange_state::connector& c, margins& marginstable ) {
auto price_idx = marginstable.get_index<N(callprice)>();
auto pos = price_idx.begin();
if( pos == price_idx.end() )
return;
auto receipt = convert( state, pos->collateral, pos->borrowed.get_extended_symbol() );
eosio_assert( receipt.amount >= pos->borrowed.amount, "programmer error: insufficient collateral to cover" );/// VERY BAD, SHOULD NOT HAPPEN
auto change_debt = receipt - pos->borrowed;
auto change_collat = convert( state, change_debt, pos->collateral.get_extended_symbol() );
adjust_balance( pos->owner, change_collat );
c.peer_margin.total_lent.amount -= pos->borrowed.amount;
price_idx.erase(pos);
pos = price_idx.begin();
if( pos != price_idx.end() )
c.peer_margin.least_collateralized = pos->call_price;
else
c.peer_margin.least_collateralized = double(uint64_t(-1));
}
void on( const upmargin& b ) {
require_auth( b.borrower );
auto marketid = b.market.name();
margins base_margins( _this_contract, marketid );
margins quote_margins( _this_contract, marketid << 1 );
markets market_table( _this_contract, marketid );
auto market_state = market_table.find( marketid );
eosio_assert( market_state != market_table.end(), "unknown market" );
auto state = *market_state;
eosio_assert( b.delta_borrow.amount != 0 || b.delta_collateral.amount != 0, "no effect" );
eosio_assert( b.delta_borrow.get_extended_symbol() != b.delta_collateral.get_extended_symbol(), "invalid args" );
eosio_assert( state.base.balance.get_extended_symbol() == b.delta_borrow.get_extended_symbol() ||
state.quote.balance.get_extended_symbol() == b.delta_borrow.get_extended_symbol(),
"invalid asset for market" );
eosio_assert( state.base.balance.get_extended_symbol() == b.delta_collateral.get_extended_symbol() ||
state.quote.balance.get_extended_symbol() == b.delta_collateral.get_extended_symbol(),
"invalid asset for market" );
auto adjust_margin = [&b]( exchange_state::connector& c, margins& mtable ) {
margin_position temp_pos;
auto existing = mtable.find( b.borrower );
if( existing == mtable.end() ) {
eosio_assert( b.delta_borrow.amount > 0, "borrow neg" );
eosio_assert( b.delta_collateral.amount > 0, "collat neg" );
temp_pos.owner = b.borrower;
temp_pos.borrowed = b.delta_borrow;
temp_pos.collateral = b.delta_collateral;
temp_pos.call_price = double( temp_pos.collateral.amount ) / temp_pos.borrowed.amount;
} else {
temp_pos = *existing;
temp_pos.borrowed += b.delta_borrow;
temp_pos.collateral += b.delta_borrow;
eosio_assert( temp_pos.borrowed.amount > 0, "neg borrowed" );
eosio_assert( temp_pos.collateral.amount > 0, "neg collateral" );
if( temp_pos.borrowed.amount > 0 )
temp_pos.call_price = double( temp_pos.collateral.amount ) / temp_pos.borrowed.amount;
else
temp_pos.call_price = double( uint64_t(-1) );
}
c.peer_margin.total_lent += b.delta_borrow;
auto least = mtable.begin();
if( least == existing ) ++least;
if( least != mtable.end() )
c.peer_margin.least_collateralized = least->call_price;
if( temp_pos.call_price < c.peer_margin.least_collateralized )
c.peer_margin.least_collateralized = temp_pos.call_price;
};
auto temp_state = state;
margins* mt = nullptr;
auto baseptr = &exchange_state::base;
auto quoteptr = &exchange_state::quote;
auto conptr = quoteptr;
if( b.delta_borrow.get_extended_symbol() == state.base.balance.get_extended_symbol() ) {
mt = &base_margins;
conptr = baseptr;
} else {
mt = &quote_margins;
}
adjust_margin( temp_state.*conptr, *mt );
while( requires_margin_call( temp_state ) ) {
// margin_call( state );
temp_state = state;
adjust_margin( temp_state.*conptr, *mt );
}
adjust_margin( state.*conptr, *mt );
/// if this succeeds then the borrower will see their balances adjusted accordingly,
/// if they don't have sufficient balance to either fund the collateral or pay off the
/// debt then this will fail before we go further.
adjust_balance( b.borrower, b.delta_borrow, "borrowed" );
adjust_balance( b.borrower, -b.delta_collateral, "collateral" );
}
struct trade {
account_name seller;
symbol_type market;
......@@ -176,6 +332,20 @@ namespace eosio {
EOSLIB_SERIALIZE( trade, (seller)(market)(sell)(min_receive)(expire)(fill_or_kill) )
};
bool requires_margin_call( const exchange_state& state, const exchange_state::connector& con ) {
if( con.peer_margin.total_lent.amount > 0 ) {
auto tmp = state;
auto base_total_col = int64_t(con.peer_margin.total_lent.amount * con.peer_margin.least_collateralized);
auto covered = convert( tmp, extended_asset( base_total_col, con.balance.get_extended_symbol()), con.peer_margin.total_lent.get_extended_symbol() );
if( covered.amount <= con.peer_margin.total_lent.amount )
return true;
}
return false;
}
bool requires_margin_call( const exchange_state& state ) {
return requires_margin_call( state, state.base ) || requires_margin_call( state, state.quote );
}
extended_asset convert( exchange_state& state, extended_asset from, extended_symbol to ) {
auto sell_symbol = from.get_extended_symbol();
auto ex_symbol = extended_symbol( state.supply.symbol, _this_contract );
......@@ -217,8 +387,22 @@ namespace eosio {
eosio_assert( market_state != market_table.end(), "unknown market" );
auto state = *market_state;
auto temp = state;
auto output = convert( temp, t.sell, t.min_receive.get_extended_symbol() );
margins base_margins( _this_contract, marketid );
margins quote_margins( _this_contract, marketid << 1 );
while( requires_margin_call( temp ) ) {
if( t.sell.get_extended_symbol() == state.base.balance.get_extended_symbol() ) {
margin_call( state, state.quote, quote_margins );
} else {
margin_call( state, state.base, base_margins );
}
temp = state;
output = convert( temp, t.sell, t.min_receive.get_extended_symbol() );
}
state = temp;
auto output = convert( state, t.sell, t.min_receive.get_extended_symbol() );
print( name(t.seller), " ", t.sell, " => ", output, "\n" );
if( t.min_receive.amount != 0 ) {
......@@ -259,6 +443,7 @@ namespace eosio {
typedef eosio::multi_index<N(exaccounts), exaccount> exaccounts;
/**
* Keep a cache of all accounts tables we access
*/
......@@ -309,6 +494,16 @@ namespace eosio {
s.supply = extended_asset(c.initial_supply, _this_contract);
s.base.balance = c.base_deposit;
s.quote.balance = c.quote_deposit;
s.base.peer_margin.total_lent.symbol = c.base_deposit.symbol;
s.base.peer_margin.total_lent.contract = c.base_deposit.contract;
s.base.peer_margin.total_lendable.symbol = c.base_deposit.symbol;
s.base.peer_margin.total_lendable.contract = c.base_deposit.contract;
s.quote.peer_margin.total_lent.symbol = c.quote_deposit.symbol;
s.quote.peer_margin.total_lent.contract = c.quote_deposit.contract;
s.quote.peer_margin.total_lendable.symbol = c.quote_deposit.symbol;
s.quote.peer_margin.total_lendable.contract = c.quote_deposit.contract;
});
_excurrencies.create_currency( { .issuer = _this_contract,
......
......@@ -24,6 +24,16 @@ using namespace eosio::chain::contracts;
using namespace eosio::testing;
using namespace fc;
struct margin_state {
extended_asset total_lendable;
extended_asset total_lent;
double least_collateralized = 0;
extended_asset interest_pool;
double interest_shares = 0;
};
FC_REFLECT( margin_state, (total_lendable)(total_lent)(least_collateralized)(interest_pool)(interest_shares) )
struct exchange_state {
account_name manager;
extended_asset supply;
......@@ -32,13 +42,14 @@ struct exchange_state {
struct connector {
extended_asset balance;
uint32_t weight = 500;
margin_state peer_margin;
};
connector base;
connector quote;
};
FC_REFLECT( exchange_state::connector, (balance)(weight) );
FC_REFLECT( exchange_state::connector, (balance)(weight)(peer_margin) );
FC_REFLECT( exchange_state, (manager)(supply)(fee)(base)(quote) );
class exchange_tester : public tester {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册