baocor网络在EOS上实现的智能合约代码:https://github.com/bancorprotocol/contracts_eos
简介
bancor网络最初是用在以太坊上为了实现代币之间自动定价,自由兑换,目前bancor网络也已经运行在eos上,bancor网络实现了eos网络中的代币自动定价和自由兑换,这里的关键是自动定价,自动定价的核心是bancor算法,既然bancor网络是一个网络,组成网络的基本要素就是有节点跟节点之间的连接,节点实现代币的自由定价,节点之间的连接就是代币的转换,那么整个网络就实现了eos中代币的自动定价跟兑换
bancor 网络节点
bancor网络节点实现节点代币跟连接器代币的自动定价,下面给出一个节点组成示意图:
T:节点代币
C1,C2,C3:连接器代币
上述节点示意图中网络节点可以有很多连接器代币(实际当中一般只有1~2个),T为节点代币,T跟C之间的转换就是bancor算法
上述节点对于代币的流向一共可以分3类:
1) 用T购买C,T进入,连接器代币C出去,T===》C
2) 用C购买T,连接器代币C进入,T出去,C===》T
3) 用C1购买C2,连接器代币C1进入,连接器代币C2出去,C1===》C2
- 特殊节点代币
Bancor网络代币(BNT),作为网络代币的枢纽,连接Bancor网络中的所有代币,任意一个节点的连接器代币C中一般都有该代币,这样该节点的其他连接器代币跟节点代币才能在整个bancor网络中流通
示例
打开网站https://eos.bancor.network/,我们从该网站上可以看到已经加入bancor网络的所有代币,那么这些代币就可以通过bancor网络自由兑换,例如可以发现代币EOS跟DICE已经存在于网络中,那么我们用EOS来购买DICE,购买完成之后我们可以在eospark上查看该笔trx,如下:
该trx一共有5个action(这些内联action的完成基于require_recipient)来实现EOS到DICE代币的兑换,我们用如下示意图来展示这个过程,下图的5个步骤依次对应上述5个action
BN:bancor network
C1:eos节点
C2:dice节点
因为不管是C1还是C2它们的节点连接器中都有bnt代币,所以C1中的eos能跟C2中的dice兑换,eos===》bnt的转换由C1完成,bnt===》dice的转换由C2完成,这个2个步骤是关键步骤,它们由BancorConverter::convert函数完成,该函数位于BancorConverter合约中, 该函数是bancor网络中实现代币自由定价跟自由兑换最核心的函数,该函数做的其实就是上述网络节点示意图中代币流向的3个分类
void BancorConverter::convert(name from, eosio::asset quantity, std::string memo, name code) {
eosio_assert(quantity.is_valid(), "invalid quantity");
eosio_assert(quantity.amount != 0, "zero quantity is disallowed");
auto from_amount = quantity.amount / pow(10, quantity.symbol.precision());
auto memo_object = parse_memo(memo);
eosio_assert(memo_object.path.size() > 1, "invalid memo format");
settings settings_table(_self, _self.value);
auto converter_settings = settings_table.get();
eosio_assert(converter_settings.enabled, "converter is disabled");
eosio_assert(converter_settings.network == from, "converter can only receive from network contract");
auto contract_name = name(memo_object.path[0].c_str());
eosio_assert(contract_name == _self, "wrong converter");
auto from_path_currency = quantity.symbol.code().raw();
auto to_path_currency = symbol_code(memo_object.path[1].c_str()).raw();
eosio_assert(from_path_currency != to_path_currency, "cannot convert to self");
auto smart_symbol_name = converter_settings.smart_currency.symbol.code().raw();
auto from_token = get_reserve(from_path_currency, converter_settings);
auto to_token = get_reserve(to_path_currency, converter_settings);
auto from_currency = from_token.currency;
auto to_currency = to_token.currency;
auto from_contract = from_token.contract;
auto to_contract = to_token.contract;
bool incoming_smart_token = (from_currency.symbol.code().raw() == smart_symbol_name);
bool outgoing_smart_token = (to_currency.symbol.code().raw() == smart_symbol_name);
auto from_ratio = from_token.ratio;
auto to_ratio = to_token.ratio;
eosio_assert(from_token.p_enabled, "'from' token purchases disabled");
eosio_assert(code == from_contract, "unknown 'from' contract");
auto current_from_balance = ((get_balance(from_contract, _self, from_currency.symbol.code())).amount + from_currency.amount - quantity.amount) / pow(10, from_currency.symbol.precision());
auto current_to_balance = ((get_balance(to_contract, _self, to_currency.symbol.code())).amount + to_currency.amount) / pow(10, to_currency.symbol.precision());
auto current_smart_supply = ((get_supply(converter_settings.smart_contract, converter_settings.smart_currency.symbol.code())).amount + converter_settings.smart_currency.amount) / pow(10, converter_settings.smart_currency.symbol.precision());
name final_to = name(memo_object.to_token.c_str());
double smart_tokens = 0;
double to_tokens = 0;
int64_t total_fee_amount = 0;
bool quick = false;
// 对应网络节点示意图代币流向分类 1)
if (incoming_smart_token) {
// destory received token
action(
permission_level{ _self, "active"_n },
converter_settings.smart_contract, "retire"_n,
std::make_tuple(quantity, std::string("destroy on conversion"))
).send();
smart_tokens = from_amount;
current_smart_supply -= smart_tokens;
}
// 对应网络节点示意图代币流向分类 3)
else if (!incoming_smart_token && !outgoing_smart_token && (from_ratio == to_ratio) && (converter_settings.fee == 0)) {
to_tokens = quick_convert(current_from_balance, from_amount, current_to_balance);
quick = true;
}
// 对应网络节点示意图代币流向分类 3)或 2)
else {
smart_tokens = calculate_purchase_return(current_from_balance, from_amount, current_smart_supply, from_ratio);
current_smart_supply += smart_tokens;
if (converter_settings.fee > 0) {
double ffee = (1.0 * converter_settings.fee / 1000.0);
auto fee = smart_tokens * ffee;
int64_t fee_amount = (fee * pow(10, converter_settings.smart_currency.symbol.precision()));
if (fee_amount > 0) {
smart_tokens = smart_tokens - fee;
total_fee_amount += fee_amount;
}
}
}
auto issue = false;
// 对应网络节点示意图代币流向分类 2)
if (outgoing_smart_token) {
eosio_assert(memo_object.path.size() == 2, "smart token must be final currency");
to_tokens = smart_tokens;
issue = true;
}
// 对应网络节点示意图代币流向分类 3)
else if (!quick) {
if (converter_settings.fee) {
double ffee = (1.0 * converter_settings.fee / 1000.0);
auto fee = smart_tokens * ffee;
int64_t fee_amount = (fee * pow(10, converter_settings.smart_currency.symbol.precision()));
if (fee_amount > 0) {
smart_tokens = smart_tokens - fee;
total_fee_amount += fee_amount;
}
}
to_tokens = calculate_sale_return(current_to_balance, smart_tokens, current_smart_supply, to_ratio);
}
int64_t to_amount = (to_tokens * pow(10, to_currency.symbol.precision()));
EMIT_CONVERSION_EVENT(memo, from_token.contract, from_currency.symbol.code(), to_token.contract, to_currency.symbol.code(), from_amount, to_amount, total_fee_amount);
if (incoming_smart_token || !outgoing_smart_token)
EMIT_PRICE_DATA_EVENT(current_smart_supply, to_token.contract, to_currency.symbol.code(), current_to_balance - to_amount, to_ratio);
if (outgoing_smart_token || !incoming_smart_token)
EMIT_PRICE_DATA_EVENT(current_smart_supply, from_token.contract, from_currency.symbol.code(), current_from_balance, from_ratio);
auto next_hop_memo = next_hop(memo_object);
auto new_memo = build_memo(next_hop_memo);
auto new_asset = asset(to_amount, to_currency.symbol);
name inner_to = converter_settings.network;
if (next_hop_memo.path.size() == 0) {
inner_to = final_to;
verify_min_return(new_asset, memo_object.min_return);
if (converter_settings.require_balance)
verify_entry(inner_to, to_contract, new_asset);
new_memo = std::string("convert");
}
if (issue)
// 对应网络节点示意图代币流向分类 2)
action(
permission_level{ _self, "active"_n },
to_contract, "issue"_n,
std::make_tuple(inner_to, new_asset, new_memo)
).send();
// 对应网络节点示意图代币流向分类 1)或 3)
else
action(
permission_level{ _self, "active"_n },
to_contract, "transfer"_n,
std::make_tuple(_self, inner_to, new_asset, new_memo)
).send();
}
上述函数中的quick_convert原理参考 EOS中的ram购买算法(python版)中的quick_conver
- 网络节点
上述bancor网络节点示意图中对节点代币的配置通过BancorConverter合约中的init action完成,网络节点的连接器代币配置通过BancorConverter合约中的setreserve action完成
参考:http://www.qukuaiwang.com.cn/news/12895.html