1 /********************************************************************
2 * (C) 2019 Michael Toutonghi
4 * Distributed under the MIT software license, see the accompanying
5 * file COPYING or http://www.opensource.org/licenses/mit-license.php.
7 * This provides reserve currency functions, leveraging the multi-precision boost libraries to calculate reserve currency conversions.
12 #include "pbaas/pbaas.h"
13 #include "pbaas/reserves.h"
14 #include "pbaas/notarization.h"
15 #include "rpc/server.h"
19 CTokenOutput::CTokenOutput(const UniValue &obj)
21 nVersion = (uint32_t)uni_get_int(find_value(obj, "version"), VERSION_CURRENT);
22 currencyID = GetDestinationID(DecodeDestination(uni_get_str(find_value(obj, "currencyid"))));
26 nValue = AmountFromValue(find_value(obj, "value"));
28 catch(const std::exception& e)
30 std::cerr << e.what() << '\n';
31 nVersion = VERSION_INVALID;
35 CAmount CReserveTransfer::CalculateTransferFee(const CTransferDestination &destination)
37 return CReserveTransfer::DEFAULT_PER_STEP_FEE << 1 +
38 ((CReserveTransfer::DEFAULT_PER_STEP_FEE << 1) * (destination.destination.size() / DESTINATION_BYTE_DIVISOR));
41 CCurrencyValueMap CReserveTransfer::CalculateFee(uint32_t flags, CAmount transferTotal) const
43 CCurrencyValueMap feeMap;
45 // determine fee for this send
46 if (flags & FEE_OUTPUT)
51 feeMap.valueMap[currencyID] = CalculateTransferFee(destination);
53 // add conversion fees in source currency for preconvert
54 if (flags & (CReserveTransfer::PRECONVERT | CReserveTransfer::CONVERT))
56 feeMap.valueMap[currencyID] += CReserveTransactionDescriptor::CalculateConversionFee(transferTotal);
62 CAmount CReserveTransfer::CalculateTransferFee() const
64 // determine fee for this send
65 if (flags & FEE_OUTPUT)
69 return CalculateTransferFee(destination);
72 CReserveExchange::CReserveExchange(const UniValue &uni) : CTokenOutput(uni)
74 nVersion = (uint32_t)uni_get_int(find_value(uni, "version"), VERSION_CURRENT);
75 currencyID = GetDestinationID(DecodeDestination(uni_get_str(find_value(uni, "currencyid"))));
76 nValue = AmountFromValue(find_value(uni, "value"));
78 if (uni_get_bool(find_value(uni, "toreserve")))
82 if (uni_get_bool(find_value(uni, "limitorder")))
86 if (uni_get_bool(find_value(uni, "fillorkill")))
88 flags |= FILL_OR_KILL;
90 if (uni_get_bool(find_value(uni, "sendoutput")))
97 nLimit = AmountFromValue(find_value(uni, "limitprice"));
98 nValidBefore = uni_get_int(find_value(uni, "validbeforeblock"));
100 catch(const std::exception& e)
102 std::cerr << e.what() << '\n';
103 nVersion = VERSION_INVALID;
107 CReserveExchange::CReserveExchange(const CTransaction &tx)
109 bool orderFound = false;
110 for (auto out : tx.vout)
113 if (IsPayToCryptoCondition(out.scriptPubKey, p))
115 if (p.evalCode == EVAL_RESERVE_EXCHANGE)
119 nVersion = VERSION_INVALID;
123 FromVector(p.vData[0], *this);
131 CCrossChainImport::CCrossChainImport(const CTransaction &tx, int32_t *pOutNum)
133 for (int i = 0; i < tx.vout.size(); i++)
136 if (IsPayToCryptoCondition(tx.vout[i].scriptPubKey, p) && p.IsValid())
138 // always take the first for now
139 if (p.evalCode == EVAL_CROSSCHAIN_IMPORT && p.vData.size())
141 FromVector(p.vData[0], *this);
152 CCurrencyState::CCurrencyState(const UniValue &obj)
154 flags = uni_get_int(find_value(obj, "flags"));
156 std::string cIDStr = uni_get_str(find_value(obj, "currencyid"));
159 CTxDestination currencyDest = DecodeDestination(cIDStr);
160 currencyID = GetDestinationID(currencyDest);
163 if (flags & FLAG_FRACTIONAL)
165 auto CurrenciesArr = find_value(obj, "reservecurrencies");
166 size_t numCurrencies;
167 if (!CurrenciesArr.isArray() ||
168 !(numCurrencies = CurrenciesArr.size()) ||
169 numCurrencies > MAX_RESERVE_CURRENCIES)
171 flags &= ~FLAG_VALID;
172 LogPrintf("Failed to proplerly specify currencies in reserve currency definition\n");
176 // store currencies, weights, and reserves
179 for (int i = 0; i < CurrenciesArr.size(); i++)
181 uint160 currencyID = GetDestinationID(DecodeDestination(uni_get_str(find_value(CurrenciesArr[i], "currencyid"))));
182 if (currencyID.IsNull())
184 LogPrintf("Invalid currency ID\n");
185 flags &= ~FLAG_VALID;
188 currencies[i] = currencyID;
189 weights[i] = AmountFromValue(find_value(CurrenciesArr[i], "weight"));
190 reserves[i] = AmountFromValue(find_value(CurrenciesArr[i], "reserves"));
193 catch(const std::exception& e)
195 std::cerr << e.what() << '\n';
196 flags &= ~FLAG_VALID;
197 LogPrintf("Invalid specification of currencies, weights, and/or reserves in initial definition of reserve currency\n");
202 if (!(flags & FLAG_VALID))
204 printf("Invalid currency specification, see debug.log for reason other than invalid flags\n");
205 LogPrintf("Invalid currency specification\n");
211 initialSupply = AmountFromValue(find_value(obj, "initialsupply"));
212 emitted = AmountFromValue(find_value(obj, "emitted"));
213 supply = AmountFromValue(find_value(obj, "supply"));
215 catch(const std::exception& e)
217 std::cerr << e.what() << '\n';
218 flags &= ~FLAG_VALID;
223 CCoinbaseCurrencyState::CCoinbaseCurrencyState(const CTransaction &tx, int *pOutIdx)
226 int &i = pOutIdx ? *pOutIdx : localIdx;
227 for (i = 0; i < tx.vout.size(); i++)
230 if (IsPayToCryptoCondition(tx.vout[i].scriptPubKey, p))
232 if (p.evalCode == EVAL_CURRENCYSTATE && p.vData.size())
234 FromVector(p.vData[0], *this);
241 std::vector<std::vector<CAmount>> ValueColumnsFromUniValue(const UniValue &uni,
242 const std::vector<std::string> &rowNames,
243 const std::vector<std::string> &columnNames)
245 std::vector<std::vector<CAmount>> retVal;
246 for (int i = 0; i < rowNames.size(); i++)
248 UniValue row = find_value(uni, rowNames[i]);
251 for (int j = 0; j < columnNames.size(); j++)
253 if (retVal.size() == j)
255 retVal.emplace_back();
257 CAmount columnVal = 0;
260 columnVal = AmountFromValue(find_value(row, columnNames[j]));
262 catch(const std::exception& e)
264 std::cerr << e.what() << '\n';
266 retVal[j].push_back(columnVal);
274 CCoinbaseCurrencyState::CCoinbaseCurrencyState(const UniValue &obj) : CCurrencyState(obj)
276 std::vector<std::vector<CAmount>> columnAmounts;
278 auto currenciesValue = find_value(obj, "currencies");
279 std::vector<std::string> rowNames;
280 for (int i = 0; i < currencies.size(); i++)
282 rowNames.push_back(EncodeDestination(CIdentityID(currencies[i])));
284 std::vector<std::string> columnNames({"reservein", "nativein", "reserveout", "lastconversionprice", "fees", "conversionfees"});
285 if (currenciesValue.isObject())
287 columnAmounts = ValueColumnsFromUniValue(currenciesValue, rowNames, columnNames);
288 reserveIn = columnAmounts[0];
289 nativeIn = columnAmounts[1];
290 reserveOut = columnAmounts[2];
291 conversionPrice = columnAmounts[3];
292 fees = columnAmounts[4];
293 conversionFees = columnAmounts[5];
295 nativeFees = uni_get_int64(find_value(obj, "nativefees"));
296 nativeConversionFees = uni_get_int64(find_value(obj, "nativeconversionfees"));
299 CAmount CalculateFractionalOut(CAmount NormalizedReserveIn, CAmount Supply, CAmount NormalizedReserve, int32_t reserveRatio)
301 static cpp_dec_float_50 one("1");
302 static cpp_dec_float_50 bigSatoshi("100000000");
303 cpp_dec_float_50 reservein(std::to_string(NormalizedReserveIn));
304 reservein = reservein / bigSatoshi;
305 cpp_dec_float_50 supply(std::to_string((Supply ? Supply : 1)));
306 supply = supply / bigSatoshi;
307 cpp_dec_float_50 reserve(std::to_string(NormalizedReserve ? NormalizedReserve : 1));
308 reserve = reserve / bigSatoshi;
309 cpp_dec_float_50 ratio(std::to_string(reserveRatio));
310 ratio = ratio / bigSatoshi;
312 //printf("reservein: %s\nsupply: %s\nreserve: %s\nratio: %s\n\n", reservein.str().c_str(), supply.str().c_str(), reserve.str().c_str(), ratio.str().c_str());
314 int64_t fractionalOut = 0;
316 // first check if anything to buy
317 if (NormalizedReserveIn)
319 cpp_dec_float_50 supplyout = bigSatoshi * (supply * (pow((reservein / reserve) + one, ratio) - one));
320 //printf("supplyout: %s\n", supplyout.str(0, std::ios_base::fmtflags::_S_fixed).c_str());
322 if (!CCurrencyState::to_int64(supplyout, fractionalOut))
327 return fractionalOut;
330 CAmount CalculateReserveOut(CAmount FractionalIn, CAmount Supply, CAmount NormalizedReserve, int32_t reserveRatio)
332 static cpp_dec_float_50 one("1");
333 static cpp_dec_float_50 bigSatoshi("100000000");
334 cpp_dec_float_50 fractionalin(std::to_string(FractionalIn));
335 fractionalin = fractionalin / bigSatoshi;
336 cpp_dec_float_50 supply(std::to_string((Supply ? Supply : 1)));
337 supply = supply / bigSatoshi;
338 cpp_dec_float_50 reserve(std::to_string(NormalizedReserve ? NormalizedReserve : 1));
339 reserve = reserve / bigSatoshi;
340 cpp_dec_float_50 ratio(std::to_string(reserveRatio));
341 ratio = ratio / bigSatoshi;
343 //printf("fractionalin: %s\nsupply: %s\nreserve: %s\nratio: %s\n\n", fractionalin.str().c_str(), supply.str().c_str(), reserve.str().c_str(), ratio.str().c_str());
345 int64_t reserveOut = 0;
347 // first check if anything to buy
350 cpp_dec_float_50 reserveout = bigSatoshi * (reserve * (one - pow(one - (fractionalin / supply), (one / ratio))));
351 //printf("reserveout: %s\n", reserveout.str(0, std::ios_base::fmtflags::_S_fixed).c_str());
353 if (!CCurrencyState::to_int64(reserveout, reserveOut))
361 // This can handle multiple aggregated, bidirectional conversions in one block of transactions. To determine the conversion price, it
362 // takes both input amounts of any number of reserves and the fractional currencies targeting those reserves to merge the conversion into one
363 // merged calculation with the same price across currencies for all transactions in the block. It returns the newly calculated
364 // conversion prices of the fractional reserve in the reserve currency.
365 std::vector<CAmount> CCurrencyState::ConvertAmounts(const std::vector<CAmount> &inputReserves, const std::vector<CAmount> &inputFractional, CCurrencyState &newState) const
367 assert(inputReserves.size() == inputFractional.size() && inputReserves.size() == currencies.size());
370 std::vector<CAmount> rates;
372 bool haveConversion = false;
373 for (auto oneIn : inputReserves)
377 haveConversion = true;
383 for (auto oneIn : inputFractional)
387 haveConversion = true;
394 return PricesInReserve();
398 for (auto oneIn : inputReserves)
402 printf("%s: invalid reserve input amount for conversion %ld\n", __func__, oneIn);
406 for (auto oneIn : inputFractional)
410 printf("%s: invalid fractional input amount for conversion %ld\n", __func__, oneIn);
411 haveConversion = true;
417 // Create corresponding fractions of the supply for each currency to be used as starting calculation of that currency's value
418 // Determine the equivalent amount of input and output based on current values. Balance each such that each currency has only
419 // input or output, denominated in supply at the starting value.
421 // For each currency in either direction, sell to reserve or buy aggregate, we convert to a contribution of amount at the reserve
422 // percent value. For example, consider 4 currencies, r1...r4, which are all 25% reserves of currency fr1. For simplicity of example,
423 // assume 1000 reserve of each reserve currency, where all currencies are equal in value to each other at the outset, and a supply of
424 // 4000, where each fr1 is equal in value to 1 of each component reserve.
425 // Now, consider the following cases:
427 // 1. purchase fr1 with 100 r1
428 // This is treated as a single 25% fractional purchase with respect to amount purchased, ending price, and supply change
429 // 2. purchase fr1 with 100 r1, 100 r2, 100 r3, 100 r4
430 // This is treated as a common layer of purchase across 4 x 25% currencies, resulting in 100% fractional purchase divided 4 ways
431 // 3. purchase fr1 with 100 r1, 50 r2, 25 r3
432 // This is treated as 3 separate purchases in order:
433 // a. one of 25 units across 3 currencies (3 x 25%), making a 75% fractional purchase of 75 units divided equally across 3 currencies
434 // b. one of 25 units across 2 currencies (2 x 25%), making a 50% fractional purchase of 50 units divided equally between r1 and r2
435 // c. one purchase of 50 units in r1 at 25% fractional purchase
436 // 4. purchase fr1 with 100 r1, sell 100 fr1 to r2
437 // a. one fractional purchase of 100 units at 25%
438 // b. one fractional sell of 100 units at 25%
439 // c. do each in forward and reverse order and set conversion at mean between each
440 // 5. purchase fr1 with 100 r1, 50 r2, sell 100 fr1 to r3, 50 to r4
441 // This consists of one composite (multi-layer) buy and one composite sell
442 // a. Compose one two layer purchase of 50 r1 + 50 r2 at 50% and 50 r1 at 25%
443 // b. Compose one two layer sell of 50 r3 + 50 r4 at 50% and 50 r3 at 25%
444 // c. execute each operation of a and b in forward and reverse order and set conversion at mean between results
447 std::multimap<CAmount, std::pair<CAmount, uint160>> fractionalIn, fractionalOut;
449 // aggregate amounts of ins and outs across all currencies expressed in fractional values in both directions first buy/sell, then sell/buy
450 std::map<uint160, std::pair<CAmount, CAmount>> fractionalInMap, fractionalOutMap;
452 arith_uint256 bigSatoshi(SATOSHIDEN);
453 arith_uint256 bigSupply(supply);
455 int32_t totalReserveWeight = 0;
456 int32_t maxReserveRatio = 0;
458 for (auto weight : weights)
460 maxReserveRatio = weight > maxReserveRatio ? weight : maxReserveRatio;
461 totalReserveWeight += weight;
464 if (!maxReserveRatio)
466 LogPrintf("%s: attempting to convert amounts on non-reserve currency\n", __func__);
472 arith_uint256 bigMaxReserveRatio = arith_uint256(maxReserveRatio);
473 arith_uint256 bigTotalReserveWeight = arith_uint256(totalReserveWeight);
475 // reduce each currency change to a net inflow or outflow of fractional currency and
476 // store both negative and positive in structures sorted by the net amount, adjusted
477 // by the difference of the ratio between the weights of each currency
478 for (int64_t i = 0; i < currencies.size(); i++)
480 arith_uint256 weight(weights[i]);
481 //printf("%s: %ld\n", __func__, ReserveToNative(inputReserves[i], i));
482 CAmount netFractional = inputFractional[i] - ReserveToNative(inputReserves[i], i);
484 if (netFractional > 0)
486 deltaRatio = ((arith_uint256(netFractional) * bigMaxReserveRatio) / weight).GetLow64();
487 fractionalIn.insert(std::make_pair(deltaRatio, std::make_pair(netFractional, currencies[i])));
489 else if (netFractional < 0)
491 netFractional = -netFractional;
492 deltaRatio = ((arith_uint256(netFractional) * bigMaxReserveRatio) / weight).GetLow64();
493 fractionalOut.insert(std::make_pair(deltaRatio, std::make_pair(netFractional, currencies[i])));
497 // create "layers" of equivalent value at different fractional percentages
498 // across currencies going in or out at the same time, enabling their effect on the aggregate
499 // to be represented by a larger fractional percent impact of "normalized reserve" on the currency,
500 // which results in accurate pricing impact simulating a basket of currencies.
502 // since we have all values sorted, the lowest non-zero value determines the first common layer, then next lowest, the next, etc.
503 std::vector<std::pair<int32_t, std::pair<CAmount, std::vector<uint160>>>> fractionalLayersIn, fractionalLayersOut;
504 auto reserveMap = GetReserveMap();
506 CAmount layerAmount = 0;
509 for (auto inFIT = fractionalIn.upper_bound(layerAmount); inFIT != fractionalIn.end(); inFIT = fractionalIn.upper_bound(layerAmount))
511 // make a common layer out of all entries from here until the end
512 int frIdx = fractionalLayersIn.size();
513 layerStart = layerAmount;
514 layerAmount = inFIT->first;
515 CAmount layerHeight = layerAmount - layerStart;
516 fractionalLayersIn.emplace_back(std::make_pair(0, std::make_pair(0, std::vector<uint160>())));
517 for (auto it = inFIT; it != fractionalIn.end(); it++)
519 // reverse the calculation from layer height to amount for this currency, based on currency weight
520 int32_t weight = weights[reserveMap[it->second.second]];
521 CAmount curAmt = ((arith_uint256(layerHeight) * arith_uint256(weight) / bigMaxReserveRatio)).GetLow64();
522 it->second.first -= curAmt;
523 assert(it->second.first >= 0);
525 fractionalLayersIn[frIdx].first += weight;
526 fractionalLayersIn[frIdx].second.first += curAmt;
527 fractionalLayersIn[frIdx].second.second.push_back(it->second.second);
532 for (auto outFIT = fractionalOut.upper_bound(layerAmount); outFIT != fractionalOut.end(); outFIT = fractionalOut.upper_bound(layerAmount))
534 int frIdx = fractionalLayersOut.size();
535 layerStart = layerAmount;
536 layerAmount = outFIT->first;
537 CAmount layerHeight = layerAmount - layerStart;
538 fractionalLayersOut.emplace_back(std::make_pair(0, std::make_pair(0, std::vector<uint160>())));
539 for (auto it = outFIT; it != fractionalOut.end(); it++)
541 int32_t weight = weights[reserveMap[it->second.second]];
542 CAmount curAmt = ((arith_uint256(layerHeight) * arith_uint256(weight) / bigMaxReserveRatio)).GetLow64();
543 it->second.first -= curAmt;
544 assert(it->second.first >= 0);
546 fractionalLayersOut[frIdx].first += weight;
547 fractionalLayersOut[frIdx].second.first += curAmt;
548 fractionalLayersOut[frIdx].second.second.push_back(it->second.second);
552 int64_t supplyAfterBuy = 0, supplyAfterBuySell = 0, supplyAfterSell = 0, supplyAfterSellBuy = 0;
553 int64_t reserveAfterBuy = 0, reserveAfterBuySell = 0, reserveAfterSell = 0, reserveAfterSellBuy = 0;
555 // first, loop through all buys layer by layer. calculate and divide the proceeds between currencies
556 // in each participating layer, in accordance with each currency's relative percentage
557 CAmount addSupply = 0;
558 CAmount addNormalizedReserves = 0;
559 for (auto &layer : fractionalLayersOut)
561 // each layer has a fractional percentage/weight and a total amount, determined by the total of all weights for that layer
562 // and net amounts across all currencies in that layer. each layer also includes a list of all currencies.
564 // calculate a fractional buy at the total layer ratio for the amount specified
565 // and divide the value according to the relative weight of each currency, adding to each entry of fractionalOutMap
566 arith_uint256 bigLayerWeight = arith_uint256(layer.first);
567 CAmount totalLayerReserves = ((bigSupply * bigLayerWeight) / bigSatoshi).GetLow64() + addNormalizedReserves;
568 addNormalizedReserves += layer.second.first;
569 CAmount newSupply = CalculateFractionalOut(layer.second.first, supply + addSupply, totalLayerReserves, layer.first);
570 arith_uint256 bigNewSupply(newSupply);
571 addSupply += newSupply;
572 for (auto &id : layer.second.second)
574 auto idIT = fractionalOutMap.find(id);
575 CAmount newSupplyForCurrency = ((bigNewSupply * weights[reserveMap[id]]) / bigLayerWeight).GetLow64();
577 // initialize or add to the new supply for this currency
578 if (idIT == fractionalOutMap.end())
580 fractionalOutMap[id] = std::make_pair(newSupplyForCurrency, int64_t(0));
584 idIT->second.first += newSupplyForCurrency;
589 supplyAfterBuy = supply + addSupply;
590 assert(supplyAfterBuy >= 0);
592 reserveAfterBuy = supply + addNormalizedReserves;
593 assert(reserveAfterBuy >= 0);
596 addNormalizedReserves = 0;
597 CAmount addNormalizedReservesBB = 0, addNormalizedReservesAB = 0;
599 // calculate sell both before and after buy through this loop
600 for (auto &layer : fractionalLayersIn)
602 // first calculate sell before-buy, then after-buy
603 arith_uint256 bigLayerWeight(layer.first);
605 // before-buy starting point
606 CAmount totalLayerReservesBB = ((bigSupply * bigLayerWeight) / bigSatoshi).GetLow64() + addNormalizedReserves;
607 CAmount totalLayerReservesAB = ((arith_uint256(supplyAfterBuy) * bigLayerWeight) / bigSatoshi).GetLow64() + addNormalizedReserves;
609 CAmount newNormalizedReserveBB = CalculateReserveOut(layer.second.first, supply + addSupply, totalLayerReservesBB + addNormalizedReservesBB, layer.first);
610 CAmount newNormalizedReserveAB = CalculateReserveOut(layer.second.first, supplyAfterBuy + addSupply, totalLayerReservesAB + addNormalizedReservesAB, layer.first);
612 // input fractional is burned and output reserves are removed from reserves
613 addSupply -= layer.second.first;
614 addNormalizedReservesBB -= newNormalizedReserveBB;
615 addNormalizedReservesAB -= newNormalizedReserveAB;
617 for (auto &id : layer.second.second)
619 auto idIT = fractionalInMap.find(id);
620 CAmount newReservesForCurrencyBB = ((arith_uint256(newNormalizedReserveBB) * arith_uint256(weights[reserveMap[id]])) / bigLayerWeight).GetLow64();
621 CAmount newReservesForCurrencyAB = ((arith_uint256(newNormalizedReserveAB) * arith_uint256(weights[reserveMap[id]])) / bigLayerWeight).GetLow64();
623 // initialize or add to the new supply for this currency
624 if (idIT == fractionalInMap.end())
626 fractionalInMap[id] = std::make_pair(newReservesForCurrencyBB, newReservesForCurrencyAB);
630 idIT->second.first += newReservesForCurrencyBB;
631 idIT->second.second += newReservesForCurrencyAB;
636 supplyAfterSell = supply + addSupply;
637 assert(supplyAfterSell >= 0);
639 supplyAfterBuySell = supplyAfterBuy + addSupply;
640 assert(supplyAfterBuySell >= 0);
642 reserveAfterSell = supply + addNormalizedReservesBB;
643 assert(reserveAfterSell >= 0);
645 reserveAfterBuySell = reserveAfterBuy + addNormalizedReservesAB;
646 assert(reserveAfterBuySell >= 0);
649 addNormalizedReserves = 0;
651 // now calculate buy after sell
652 for (auto &layer : fractionalLayersOut)
654 arith_uint256 bigLayerWeight = arith_uint256(layer.first);
655 CAmount totalLayerReserves = ((arith_uint256(supplyAfterSell) * bigLayerWeight) / bigSatoshi).GetLow64() + addNormalizedReserves;
656 addNormalizedReserves += layer.second.first;
657 CAmount newSupply = CalculateFractionalOut(layer.second.first, supplyAfterSell + addSupply, totalLayerReserves, layer.first);
658 arith_uint256 bigNewSupply(newSupply);
659 addSupply += newSupply;
660 for (auto &id : layer.second.second)
662 auto idIT = fractionalOutMap.find(id);
664 assert(idIT != fractionalOutMap.end());
666 idIT->second.second += ((bigNewSupply * weights[reserveMap[id]]) / bigLayerWeight).GetLow64();
670 // now loop through all currencies, calculate conversion rates for each based on mean of all prices that we calculate for
671 // buy before sell and sell before buy
672 rates.resize(currencies.size());
673 for (int i = 0; i < currencies.size(); i++)
675 // each coin has an amount of reserve in, an amount of fractional in, and potentially two delta amounts in one of the
676 // fractionalInMap or fractionalOutMap maps, one for buy before sell and one for sell before buy.
677 // add the mean of the delta amounts to the appropriate side of the equation and calculate a price for each
679 auto fractionalOutIT = fractionalOutMap.find(currencies[i]);
680 auto fractionalInIT = fractionalInMap.find(currencies[i]);
682 auto inputReserve = inputReserves[i];
683 auto inputFraction = inputFractional[i];
685 CAmount fractionDelta = 0, reserveDelta = 0;
687 if (fractionalOutIT != fractionalOutMap.end())
689 arith_uint256 bigFractionDelta(fractionalOutIT->second.first);
690 fractionDelta = ((bigFractionDelta + arith_uint256(fractionalOutIT->second.second)) >> 1).GetLow64();
691 assert(inputFraction + fractionDelta > 0);
693 rates[i] = ((arith_uint256(inputReserve) * bigSatoshi) / arith_uint256(inputFraction + fractionDelta)).GetLow64();
695 // add the new reserve and supply to the currency
696 newState.supply += fractionDelta;
698 // all reserves have been calculated using a substituted value, which was 1:1 for native initially
699 newState.reserves[i] += inputFractional[i] ? NativeToReserveRaw(fractionDelta, rates[i]) : inputReserves[i];
701 else if (fractionalInIT != fractionalInMap.end())
703 arith_uint256 bigReserveDelta(fractionalInIT->second.first);
704 CAmount adjustedReserveDelta = NativeToReserve(((bigReserveDelta + arith_uint256(fractionalInIT->second.second)) >> 1).GetLow64(), i);
706 assert(inputFraction > 0);
708 rates[i] = ((arith_uint256(inputReserve + adjustedReserveDelta) * bigSatoshi) / arith_uint256(inputFraction)).GetLow64();
710 // subtract the fractional and reserve that has left the currency
711 newState.supply -= inputFraction;
712 newState.reserves[i] -= adjustedReserveDelta;
716 rates[i] = PriceInReserve(i);
722 CAmount CCurrencyState::ConvertAmounts(CAmount inputReserve, CAmount inputFraction, CCurrencyState &newState, int32_t reserveIndex) const
724 if (reserveIndex >= newState.currencies.size())
726 printf("%s: reserve index out of range\n", __func__);
729 std::vector<CAmount> inputReserves(newState.currencies.size());
730 inputReserves[reserveIndex] = inputReserve;
731 std::vector<CAmount> inputFractional(newState.currencies.size());
732 inputFractional[reserveIndex] = inputFraction;
733 std::vector<CAmount> retVal = ConvertAmounts(inputReserves, inputFractional, newState);
734 return retVal[reserveIndex];
738 // This can handle multiple aggregated, bidirectional conversions in one block of transactions. To determine the conversion price, it
739 // takes both input amounts of the reserve and the fractional currency to merge the conversion into one calculation
740 // with the same price for all transactions in the block. It returns the newly calculated conversion price of the fractional
741 // reserve in the reserve currency.
742 CAmount CCurrencyState::ConvertAmounts(CAmount inputReserve, CAmount inputFractional, CCurrencyState &newState) const
745 CAmount conversionPrice = PriceInReserve();
747 int64_t totalFractionalOut = 0; // how much fractional goes to buyers
748 int64_t totalReserveOut = 0; // how much reserve goes to sellers
750 // if both conversions are zero, nothing to do but return current price
751 if ((!inputReserve && !inputFractional) || !(flags & FLAG_FRACTIONAL))
753 return conversionPrice;
756 // first, cancel out all parity at the current price, leaving one or the other amount to be converted, but not both
757 // that results in consistently minimum slippage in either direction
758 CAmount reserveOffset = NativeToReserve(inputFractional, conversionPrice);
759 CAmount convertFractional = 0, convertReserve = 0;
760 if (inputReserve >= reserveOffset)
762 convertReserve = inputReserve - reserveOffset;
763 totalFractionalOut += inputFractional;
767 CAmount fractionalOffset = ReserveToNative(inputReserve, conversionPrice);
768 convertFractional = inputFractional - fractionalOffset;
769 if (convertFractional < 0)
771 convertFractional = 0;
773 totalReserveOut += inputReserve;
776 if (!convertReserve && !convertFractional)
778 return conversionPrice;
781 cpp_dec_float_50 reservein(std::to_string(convertReserve));
782 cpp_dec_float_50 fractionalin(std::to_string(convertFractional));
783 cpp_dec_float_50 supply(std::to_string((Supply)));
784 cpp_dec_float_50 reserve(std::to_string(Reserve));
785 cpp_dec_float_50 ratio = GetReserveRatio();
786 cpp_dec_float_50 one("1");
787 cpp_dec_float_50 dec_satoshi(std::to_string(CReserveExchange::SATOSHIDEN));
789 // first check if anything to buy
792 cpp_dec_float_50 supplyout;
793 supplyout = (supply * (pow((reservein / reserve) + one, ratio) - one));
796 if (!to_int64(supplyout, supplyOut))
800 totalFractionalOut += supplyOut;
802 cpp_dec_float_50 dec_price = cpp_dec_float_50(std::to_string(inputReserve)) / cpp_dec_float_50(std::to_string(totalFractionalOut));
803 cpp_dec_float_50 reserveFromFractional = cpp_dec_float_50(std::to_string(inputFractional)) * dec_price;
804 dec_price = dec_price * dec_satoshi;
806 if (!to_int64(reserveFromFractional, totalReserveOut))
811 if (!to_int64(dec_price, conversionPrice))
818 cpp_dec_float_50 reserveout;
820 reserveout = reserve * (one - pow(one - (fractionalin / supply), (one / ratio)));
821 if (!to_int64(reserveout, reserveOut))
826 totalReserveOut += reserveOut;
828 cpp_dec_float_50 dec_price = cpp_dec_float_50(std::to_string(totalReserveOut)) / cpp_dec_float_50(std::to_string(inputFractional));
829 cpp_dec_float_50 fractionalFromReserve = cpp_dec_float_50(std::to_string(inputReserve)) / dec_price;
830 dec_price = dec_price * dec_satoshi;
832 if (!to_int64(fractionalFromReserve, totalFractionalOut))
837 if (!to_int64(dec_price, conversionPrice))
843 newState.Supply += totalFractionalOut - inputFractional;
844 newState.Reserve += inputReserve - totalReserveOut;
846 return conversionPrice;
850 void CReserveTransactionDescriptor::AddReserveInput(const uint160 ¤cy, CAmount value)
852 //printf("adding %ld:%s reserve input\n", value, EncodeDestination(CIdentityID(currency)).c_str());
854 auto it = currencies.find(currency);
855 if (it != currencies.end())
857 it->second.reserveIn += value;
861 currencies[currency] = CReserveInOuts(value, 0, 0, 0, 0);
865 void CReserveTransactionDescriptor::AddReserveOutput(const uint160 ¤cy, CAmount value)
867 //printf("adding %ld:%s reserve output\n", value, EncodeDestination(CIdentityID(currency)).c_str());
869 auto it = currencies.find(currency);
870 if (it != currencies.end())
872 it->second.reserveOut += value;
876 currencies[currency] = CReserveInOuts(0, value, 0, 0, 0);
880 void CReserveTransactionDescriptor::AddReserveOutConverted(const uint160 ¤cy, CAmount value)
882 auto it = currencies.find(currency);
883 if (it != currencies.end())
885 it->second.reserveOutConverted += value;
889 currencies[currency] = CReserveInOuts(0, 0, value, 0, 0);
893 void CReserveTransactionDescriptor::AddNativeOutConverted(const uint160 ¤cy, CAmount value)
895 auto it = currencies.find(currency);
896 if (it != currencies.end())
898 it->second.nativeOutConverted += value;
902 currencies[currency] = CReserveInOuts(0, 0, 0, value, 0);
906 void CReserveTransactionDescriptor::AddReserveConversionFees(const uint160 ¤cy, CAmount value)
908 auto it = currencies.find(currency);
909 if (it != currencies.end())
911 it->second.reserveConversionFees += value;
915 currencies[currency] = CReserveInOuts(0, 0, 0, 0, value);
919 void CReserveTransactionDescriptor::AddReserveExchange(const CReserveExchange &rex, int32_t outputIndex, int32_t nHeight)
923 bool wasMarket = IsMarket();
925 flags |= IS_RESERVE + IS_RESERVEEXCHANGE;
927 if (IsLimit() || rex.flags & CReserveExchange::LIMIT)
929 if (wasMarket || (vRex.size() && (vRex.back().second.flags != rex.flags || (vRex.back().second.nValidBefore != rex.nValidBefore))))
937 if (rex.nValidBefore > nHeight)
939 flags |= IS_FILLORKILL;
940 // fill or kill fee must be pre-subtracted, but is added back on success, making conversion the only
941 // requred fee on success
942 fee = CalculateConversionFee(rex.nValue + rex.FILL_OR_KILL_FEE);
943 if (rex.flags & CReserveExchange::TO_RESERVE)
946 nativeConversionFees += fee;
947 AddNativeOutConverted(rex.currencyID, (rex.nValue + rex.FILL_OR_KILL_FEE) - fee);
948 nativeOut += rex.nValue + rex.FILL_OR_KILL_FEE; // output is always less fees
953 auto it = currencies.find(rex.currencyID);
954 if (it != currencies.end())
956 it->second.reserveOut += rex.nValue + rex.FILL_OR_KILL_FEE;
957 it->second.reserveOutConverted += rex.nValue + rex.FILL_OR_KILL_FEE - fee;
958 it->second.reserveConversionFees += fee;
962 currencies[rex.currencyID] = CReserveInOuts(0, rex.nValue + rex.FILL_OR_KILL_FEE, rex.nValue + rex.FILL_OR_KILL_FEE - fee, 0, fee);
968 flags &= !IS_RESERVEEXCHANGE; // no longer a reserve exchange transaction, falls back to normal reserve tx
969 flags |= IS_FILLORKILLFAIL;
971 if (rex.flags & CReserveExchange::TO_RESERVE)
974 nativeOut += rex.nValue;
979 AddReserveOutput(rex.currencyID, rex.nValue);
985 fee = CalculateConversionFee(rex.nValue);
986 if (rex.flags & CReserveExchange::TO_RESERVE)
989 nativeConversionFees += fee;
990 AddNativeOutConverted(rex.currencyID, rex.nValue - fee);
991 nativeOut += rex.nValue; // conversion fee is considered part of the total native out, since it will be attributed to conversion tx
996 if (!(flags & IS_IMPORT))
998 auto it = currencies.find(rex.currencyID);
999 if (it != currencies.end())
1001 it->second.reserveOut += rex.nValue;
1002 it->second.reserveOutConverted += rex.nValue - fee;
1003 it->second.reserveConversionFees += fee;
1007 currencies[rex.currencyID] = CReserveInOuts(0, rex.nValue, rex.nValue - fee, 0, fee);
1012 vRex.push_back(std::make_pair(outputIndex, rex));
1015 CAmount CReserveTransactionDescriptor::AllFeesAsNative(const CCurrencyState ¤cyState) const
1017 CAmount nativeFees = NativeFees();
1018 CCurrencyValueMap reserveFees = ReserveFees();
1019 for (int i = 0; i < currencyState.currencies.size(); i++)
1021 auto it = reserveFees.valueMap.find(currencyState.currencies[i]);
1022 if (it != reserveFees.valueMap.end())
1024 nativeFees += currencyState.ReserveToNative(it->second, i);
1030 CAmount CReserveTransactionDescriptor::AllFeesAsNative(const CCurrencyState ¤cyState, const std::vector<CAmount> &exchangeRates) const
1032 assert(exchangeRates.size() == currencyState.currencies.size());
1033 CAmount nativeFees = NativeFees();
1034 CCurrencyValueMap reserveFees = ReserveFees();
1035 for (int i = 0; i < currencyState.currencies.size(); i++)
1037 auto it = reserveFees.valueMap.find(currencyState.currencies[i]);
1038 if (it != reserveFees.valueMap.end())
1040 nativeFees += currencyState.ReserveToNativeRaw(it->second, exchangeRates[i]);
1046 CCurrencyValueMap CReserveTransactionDescriptor::AllFeesAsReserve(const CCurrencyState ¤cyState, int defaultReserve) const
1048 CCurrencyValueMap reserveFees = ReserveFees();
1050 auto it = reserveFees.valueMap.find(currencyState.currencies[defaultReserve]);
1051 if (it != reserveFees.valueMap.end())
1053 it->second += currencyState.NativeToReserve(NativeFees(), defaultReserve);
1057 reserveFees.valueMap[currencyState.currencies[defaultReserve]] = NativeFees();
1062 CCurrencyValueMap CReserveTransactionDescriptor::AllFeesAsReserve(const CCurrencyState ¤cyState, const std::vector<CAmount> &exchangeRates, int defaultReserve) const
1064 CCurrencyValueMap reserveFees = ReserveFees();
1066 auto it = reserveFees.valueMap.find(currencyState.currencies[defaultReserve]);
1067 if (it != reserveFees.valueMap.end())
1069 it->second += currencyState.NativeToReserveRaw(NativeFees(), exchangeRates[defaultReserve]);
1073 reserveFees.valueMap[currencyState.currencies[defaultReserve]] = NativeFees();
1079 * Checks all structural aspects of the reserve part of a transaction that may have reserve inputs and/or outputs
1081 CReserveTransactionDescriptor::CReserveTransactionDescriptor(const CTransaction &tx, const CCoinsViewCache &view, int32_t nHeight) :
1089 nativeConversionFees(0)
1091 // market conversions can have any number of both buy and sell conversion outputs, this is used to make efficient, aggregated
1092 // reserve transfer operations with conversion
1094 // limit conversion outputs may have multiple outputs with different input amounts and destinations,
1095 // but they must not be mixed in a transaction with any dissimilar set of conditions on the output,
1096 // including mixing with market orders, parity of buy or sell, limit value and validbefore values,
1097 // or the transaction is considered invalid
1099 // no inputs are valid at height 0
1106 // reserve descriptor transactions cannot run until identity activates
1107 if (!chainActive.LastTip() ||
1108 CConstVerusSolutionVector::activationHeight.ActiveVersion(nHeight) < CConstVerusSolutionVector::activationHeight.ACTIVATE_IDENTITY)
1113 bool isVerusActive = IsVerusActive();
1114 bool isPBaaSActivation = CConstVerusSolutionVector::activationHeight.IsActivationHeight(CActivationHeight::ACTIVATE_PBAAS, nHeight);
1116 CCrossChainImport cci;
1117 CCrossChainExport ccx;
1119 CNameReservation nameReservation;
1121 CCurrencyDefinition newCurrencyDef;
1125 for (int i = 0; i < tx.vout.size(); i++)
1129 if (tx.vout[i].scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid())
1133 case EVAL_IDENTITY_RESERVATION:
1135 // one name reservation per transaction
1136 if (p.version < p.VERSION_V3 || !p.vData.size() || nameReservation.IsValid() || !(nameReservation = CNameReservation(p.vData[0])).IsValid())
1142 if (identity.IsValid())
1144 if (identity.name == nameReservation.name)
1146 flags |= IS_IDENTITY_DEFINITION + IS_HIGH_FEE;
1158 case EVAL_IDENTITY_PRIMARY:
1160 // one identity per transaction
1161 if (p.version < p.VERSION_V3 || !p.vData.size() || identity.IsValid() || !(identity = CIdentity(p.vData[0])).IsValid())
1167 flags |= IS_IDENTITY;
1168 if (nameReservation.IsValid())
1170 if (identity.name == nameReservation.name)
1172 flags |= IS_IDENTITY_DEFINITION + IS_HIGH_FEE;
1184 case EVAL_RESERVE_DEPOSIT:
1187 if (!p.vData.size() || !(rd = CReserveDeposit(p.vData[0])).IsValid())
1193 for (auto &oneCur : rd.reserveValues.valueMap)
1195 if (oneCur.first != ASSETCHAINS_CHAINID)
1197 AddReserveOutput(oneCur.first, oneCur.second);
1203 case EVAL_RESERVE_OUTPUT:
1206 if (!p.vData.size() || !(ro = CTokenOutput(p.vData[0])).IsValid())
1214 AddReserveOutput(ro);
1219 case EVAL_RESERVE_TRANSFER:
1221 CReserveTransfer rt;
1222 if (!p.vData.size() || !(rt = CReserveTransfer(p.vData[0])).IsValid())
1228 // on a PBaaS reserve chain, a reserve transfer is always denominated in reserve, as exchange must happen before it is
1229 // created. explicit fees in transfer object are for export and import, not initial mining
1230 AddReserveTransfer(rt);
1234 case EVAL_RESERVE_EXCHANGE:
1236 CReserveExchange rex;
1237 if (isVerusActive || !p.vData.size() || !(rex = CReserveExchange(p.vData[0])).IsValid())
1244 // if we send the output to the reserve chain, it must be a TO_RESERVE transaction
1245 if ((rex.flags & rex.SEND_OUTPUT) && !(rex.flags & rex.TO_RESERVE))
1251 AddReserveExchange(rex, i, nHeight);
1261 case EVAL_CROSSCHAIN_IMPORT:
1263 // if this is an import, add the amount imported to the reserve input and the amount of reserve output as
1264 // the amount available to take from this transaction in reserve as an import fee
1265 if (flags & IS_IMPORT || !p.vData.size() || !(cci = CCrossChainImport(p.vData[0])).IsValid())
1274 std::vector<CBaseChainObject *> chainObjs;
1275 // either a fully valid import with an export or the first import either in block 1 or the chain definition
1276 // on the Verus chain
1277 std::vector<CCurrencyDefinition> txCurrencies = CCurrencyDefinition::GetCurrencyDefinitions(tx);
1278 if ((nHeight == 1 && tx.IsCoinBase() && !IsVerusActive()) ||
1279 (txCurrencies.size() == 1 &&
1280 ((txCurrencies[0].parent == ASSETCHAINS_CHAINID) ||
1281 (txCurrencies[0].GetID() == ASSETCHAINS_CHAINID && isPBaaSActivation)) &&
1283 (tx.vout.back().scriptPubKey.IsOpReturn() &&
1284 (chainObjs = RetrieveOpRetArray(tx.vout.back().scriptPubKey)).size() >= 1 &&
1285 chainObjs[0]->objectType == CHAINOBJ_TRANSACTION_PROOF))
1287 if (chainObjs.size())
1289 CPartialTransactionProof &exportTxProof = ((CChainObject<CPartialTransactionProof> *)chainObjs[0])->object;
1290 CTransaction exportTx;
1291 std::vector<CBaseChainObject *> exportTransfers;
1293 if (exportTxProof.txProof.proofSequence.size() == 3 &&
1294 !exportTxProof.GetPartialTransaction(exportTx).IsNull() &&
1295 (ccx = CCrossChainExport(exportTx)).IsValid() &&
1296 exportTx.vout.back().scriptPubKey.IsOpReturn() &&
1297 (exportTransfers = RetrieveOpRetArray(exportTx.vout.back().scriptPubKey)).size() &&
1298 exportTransfers[0]->objectType == CHAINOBJ_RESERVETRANSFER)
1300 // an import transaction is built from the export list
1301 // we can rebuild it and confirm that it matches exactly
1302 // we can also subtract all pre-converted reserve from input
1303 // and verify that the output matches the proper conversion
1305 // get the chain definition of the chain we are importing
1306 std::vector<CTxOut> checkOutputs;
1308 CPBaaSNotarization importNotarization;
1309 CCurrencyDefinition importCurrencyDef = ConnectedChains.GetCachedCurrency(cci.systemID);
1310 if (importCurrencyDef.IsToken() && !(importNotarization = CPBaaSNotarization(tx)).IsValid())
1316 CCoinbaseCurrencyState currencyState = importCurrencyDef.IsToken() ?
1317 importNotarization.currencyState :
1318 GetInitialCurrencyState(importCurrencyDef);
1319 importCurrencyDef.conversions = currencyState.conversionPrice;
1321 if (!currencyState.IsValid() ||
1322 !AddReserveTransferImportOutputs(cci.systemID, importCurrencyDef, currencyState, exportTransfers, checkOutputs))
1327 for (auto &oneOutCur : cci.totalReserveOutMap.valueMap)
1329 AddReserveOutput(oneOutCur.first, oneOutCur.second);
1332 // TODO:PBAAS - hardening - validate all the outputs we got back as the same as what is in the transaction
1344 DeleteOpRetObjects(chainObjs);
1353 // this check will need to be made complete by preventing mixing both here and where the others
1355 case EVAL_CROSSCHAIN_EXPORT:
1357 // cross chain export is incompatible with reserve exchange outputs
1358 std::vector<CCurrencyDefinition> txCurrencies = CCurrencyDefinition::GetCurrencyDefinitions(tx);
1359 if (IsReserveExchange() ||
1360 (!(nHeight == 1 && tx.IsCoinBase() && !isVerusActive) &&
1361 !(txCurrencies.size() &&
1362 ((txCurrencies[0].parent == ASSETCHAINS_CHAINID) ||
1363 (txCurrencies[0].GetID() == ASSETCHAINS_CHAINID && isPBaaSActivation))) &&
1364 (flags & IS_IMPORT)))
1374 case EVAL_CURRENCY_DEFINITION:
1383 // we have all inputs, outputs, and fees, if check inputs, we can check all for consistency
1384 // inputs may be in the memory pool or on the blockchain
1385 CAmount dummyInterest;
1386 nativeOut = tx.GetValueOut();
1387 nativeIn = view.GetValueIn(nHeight, &dummyInterest, tx);
1389 // if we don't have a reserve transaction, we're done
1390 // don't try to replace basic transaction validation
1393 CAmount nValueIn = 0;
1395 // if it is a conversion to reserve, the amount in is accurate, since it is from the native coin, if converting to
1396 // the native PBaaS coin, the amount input is a sum of all the reserve token values of all of the inputs
1397 auto reservesIn = view.GetReserveValueIn(nHeight, tx);
1398 for (auto &oneIn : reservesIn.valueMap)
1400 auto it = currencies.find(oneIn.first);
1401 if (it == currencies.end())
1403 currencies[oneIn.first] = CReserveInOuts(oneIn.second, 0, 0, 0, 0);
1407 it->second.reserveIn += oneIn.second;
1411 // TODO:PBAAS hardening total minimum required fees as we build the descriptor and
1412 // reject if not correct
1417 CReserveTransfer RefundExport(const CBaseChainObject *objPtr)
1419 if (objPtr->objectType == CHAINOBJ_RESERVETRANSFER)
1421 CReserveTransfer &rt = ((CChainObject<CReserveTransfer> *)objPtr)->object;
1423 // convert full ID destinations to normal ID outputs, since it's refund, full ID will be on this chain already
1424 if (rt.destination.type == CTransferDestination::DEST_FULLID)
1426 CIdentity(rt.destination.destination);
1427 rt.destination = CTransferDestination(CTransferDestination::DEST_ID, rt.destination.destination);
1430 // turn it into a normal transfer, which will create an unconverted output
1431 rt.flags &= ~(CReserveTransfer::SEND_BACK | CReserveTransfer::PRECONVERT | CReserveTransfer::CONVERT);
1433 if (rt.flags & (CReserveTransfer::PREALLOCATE | CReserveTransfer::MINT_CURRENCY))
1435 rt.flags &= ~(CReserveTransfer::PREALLOCATE | CReserveTransfer::MINT_CURRENCY);
1438 rt.destCurrencyID = rt.currencyID;
1441 return CReserveTransfer();
1444 // the source currency indicates the system from which the import comes, but the imports may contain additional
1445 // currencies that are supported in that system and are not limited to the native currency. Fees are assumed to
1446 // be covered by the native currency of the source or source currency, if this is a reserve conversion. That
1447 // means that all explicit fees are assumed to be in the currency of the source.
1448 bool CReserveTransactionDescriptor::AddReserveTransferImportOutputs(const uint160 &nativeSourceCurrencyID,
1449 const CCurrencyDefinition &importCurrencyDef,
1450 const CCoinbaseCurrencyState &importCurrencyState,
1451 const std::vector<CBaseChainObject *> &exportObjects,
1452 std::vector<CTxOut> &vOutputs,
1453 CCoinbaseCurrencyState *pNewCurrencyState)
1455 // easy way to refer to return currency state or a dummy without conditionals
1456 CCoinbaseCurrencyState _newCurrencyState;
1457 if (!pNewCurrencyState)
1459 pNewCurrencyState = &_newCurrencyState;
1461 CCoinbaseCurrencyState &newCurrencyState = *pNewCurrencyState;
1462 newCurrencyState = importCurrencyState;
1463 newCurrencyState.ClearForNextBlock();
1465 // reserve currency amounts converted to fractional
1466 CCurrencyValueMap reserveConverted;
1468 // fractional currency amount and the reserve it is converted to
1469 CCurrencyValueMap fractionalConverted;
1471 // used to keep track of burned fractional currency. this currency is subtracted from the
1472 // currency supply, but not converted. In doing so, it can either raise the price of the fractional
1473 // currency in all other currencies, or increase the reserve ratio of all currencies by some slight amount.
1474 CAmount burnedChangePrice = 0;
1476 // this is cached here, but only used for pre-conversions
1477 CCoinbaseCurrencyState initialCurrencyState;
1478 CCurrencyValueMap preConvertedOutput;
1480 // we do not change native in or conversion fees, but we define all of these members
1483 for (auto &oneInOut : currencies)
1485 oneInOut.second.reserveIn = 0;
1486 oneInOut.second.reserveOut = 0;
1489 bool isVerusActive = IsVerusActive();
1492 CCcontract_info *cp;
1494 uint160 systemDestID = importCurrencyDef.systemID; // native on destination system
1495 uint160 importCurrencyID = importCurrencyDef.GetID();
1496 std::map<uint160, int32_t> currencyIndexMap = importCurrencyDef.GetCurrenciesMap();
1497 //printf("%s\n", importCurrencyDef.ToUniValue().write(1,2).c_str());
1499 std::map<uint160, CAmount> preAllocMap; // if this contains pre-allocations, only make the necessary map once
1500 CCurrencyValueMap transferFees; // calculated fees based on all transfers/conversions, etc.
1501 CCurrencyValueMap feeOutputs; // actual fee output amounts
1503 bool feeOutputStart = false; // fee outputs must come after all others, this indicates they have started
1504 int nFeeOutputs = 0; // number of fee outputs
1506 bool carveOutSet = false;
1507 int32_t totalCarveOut;
1508 CCurrencyValueMap totalCarveOuts;
1509 CAmount totalMinted = 0;
1511 for (int i = 0; i < exportObjects.size(); i++)
1513 if (exportObjects[i]->objectType == CHAINOBJ_RESERVETRANSFER)
1515 CReserveTransfer _curTransfer;
1516 CReserveTransfer *pCurTransfer;
1518 if (importCurrencyState.IsRefunding())
1520 _curTransfer = RefundExport(exportObjects[i]);
1521 pCurTransfer = &_curTransfer;
1525 pCurTransfer = &(((CChainObject<CReserveTransfer> *)exportObjects[i])->object);
1527 CReserveTransfer &curTransfer = *pCurTransfer;
1529 //printf("currency transfer #%d:\n%s\n", i, curTransfer.ToUniValue().write(1,2).c_str());
1530 CCurrencyDefinition currencyDest = ConnectedChains.GetCachedCurrency(curTransfer.destCurrencyID);
1532 if (!currencyDest.IsValid())
1534 printf("%s: invalid currency or currency not found %s\n", __func__, EncodeDestination(CIdentityID(curTransfer.destCurrencyID)).c_str());
1535 LogPrintf("%s: invalid currency or currency not found %s\n", __func__, EncodeDestination(CIdentityID(curTransfer.destCurrencyID)).c_str());
1539 if (curTransfer.IsValid())
1544 if (curTransfer.flags & curTransfer.FEE_OUTPUT)
1546 feeOutputStart = true;
1550 // if first fee output, calculate what they all should be
1551 if (nFeeOutputs == 1)
1553 feeOutputs = CCrossChainExport::CalculateExportFee(transferFees, numTransfers);
1556 //printf("%s: transferFees, numTransfers: %s, %d\n", __func__, transferFees.ToUniValue().write().c_str(), numTransfers);
1557 //printf("%s: feeOutputs: %s\n", __func__, feeOutputs.ToUniValue().write().c_str());
1559 if (curTransfer.nValue > feeOutputs.valueMap[curTransfer.currencyID])
1561 // TODO: we are going to allow rewriting of the fee out value for now
1562 // to accomodate prior versions that calculated too much fee. we may or may not want to keep this
1563 // behavior, in favor of the more restricted, refund-only rewriting that is
1564 // commented out below
1565 curTransfer.nValue = feeOutputs.valueMap[curTransfer.currencyID];
1566 if (curTransfer.nValue == 0)
1568 // this currency will not have a fee output at all
1572 // if this is a refund, we will adjust the output, otherwise, reject the transaction
1573 if (importCurrencyState.IsRefunding())
1575 curTransfer.nValue = feeOutputs.valueMap[curTransfer.currencyID];
1576 if (curTransfer.nValue == 0)
1578 // this currency will not have a fee output at all
1585 printf("%s: Too much fee taken by transfer in export %s\n", __func__, curTransfer.ToUniValue().write().c_str());
1586 LogPrintf("%s: Too much fee taken %s\n", __func__, curTransfer.ToUniValue().write().c_str());
1591 // erase so any repeats will be caught
1592 feeOutputs.valueMap.erase(curTransfer.currencyID);
1594 else if (feeOutputStart)
1596 printf("%s: Invalid non-fee output after fee output %s\n", __func__, curTransfer.ToUniValue().write().c_str());
1597 LogPrintf("%s: Invalid non-fee output after fee output %s\n", __func__, curTransfer.ToUniValue().write().c_str());
1602 if (curTransfer.nFees < curTransfer.CalculateTransferFee(curTransfer.destination))
1604 printf("%s: Incorrect fee sent with export %s\n", __func__, curTransfer.ToUniValue().write().c_str());
1605 LogPrintf("%s: Incorrect fee sent with export %s\n", __func__, curTransfer.ToUniValue().write().c_str());
1609 if (curTransfer.currencyID == systemDestID && !(curTransfer.flags & (curTransfer.MINT_CURRENCY | curTransfer.PREALLOCATE)))
1611 nativeIn += (curTransfer.nValue + curTransfer.nFees);
1615 // when minting new currency or burning a fractional for conversion to a reserve,
1617 if ((curTransfer.flags & (curTransfer.MINT_CURRENCY | curTransfer.PREALLOCATE)))
1619 nativeIn += curTransfer.nFees;
1623 AddReserveInput(curTransfer.currencyID, curTransfer.nValue + curTransfer.nFees);
1626 if (!(curTransfer.flags & (curTransfer.PRECONVERT | curTransfer.CONVERT)))
1628 transferFees.valueMap[curTransfer.currencyID] += curTransfer.nFees;
1631 if (curTransfer.flags & curTransfer.PREALLOCATE)
1633 // look up preallocation in the currency definition based on ID, add the correct amount to input
1634 // and to the transfer for output
1635 if (currencyDest.preAllocation.size() && !preAllocMap.size())
1637 for (auto &onePreAlloc : currencyDest.preAllocation)
1639 preAllocMap.insert(onePreAlloc);
1641 // TODO: this is where we should add percentage based pre-allocation calculations
1644 auto it = preAllocMap.find(GetDestinationID(TransferDestinationToDestination(curTransfer.destination)));
1645 if (it == preAllocMap.end() || it->second != curTransfer.nValue)
1647 printf("%s: Invalid preallocation transfer\n", __func__);
1648 LogPrintf("%s: Invalid preallocation transfer\n", __func__);
1654 if (curTransfer.flags & curTransfer.PRECONVERT)
1656 // first time through with preconvert, initialize the starting currency state
1657 if (!initialCurrencyState.IsValid())
1659 initialCurrencyState = ConnectedChains.GetCurrencyState(importCurrencyID, currencyDest.startBlock - 1);
1660 if (!initialCurrencyState.IsValid())
1662 printf("%s: Invalid currency for preconversion %s\n", __func__, curTransfer.ToUniValue().write().c_str());
1663 LogPrintf("%s: Invalid currency for preconversion %s\n", __func__, curTransfer.ToUniValue().write().c_str());
1668 // get currency index
1669 auto curIndexIt = currencyIndexMap.find(curTransfer.currencyID);
1670 if (curIndexIt == currencyIndexMap.end())
1672 printf("%s: Invalid currency for conversion %s\n", __func__, curTransfer.ToUniValue().write().c_str());
1673 LogPrintf("%s: Invalid currency for conversion %s\n", __func__, curTransfer.ToUniValue().write().c_str());
1676 int curIdx = curIndexIt->second;
1678 // output the converted amount, minus fees, and generate a normal output that spends the net input of the import as native
1679 // difference between all potential value out and what was taken unconverted as a fee in our fee output
1680 CAmount preConversionFee = 0;
1681 CAmount newConvertedFees = 0;
1682 CAmount newCurrencyConverted = 0;
1683 CAmount feesConverted = 0;
1685 preConversionFee = CalculateConversionFee(curTransfer.nValue);
1686 if (preConversionFee > curTransfer.nValue)
1688 preConversionFee = curTransfer.nValue;
1690 CAmount valueOut = curTransfer.nValue - preConversionFee;
1692 if (!(curTransfer.flags & curTransfer.FEE_OUTPUT))
1694 newCurrencyConverted = initialCurrencyState.ReserveToNativeRaw(valueOut, initialCurrencyState.conversionPrice[curIdx]);
1695 CAmount totalReserveFee = preConversionFee + curTransfer.nFees;
1697 // see if fees should be paid in the new currency or not
1698 if ((currencyDest.ChainOptions() & currencyDest.OPTION_FEESASRESERVE) || currencyDest.IsToken())
1700 AddReserveConversionFees(curTransfer.currencyID, preConversionFee);
1701 CAmount reserveIn = valueOut;
1704 totalCarveOut = importCurrencyDef.GetTotalCarveOut();
1706 if (totalCarveOut > 0 && totalCarveOut < SATOSHIDEN)
1708 CAmount newReserveIn = CCurrencyState::NativeToReserveRaw(reserveIn, SATOSHIDEN - totalCarveOut);
1709 totalCarveOuts.valueMap[curTransfer.currencyID] += reserveIn - newReserveIn;
1710 reserveIn = newReserveIn;
1713 if (curTransfer.currencyID != systemDestID)
1715 // if this is a fractional currency, everything but fees and carveouts stay in reserve deposit
1716 // else all that would be reserves is sent to chain ID
1717 if (!importCurrencyDef.IsFractional())
1719 AddReserveOutput(curTransfer.currencyID, reserveIn);
1720 std::vector<CTxDestination> dests({CIdentityID(importCurrencyID)});
1721 CTokenOutput ro = CTokenOutput(curTransfer.currencyID, reserveIn);
1722 vOutputs.push_back(CTxOut(0, MakeMofNCCScript(CConditionObj<CTokenOutput>(EVAL_RESERVE_OUTPUT, dests, 1, &ro))));
1727 newConvertedFees = totalReserveFee;
1728 totalReserveFee = 0;
1729 // leave it in reserve deposit
1730 if (!importCurrencyDef.IsFractional())
1732 nativeOut += reserveIn;
1733 vOutputs.push_back(CTxOut(reserveIn, GetScriptForDestination(CIdentityID(importCurrencyID))));
1739 if (curTransfer.destCurrencyID == systemDestID)
1741 newConvertedFees = initialCurrencyState.ReserveToNativeRaw(totalReserveFee, initialCurrencyState.conversionPrice[curIdx]);
1742 AddReserveConversionFees(systemDestID, newConvertedFees);
1743 feesConverted = newConvertedFees;
1744 totalReserveFee = 0;
1746 else if (curTransfer.currencyID == systemDestID)
1748 AddReserveConversionFees(curTransfer.currencyID, preConversionFee);
1749 newConvertedFees = totalReserveFee;
1750 totalReserveFee = 0;
1754 AddReserveConversionFees(curTransfer.currencyID, preConversionFee);
1758 // add reserve fees, if any as input funds to be taken by fee outputs
1759 if (totalReserveFee)
1761 transferFees.valueMap[curTransfer.currencyID] += totalReserveFee;
1763 if (newConvertedFees)
1765 transferFees.valueMap[systemDestID] += newConvertedFees;
1770 // input comes from fees
1771 newCurrencyConverted = initialCurrencyState.ReserveToNativeRaw(curTransfer.nValue, initialCurrencyState.conversionPrice[curIdx]);
1774 if (newCurrencyConverted | feesConverted)
1776 preConvertedOutput.valueMap[curTransfer.currencyID] += newCurrencyConverted + feesConverted;
1777 AddNativeOutConverted(curTransfer.currencyID, newCurrencyConverted + feesConverted);
1778 if (curTransfer.destCurrencyID == systemDestID)
1780 nativeOut += newCurrencyConverted;
1781 newOut = CTxOut(newCurrencyConverted, GetScriptForDestination(TransferDestinationToDestination(curTransfer.destination)));
1785 AddReserveOutConverted(curTransfer.destCurrencyID, newCurrencyConverted + feesConverted);
1786 AddReserveOutput(curTransfer.destCurrencyID, newCurrencyConverted);
1787 std::vector<CTxDestination> dests = std::vector<CTxDestination>({TransferDestinationToDestination(curTransfer.destination)});
1788 CTokenOutput ro = CTokenOutput(curTransfer.destCurrencyID, newCurrencyConverted);
1789 newOut = CTxOut(0, MakeMofNCCScript(CConditionObj<CTokenOutput>(EVAL_RESERVE_OUTPUT, dests, 1, &ro)));
1793 else if (curTransfer.flags & curTransfer.CONVERT)
1795 if (curTransfer.currencyID == curTransfer.destCurrencyID)
1797 printf("%s: Conversion does not specify two currencies\n", __func__);
1798 LogPrintf("%s: Conversion does not specify two currencies\n", __func__);
1802 // either the source or destination must be a reserve currency of the other fractional currency
1803 // if destination is a fractional currency of a reserve, we will mint currency
1804 // if not, we will burn currency
1805 bool toFractional = importCurrencyID == curTransfer.destCurrencyID &&
1806 currencyDest.IsFractional() &&
1807 currencyIndexMap.count(curTransfer.currencyID);
1809 CCurrencyDefinition sourceCurrency = ConnectedChains.GetCachedCurrency(curTransfer.currencyID);
1811 if (!sourceCurrency.IsValid())
1813 printf("%s: Currency specified for conversion not found\n", __func__);
1814 LogPrintf("%s: Currency specified for conversion not found\n", __func__);
1818 if (!(toFractional ||
1819 (importCurrencyID == curTransfer.currencyID &&
1820 sourceCurrency.IsFractional() &&
1821 currencyIndexMap.count(curTransfer.destCurrencyID))))
1823 printf("%s: Conversion must be between a fractional currency and one of its reserves\n", __func__);
1824 LogPrintf("%s: Conversion must be between a fractional currency and one of its reserves\n", __func__);
1828 CCurrencyDefinition &fractionalCurrency = toFractional ? currencyDest : sourceCurrency;
1829 CCurrencyDefinition &reserveCurrency = toFractional ? sourceCurrency : currencyDest;
1830 int reserveIdx = currencyIndexMap[reserveCurrency.GetID()];
1832 assert(fractionalCurrency.IsValid() &&
1833 reserveCurrency.IsValid() &&
1834 fractionalCurrency.currencies[reserveIdx] == reserveCurrency.GetID());
1836 // now, we know that we are converting from the source currency to the
1837 // destination currency and also that one of them is a reserve of the other
1838 // we convert using the provided currency state, and we update the currency
1839 // state to include newly minted or burned currencies.
1841 CAmount preConversionFee = 0;
1842 CAmount newConvertedFees = 0;
1843 CAmount newCurrencyConverted = 0;
1844 CAmount feesConverted = 0;
1845 if (!(curTransfer.flags & curTransfer.FEE_OUTPUT))
1847 preConversionFee = CalculateConversionFee(curTransfer.nValue);
1848 if (preConversionFee > curTransfer.nValue)
1850 preConversionFee = curTransfer.nValue;
1852 valueOut = curTransfer.nValue - preConversionFee;
1856 reserveConverted.valueMap[curTransfer.currencyID] += valueOut;
1857 newCurrencyConverted = importCurrencyState.ReserveToNativeRaw(valueOut, importCurrencyState.conversionPrice[reserveIdx]);
1861 fractionalConverted.valueMap[curTransfer.destCurrencyID] += valueOut;
1862 newCurrencyConverted = importCurrencyState.NativeToReserveRaw(valueOut, importCurrencyState.conversionPrice[reserveIdx]);
1865 CAmount totalSourceFee = preConversionFee + curTransfer.nFees;
1867 // see if fees should be converted or not
1868 if ((currencyDest.ChainOptions() & currencyDest.OPTION_FEESASRESERVE) || importCurrencyDef.IsToken())
1870 AddReserveConversionFees(curTransfer.currencyID, preConversionFee);
1871 if (curTransfer.currencyID == systemDestID)
1873 nativeConversionFees += preConversionFee;
1878 // convert to fractional reserve if toFractional, and native currency of chain if not and fractional is not native
1881 reserveConverted.valueMap[curTransfer.currencyID] += totalSourceFee;
1882 newConvertedFees = importCurrencyState.ReserveToNativeRaw(totalSourceFee, importCurrencyState.conversionPrice[reserveIdx]);
1883 AddReserveConversionFees(curTransfer.destCurrencyID, newConvertedFees);
1884 feesConverted = newConvertedFees;
1889 fractionalConverted.valueMap[curTransfer.currencyID] += totalSourceFee;
1890 AddReserveConversionFees(curTransfer.currencyID, preConversionFee);
1894 // add reserve fees, if any as input funds to validate fee outputs
1897 transferFees.valueMap[curTransfer.currencyID] += totalSourceFee;
1899 if (newConvertedFees)
1901 transferFees.valueMap[curTransfer.destCurrencyID] += newConvertedFees;
1906 valueOut = curTransfer.nValue;
1910 reserveConverted.valueMap[curTransfer.currencyID] += valueOut;
1911 newCurrencyConverted = importCurrencyState.ReserveToNativeRaw(valueOut, importCurrencyState.conversionPrice[reserveIdx]);
1915 fractionalConverted.valueMap[curTransfer.destCurrencyID] += valueOut;
1916 newCurrencyConverted = importCurrencyState.NativeToReserveRaw(valueOut, importCurrencyState.conversionPrice[reserveIdx]);
1920 if (newCurrencyConverted | feesConverted)
1924 AddNativeOutConverted(curTransfer.currencyID, newCurrencyConverted + feesConverted);
1925 if (curTransfer.destCurrencyID == systemDestID)
1927 nativeOut += newCurrencyConverted;
1931 AddReserveOutConverted(curTransfer.destCurrencyID, newCurrencyConverted + feesConverted);
1932 AddReserveOutput(curTransfer.destCurrencyID, newCurrencyConverted);
1937 AddReserveOutConverted(curTransfer.destCurrencyID, newCurrencyConverted + feesConverted);
1938 if (curTransfer.destCurrencyID == systemDestID)
1940 nativeOut += newCurrencyConverted;
1944 AddReserveOutput(curTransfer.destCurrencyID, newCurrencyConverted);
1947 // burn the input fractional currency
1948 AddNativeOutConverted(curTransfer.currencyID, -valueOut);
1951 if ((newCurrencyConverted | feesConverted) && curTransfer.destCurrencyID == systemDestID)
1953 newOut = CTxOut(newCurrencyConverted, GetScriptForDestination(TransferDestinationToDestination(curTransfer.destination)));
1955 else if (newCurrencyConverted | feesConverted)
1957 std::vector<CTxDestination> dests = std::vector<CTxDestination>({TransferDestinationToDestination(curTransfer.destination)});
1958 CTokenOutput ro = CTokenOutput(curTransfer.destCurrencyID, newCurrencyConverted);
1959 newOut = CTxOut(0, MakeMofNCCScript(CConditionObj<CTokenOutput>(EVAL_RESERVE_OUTPUT, dests, 1, &ro)));
1964 // emit a reserve exchange output
1965 cp = CCinit(&CC, EVAL_RESERVE_EXCHANGE);
1966 CPubKey pk = CPubKey(ParseHex(CC.CChexstr));
1968 std::vector<CTxDestination> dests = std::vector<CTxDestination>({pk.GetID(), TransferDestinationToDestination(curTransfer.destination)});
1969 CAmount conversionFees = CalculateConversionFee(curTransfer.nValue);
1970 AddReserveConversionFees(curTransfer.currencyID, conversionFees);
1971 AddReserveOutConverted(curTransfer.currencyID, curTransfer.nValue - conversionFees);
1972 AddReserveOutput(curTransfer.currencyID, curTransfer.nValue);
1974 CReserveExchange rex = CReserveExchange(CReserveExchange::VALID, curTransfer.currencyID, curTransfer.nValue);
1975 newOut = CTxOut(0, MakeMofNCCScript(CConditionObj<CReserveExchange>(EVAL_RESERVE_EXCHANGE, dests, 1, &rex)));
1978 else if ((curTransfer.flags & curTransfer.SEND_BACK) &&
1979 curTransfer.nValue >= (curTransfer.DEFAULT_PER_STEP_FEE << 2) &&
1980 curTransfer.currencyID == nativeSourceCurrencyID)
1982 // emit a reserve exchange output
1983 cp = CCinit(&CC, EVAL_RESERVE_TRANSFER);
1984 CPubKey pk = CPubKey(ParseHex(CC.CChexstr));
1986 // transfer it back to the source chain and to our address
1987 std::vector<CTxDestination> dests = std::vector<CTxDestination>({pk.GetID(), TransferDestinationToDestination(curTransfer.destination)});
1989 // naturally compress a full ID to an ID destination, since it is going back where it came from
1990 CTxDestination sendBackAddr = TransferDestinationToDestination(curTransfer.destination);
1992 CAmount fees = curTransfer.CalculateFee(curTransfer.flags, curTransfer.nValue).valueMap.begin()->second;
1994 CReserveTransfer rt = CReserveTransfer(CReserveExchange::VALID,
1995 curTransfer.currencyID,
1996 curTransfer.nValue - fees,
1998 curTransfer.currencyID,
1999 DestinationToTransferDestination(sendBackAddr));
2001 std::vector<CTxDestination> indexDests({CKeyID(ConnectedChains.ThisChain().GetConditionID(EVAL_RESERVE_TRANSFER)),
2002 CKeyID(currencyDest.systemID == ConnectedChains.ThisChain().GetID() ?
2003 currencyDest.GetID() :
2004 currencyDest.systemID)});
2005 CScript sendBackScript = MakeMofNCCScript(CConditionObj<CReserveTransfer>(EVAL_RESERVE_TRANSFER, dests, 1, &rt),
2008 // if this is sending back to the same chain as its native currency, make it a native output
2009 if (systemDestID == nativeSourceCurrencyID)
2011 nativeOut += curTransfer.nValue;
2012 newOut = CTxOut(curTransfer.nValue, sendBackScript);
2016 AddReserveOutput(curTransfer.currencyID, curTransfer.nValue);
2017 newOut = CTxOut(0, sendBackScript);
2022 // if we are supposed to burn a currency, it must be the import currency, and it
2023 // is removed from the supply, which would change all calculations for price
2024 if (curTransfer.flags & (curTransfer.BURN_CHANGE_PRICE | curTransfer.BURN_CHANGE_WEIGHT))
2026 // if the source is fractional currency, it is burned
2027 if (curTransfer.currencyID != importCurrencyID || !(importCurrencyDef.IsFractional() || importCurrencyDef.IsToken()))
2029 CCurrencyDefinition sourceCurrency = ConnectedChains.GetCachedCurrency(curTransfer.currencyID);
2030 printf("%s: Attempting to burn %s, which is either not a token or fractional currency or not the import currency %s\n", __func__, sourceCurrency.name.c_str(), importCurrencyDef.name.c_str());
2031 LogPrintf("%s: Attempting to burn %s, which is either not a token or fractional currency or not the import currency %s\n", __func__, sourceCurrency.name.c_str(), importCurrencyDef.name.c_str());
2034 if (curTransfer.flags & curTransfer.BURN_CHANGE_WEIGHT)
2036 printf("%s: burning %s to change weight is not supported\n", __func__, importCurrencyDef.name.c_str());
2037 LogPrintf("%s: burning %s to change weight is not supported\n", __func__, importCurrencyDef.name.c_str());
2040 // burn the input fractional currency
2041 AddNativeOutConverted(curTransfer.currencyID, -curTransfer.nValue);
2042 burnedChangePrice += curTransfer.nValue;
2044 else if (systemDestID == curTransfer.destCurrencyID)
2046 nativeOut += curTransfer.nValue;
2047 newOut = CTxOut(curTransfer.nValue, GetScriptForDestination(TransferDestinationToDestination(curTransfer.destination)));
2051 // generate a reserve output of the amount indicated, less fees
2052 // we will send using a reserve output, fee will be paid through coinbase by converting from reserve or not, depending on currency settings
2053 std::vector<CTxDestination> dests = std::vector<CTxDestination>({TransferDestinationToDestination(curTransfer.destination)});
2054 CTokenOutput ro = CTokenOutput(curTransfer.destCurrencyID, curTransfer.nValue);
2056 // if this is a minting of currency
2057 // this is used for both pre-allocation and also centrally, algorithmically, or externally controlled currencies
2058 if ((curTransfer.flags & (curTransfer.MINT_CURRENCY | curTransfer.PREALLOCATE)) && curTransfer.destCurrencyID == importCurrencyID)
2060 // pre-allocation is accounted for outside of this
2061 // minting is emitted in new currency state
2062 if (curTransfer.flags & curTransfer.MINT_CURRENCY)
2064 totalMinted += ro.nValue;
2066 AddNativeOutConverted(curTransfer.destCurrencyID, ro.nValue);
2067 if (curTransfer.destCurrencyID != systemDestID)
2069 AddReserveOutConverted(curTransfer.destCurrencyID, ro.nValue);
2072 AddReserveOutput(curTransfer.destCurrencyID, ro.nValue);
2073 newOut = CTxOut(0, MakeMofNCCScript(CConditionObj<CTokenOutput>(EVAL_RESERVE_OUTPUT, dests, 1, &ro)));
2076 if (newOut.nValue < 0)
2078 // if we get here, we have absorbed the entire transfer
2079 LogPrintf("%s: skip creating output for import to %s\n", __func__, currencyDest.name.c_str());
2083 vOutputs.push_back(newOut);
2088 printf("%s: Invalid reserve transfer on export\n", __func__);
2089 LogPrintf("%s: Invalid reserve transfer on export\n", __func__);
2095 if ((totalCarveOuts = totalCarveOuts.CanonicalMap()).valueMap.size())
2097 // add carveout outputs
2098 for (auto &oneCur : totalCarveOuts.valueMap)
2100 // if we are creating a reserve import for native currency, it must be spent from native inputs on the destination system
2101 if (oneCur.first == systemDestID)
2103 nativeOut += oneCur.second;
2104 CTxOut(oneCur.second, GetScriptForDestination(CIdentityID(importCurrencyID)));
2108 // generate a reserve output of the amount indicated, less fees
2109 // we will send using a reserve output, fee will be paid through coinbase by converting from reserve or not, depending on currency settings
2110 std::vector<CTxDestination> dests = std::vector<CTxDestination>({CIdentityID(importCurrencyID)});
2111 CTokenOutput ro = CTokenOutput(oneCur.first, oneCur.second);
2112 AddReserveOutput(oneCur.first, oneCur.second);
2113 CTxOut(0, MakeMofNCCScript(CConditionObj<CTokenOutput>(EVAL_RESERVE_OUTPUT, dests, 1, &ro)));
2118 // remove burned currency from supply
2119 if (burnedChangePrice > 0)
2121 if (!(burnedChangePrice <= newCurrencyState.supply))
2123 printf("%s: Invalid burn amount %ld\n", __func__, burnedChangePrice);
2124 LogPrintf("%s: Invalid burn amount %ld\n", __func__, burnedChangePrice);
2127 newCurrencyState.supply -= burnedChangePrice;
2130 if (reserveConverted.CanonicalMap().valueMap.size() || fractionalConverted.CanonicalMap().valueMap.size())
2132 if (importCurrencyDef.IsFractional())
2134 CCurrencyState dummyCurState;
2135 newCurrencyState.conversionPrice =
2136 importCurrencyState.ConvertAmounts(reserveConverted.AsCurrencyVector(importCurrencyState.currencies),
2137 fractionalConverted.AsCurrencyVector(importCurrencyState.currencies),
2141 std::vector<CAmount> vResConverted = reserveConverted.AsCurrencyVector(newCurrencyState.currencies);
2142 std::vector<CAmount> vResOutConverted = ReserveOutConvertedMap().AsCurrencyVector(newCurrencyState.currencies);
2143 std::vector<CAmount> vFracConverted = fractionalConverted.AsCurrencyVector(newCurrencyState.currencies);
2144 std::vector<CAmount> vFracOutConverted = (NativeOutConvertedMap() - preConvertedOutput).AsCurrencyVector(newCurrencyState.currencies);
2146 for (int i = 0; i < newCurrencyState.currencies.size(); i++)
2148 newCurrencyState.reserveIn[i] = vResConverted[i];
2149 newCurrencyState.reserveOut[i] = vResOutConverted[i];
2150 newCurrencyState.reserves[i] += vResConverted[i] - vResOutConverted[i];
2151 newCurrencyState.nativeIn[i] = vFracConverted[i];
2152 newCurrencyState.supply += vFracOutConverted[i];
2157 newCurrencyState.UpdateWithEmission(totalMinted);
2160 // now, pull out all fractional data and sort out native vs. fractional
2161 if (currencies.count(systemDestID))
2163 CReserveInOuts fractionalInOuts = currencies[systemDestID];
2164 newCurrencyState.nativeConversionFees = fractionalInOuts.reserveConversionFees;
2166 newCurrencyState.conversionFees = ReserveConversionFeesMap().AsCurrencyVector(newCurrencyState.currencies);
2167 newCurrencyState.fees = transferFees.AsCurrencyVector(newCurrencyState.currencies);
2169 // double check that the export fee taken as the fee output matches the export fee that should have been taken
2170 CCurrencyValueMap ReserveInputs;
2171 CCurrencyValueMap ReserveOutputs;
2172 CAmount systemOutConverted = 0;
2173 for (auto &oneInOut : currencies)
2175 ReserveInputs.valueMap[importCurrencyID] += oneInOut.second.nativeOutConverted;
2176 if (oneInOut.first == systemDestID)
2178 if (systemDestID == importCurrencyID)
2180 systemOutConverted += oneInOut.second.nativeOutConverted;
2184 systemOutConverted += oneInOut.second.reserveOutConverted;
2187 if (oneInOut.second.reserveIn || oneInOut.second.reserveOutConverted)
2189 ReserveInputs.valueMap[oneInOut.first] = oneInOut.second.reserveIn + oneInOut.second.reserveOutConverted;
2191 if (oneInOut.second.reserveOut)
2193 ReserveOutputs.valueMap[oneInOut.first] = oneInOut.second.reserveOut;
2196 if (nativeIn || systemOutConverted)
2198 ReserveInputs.valueMap[importCurrencyDef.systemID] = nativeIn + systemOutConverted;
2202 ReserveOutputs.valueMap[importCurrencyDef.systemID] = nativeOut;
2204 CCrossChainExport ccx(systemDestID, numTransfers, ReserveInputs, transferFees);
2205 //printf("ReserveInputs: %s\nReserveOutputs: %s\nReserveInputs - ReserveOutputs: %s\n", ReserveInputs.ToUniValue().write(1,2).c_str(), ReserveOutputs.ToUniValue().write(1,2).c_str(), (ReserveInputs - ReserveOutputs).ToUniValue().write(1,2).c_str());
2206 if (ReserveInputs - ReserveOutputs < ccx.CalculateImportFee())
2208 printf("%s: Too much fee taken by export, ReserveInputs: %s\nReserveOutputs: %s\nccx.CalculateImportFee(): %s\nccx: %s\n", __func__,
2209 ReserveInputs.ToUniValue().write(1,2).c_str(),
2210 ReserveOutputs.ToUniValue().write(1,2).c_str(),
2211 ccx.CalculateImportFee().ToUniValue().write(1,2).c_str(),
2212 ccx.ToUniValue().write(1,2).c_str());
2213 LogPrintf("%s: Too much fee taken by export\n", __func__);
2219 CMutableTransaction &CReserveTransactionDescriptor::AddConversionInOuts(CMutableTransaction &conversionTx, std::vector<CInputDescriptor> &conversionInputs, const CCurrencyValueMap &_exchangeRates, const CCurrencyState *pCurrencyState) const
2221 if (!IsReserveExchange() || IsFillOrKillFail())
2223 return conversionTx;
2226 bool noExchangeRate = false;
2227 CCurrencyState dummy;
2228 const CCurrencyState ¤cyState = pCurrencyState ? *pCurrencyState : dummy;
2230 // set exchange rates as well as we can, either from explicit rates or currency state if possible
2231 CCurrencyValueMap __exchangeRates;
2232 const CCurrencyValueMap *pExchangeRates = &__exchangeRates;
2233 if (_exchangeRates.valueMap.size() != 0)
2235 pExchangeRates = &_exchangeRates;
2239 if (pCurrencyState && currencyState.IsFractional())
2241 __exchangeRates = CCurrencyValueMap(currencyState.currencies, currencyState.PricesInReserve());
2245 noExchangeRate = true;
2248 const CCurrencyValueMap &exchangeRates = *pExchangeRates;
2250 CAmount nativeFeesLeft = nativeConversionFees;
2251 for (auto &oneEntry : exchangeRates.valueMap)
2253 auto it = currencies.find(oneEntry.first);
2254 if (it == currencies.end())
2256 LogPrintf("%s: invalid conversion with no exchange rate, currency: %s\n", __func__, EncodeDestination(CIdentityID(oneEntry.first)).c_str());
2260 nativeFeesLeft += currencyState.ReserveToNativeRaw(it->second.reserveConversionFees, oneEntry.second);
2264 uint256 txHash = ptx->GetHash();
2266 for (auto &indexRex : vRex)
2269 ptx->vout[indexRex.first].scriptPubKey.IsPayToCryptoCondition(p);
2272 CCcontract_info *cp;
2274 CAmount fee = CalculateConversionFee(indexRex.second.nValue);
2275 CAmount amount = indexRex.second.nValue - fee;
2276 CAmount nativeFee, nativeAmount;
2278 auto rateIt = exchangeRates.valueMap.find(indexRex.second.currencyID);
2279 if (rateIt == exchangeRates.valueMap.end())
2284 CAmount exchangeRate = rateIt->second;
2286 // if already native fees, don't convert, otherwise, do
2287 if (!(indexRex.second.flags & indexRex.second.TO_RESERVE))
2290 nativeAmount = ptx->vout[indexRex.first].nValue;
2291 amount = currencyState.NativeToReserveRaw(nativeAmount, exchangeRate);
2292 fee = currencyState.NativeToReserveRaw(nativeFee, exchangeRate);
2296 nativeFee = currencyState.ReserveToNativeRaw(fee, exchangeRate);
2297 nativeAmount = currencyState.ReserveToNativeRaw(amount, exchangeRate);
2300 if (nativeFee > nativeFeesLeft)
2302 nativeFee = nativeFeesLeft;
2304 nativeFeesLeft -= nativeFee;
2307 conversionTx.vin.push_back(CTxIn(txHash, indexRex.first, CScript()));
2309 // ... and input descriptor. we leave the CTxIn empty and use the one in the corresponding input, using the input descriptor for only
2311 conversionInputs.push_back(CInputDescriptor(ptx->vout[indexRex.first].scriptPubKey, ptx->vout[indexRex.first].nValue, CTxIn()));
2313 // if we should emit a reserve transfer or normal reserve output, sending output only occurs when converting from
2314 // native to a reserve currency
2315 if (indexRex.second.flags & indexRex.second.SEND_OUTPUT &&
2316 indexRex.second.flags & indexRex.second.TO_RESERVE &&
2317 nativeAmount > (CReserveTransfer::DEFAULT_PER_STEP_FEE << 1) << 1)
2319 cp = CCinit(&CC, EVAL_RESERVE_TRANSFER);
2320 CPubKey pk = CPubKey(ParseHex(CC.CChexstr));
2322 // send the entire amount to a reserve transfer output of the controller
2323 const auto &reserveCurrencyIt = ConnectedChains.ReserveCurrencies().find(indexRex.second.currencyID);
2324 if (reserveCurrencyIt != ConnectedChains.ReserveCurrencies().end())
2326 std::vector<CTxDestination> dests = std::vector<CTxDestination>({pk.GetID(), p.vKeys[0]});
2328 // create the transfer output with the converted amount less fees
2329 CReserveTransfer rt((uint32_t)CReserveTransfer::VALID,
2330 indexRex.second.currencyID,
2331 amount - (CReserveTransfer::DEFAULT_PER_STEP_FEE << 1),
2332 CReserveTransfer::DEFAULT_PER_STEP_FEE << 1,
2333 reserveCurrencyIt->second.systemID,
2334 CTransferDestination(p.vKeys[0].which(), GetDestinationBytes(p.vKeys[0])));
2336 // cast object to the most derived class to avoid template errors to a least derived class
2337 CTxDestination rtIndexDest(CKeyID(ConnectedChains.ThisChain().GetConditionID(EVAL_RESERVE_TRANSFER)));
2338 std::vector<CTxDestination> indexDests({CKeyID(ConnectedChains.ThisChain().GetConditionID(EVAL_RESERVE_TRANSFER)),
2339 CKeyID(reserveCurrencyIt->second.systemID == ConnectedChains.ThisChain().GetID() ?
2340 reserveCurrencyIt->second.GetID() :
2341 reserveCurrencyIt->second.systemID)});
2342 conversionTx.vout.push_back(CTxOut(0, MakeMofNCCScript(CConditionObj<CReserveTransfer>(EVAL_RESERVE_TRANSFER, dests, 1, &rt),
2346 else if (indexRex.second.flags & indexRex.second.TO_RESERVE)
2348 // send the net amount to the indicated destination, which is the first entry in the destinations of the original reserve/exchange by protocol
2349 std::vector<CTxDestination> dests = std::vector<CTxDestination>({p.vKeys[0]});
2351 // create the output with the unconverted amount less fees
2352 CTokenOutput ro(indexRex.second.currencyID, amount);
2354 conversionTx.vout.push_back(MakeCC1ofAnyVout(EVAL_RESERVE_OUTPUT, 0, dests, ro));
2358 // convert amount to native from reserve and send as normal output
2359 amount = currencyState.ReserveToNative(amount, exchangeRates.valueMap.find(indexRex.second.currencyID)->second);
2360 conversionTx.vout.push_back(CTxOut(amount, GetScriptForDestination(p.vKeys[0])));
2363 return conversionTx;
2366 CCurrencyValueMap CReserveTransactionDescriptor::ReserveInputMap() const
2368 CCurrencyValueMap retVal;
2369 for (auto &oneInOut : currencies)
2371 if (oneInOut.second.reserveIn)
2373 retVal.valueMap[oneInOut.first] = oneInOut.second.reserveIn;
2379 CCurrencyValueMap CReserveTransactionDescriptor::ReserveOutputMap() const
2381 CCurrencyValueMap retVal;
2382 for (auto &oneInOut : currencies)
2384 if (oneInOut.second.reserveOut)
2386 retVal.valueMap[oneInOut.first] = oneInOut.second.reserveOut;
2392 CCurrencyValueMap CReserveTransactionDescriptor::ReserveOutConvertedMap() const
2394 CCurrencyValueMap retVal;
2395 for (auto &oneInOut : currencies)
2397 if (oneInOut.second.reserveOutConverted)
2399 retVal.valueMap[oneInOut.first] = oneInOut.second.reserveOutConverted;
2405 CCurrencyValueMap CReserveTransactionDescriptor::NativeOutConvertedMap() const
2407 CCurrencyValueMap retVal;
2408 for (auto &oneInOut : currencies)
2410 if (oneInOut.second.nativeOutConverted)
2412 retVal.valueMap[oneInOut.first] = oneInOut.second.nativeOutConverted;
2418 CCurrencyValueMap CReserveTransactionDescriptor::ReserveConversionFeesMap() const
2420 CCurrencyValueMap retVal;
2421 for (auto &oneInOut : currencies)
2423 if (oneInOut.second.reserveConversionFees)
2425 retVal.valueMap[oneInOut.first] = oneInOut.second.reserveConversionFees;
2431 std::vector<CAmount> CReserveTransactionDescriptor::ReserveInputVec(const CCurrencyState &cState) const
2433 std::vector<CAmount> retVal(cState.currencies.size());
2434 std::map<uint160, int> curMap = cState.GetReserveMap();
2435 for (auto &oneInOut : currencies)
2437 retVal[curMap[oneInOut.first]] = oneInOut.second.reserveIn;
2442 std::vector<CAmount> CReserveTransactionDescriptor::ReserveOutputVec(const CCurrencyState &cState) const
2444 std::vector<CAmount> retVal(cState.currencies.size());
2445 std::map<uint160, int> curMap = cState.GetReserveMap();
2446 for (auto &oneInOut : currencies)
2448 retVal[curMap[oneInOut.first]] = oneInOut.second.reserveOut;
2453 std::vector<CAmount> CReserveTransactionDescriptor::ReserveOutConvertedVec(const CCurrencyState &cState) const
2455 std::vector<CAmount> retVal(cState.currencies.size());
2456 std::map<uint160, int> curMap = cState.GetReserveMap();
2457 for (auto &oneInOut : currencies)
2459 retVal[curMap[oneInOut.first]] = oneInOut.second.reserveOutConverted;
2464 std::vector<CAmount> CReserveTransactionDescriptor::NativeOutConvertedVec(const CCurrencyState &cState) const
2466 std::vector<CAmount> retVal(cState.currencies.size());
2467 std::map<uint160, int> curMap = cState.GetReserveMap();
2468 for (auto &oneInOut : currencies)
2470 retVal[curMap[oneInOut.first]] = oneInOut.second.nativeOutConverted;
2475 std::vector<CAmount> CReserveTransactionDescriptor::ReserveConversionFeesVec(const CCurrencyState &cState) const
2477 std::vector<CAmount> retVal(cState.currencies.size());
2478 std::map<uint160, int> curMap = cState.GetReserveMap();
2479 for (auto &oneInOut : currencies)
2481 retVal[curMap[oneInOut.first]] = oneInOut.second.reserveConversionFees;
2486 // this should be done no more than once to prepare a currency state to be updated to the next state
2487 // emission occurs for a block before any conversion or exchange and that impact on the currency state is calculated
2488 CCurrencyState &CCurrencyState::UpdateWithEmission(CAmount toEmit)
2490 initialSupply = supply;
2493 // if supply is 0, reserve must be zero, and we cannot function as a reserve currency
2494 if (!IsFractional() || supply <= 0 || CCurrencyValueMap(currencies, reserves) <= CCurrencyValueMap())
2498 emitted = supply = toEmit;
2510 // first determine current ratio by adding up all currency weights
2511 CAmount InitialRatio = 0;
2512 for (auto weight : weights)
2514 InitialRatio += weight;
2517 // to balance rounding with truncation, we statistically add a satoshi to the initial ratio
2518 static arith_uint256 bigSatoshi(SATOSHIDEN);
2519 arith_uint256 bigInitial(InitialRatio);
2520 arith_uint256 bigEmission(toEmit);
2521 arith_uint256 bigSupply(supply);
2523 arith_uint256 bigScratch = (bigInitial * bigSupply * bigSatoshi) / (bigSupply + bigEmission);
2524 arith_uint256 bigRatio = bigScratch / bigSatoshi;
2526 if (bigRatio >= bigSatoshi)
2528 bigScratch = arith_uint256(SATOSHIDEN) * arith_uint256(SATOSHIDEN);
2529 bigRatio = bigSatoshi;
2532 int64_t newRatio = bigRatio.GetLow64();
2533 int64_t remainder = (bigScratch - (bigRatio * SATOSHIDEN)).GetLow64();
2534 // form of bankers rounding, if odd, round up at half, if even, round down at half
2535 if (remainder > (SATOSHIDEN >> 1) || (remainder == (SATOSHIDEN >> 1) && newRatio & 1))
2540 // now, we must update all weights accordingly, based on the new, total ratio, by dividing the total among all the
2541 // weights, according to their current relative weight. because this also can be a source of rounding error, we will
2542 // distribute any modulus excess randomly among the currencies
2543 std::vector<CAmount> extraWeight(currencies.size());
2544 arith_uint256 bigRatioDelta(InitialRatio - newRatio);
2545 CAmount totalUpdates = 0;
2547 for (auto &weight : weights)
2549 CAmount weightDelta = (bigRatioDelta * arith_uint256(weight) / bigSatoshi).GetLow64();
2550 weight -= weightDelta;
2551 totalUpdates += weightDelta;
2554 CAmount updateExtra = (InitialRatio - newRatio) - totalUpdates;
2556 // if we have any extra, distribute it evenly and any mod, both deterministically and pseudorandomly
2559 CAmount forAll = updateExtra / currencies.size();
2560 CAmount forSome = updateExtra % currencies.size();
2562 // get deterministic seed for linear congruential pseudorandom number for shuffle
2563 int64_t seed = supply + forAll + forSome;
2564 auto prandom = std::minstd_rand0(seed);
2566 for (int i = 0; i < extraWeight.size(); i++)
2568 extraWeight[i] = forAll;
2575 // distribute the extra as evenly as possible
2576 std::shuffle(extraWeight.begin(), extraWeight.end(), prandom);
2577 for (int i = 0; i < weights.size(); i++)
2579 weights[i] -= extraWeight[i];
2583 // update initial supply from what we currently have
2585 supply = initialSupply + emitted;
2590 // From a vector of transaction pointers, match all that are valid orders and can be matched,
2591 // put them into a vector of reserve transaction descriptors, and return a new fractional reserve state,
2592 // if pConversionTx is present, this will add one output to it for each transaction included
2593 // and consider its size wen comparing to maxSerializeSize
2594 CCoinbaseCurrencyState CCoinbaseCurrencyState::MatchOrders(const std::vector<CReserveTransactionDescriptor> &orders,
2595 std::vector<CReserveTransactionDescriptor> &reserveFills,
2596 std::vector<CReserveTransactionDescriptor> &noFills,
2597 std::vector<const CReserveTransactionDescriptor *> &expiredFillOrKills,
2598 std::vector<const CReserveTransactionDescriptor *> &rejects,
2599 std::vector<CAmount> &prices,
2600 int32_t height, std::vector<CInputDescriptor> &conversionInputs,
2601 int64_t maxSerializeSize, int64_t *pInOutTotalSerializeSize, CMutableTransaction *pConversionTx,
2602 bool feesAsReserve) const
2604 // synthetic order book of limitBuys and limitSells sorted by limit, order of preference beyond limit sorting is random
2605 std::map<uint160, std::multimap<CAmount, CReserveTransactionDescriptor>> limitBuys;
2606 std::map<uint160, std::multimap<CAmount, CReserveTransactionDescriptor>> limitSells; // limit orders are prioritized by limit
2607 std::multimap<CAmount, CReserveTransactionDescriptor> marketOrders; // prioritized by fee rate
2608 CAmount totalNativeConversionFees = 0;
2609 CCurrencyValueMap totalReserveConversionFees;
2610 std::vector<CAmount> exchangeRates(PricesInReserve());
2611 CCurrencyValueMap exchangeRateMap = CCurrencyValueMap(currencies, exchangeRates);
2613 CMutableTransaction mConversionTx = pConversionTx ? *pConversionTx : CMutableTransaction();
2614 std::vector<CInputDescriptor> tmpConversionInputs(conversionInputs);
2616 int64_t totalSerializedSize = pInOutTotalSerializeSize ? *pInOutTotalSerializeSize + CCurrencyState::CONVERSION_TX_SIZE_MIN : CCurrencyState::CONVERSION_TX_SIZE_MIN;
2617 int64_t conversionSizeOverhead = 0;
2619 if (!(flags & FLAG_FRACTIONAL))
2621 return CCoinbaseCurrencyState();
2624 uint32_t tipTime = (chainActive.Height() >= height) ? chainActive[height]->nTime : chainActive.LastTip()->nTime;
2625 CCoinsViewCache view(pcoinsTip);
2627 // organize all valid reserve exchange transactions into multimaps, limitBuys, limitSells, and market orders
2628 // orders that should be treated as normal/failed fill or kill will go into refunds and invalid transactions into rejects
2629 for (int i = 0; i < orders.size(); i++)
2631 if (orders[i].IsValid() && orders[i].IsReserveExchange())
2633 // if this is a market order, put it in, if limit, put it in an order book, sorted by limit
2634 if (orders[i].IsMarket())
2636 CAmount fee = orders[i].AllFeesAsNative(*this) + ReserveToNative(orders[i].ReserveConversionFeesMap()) + orders[i].nativeConversionFees;
2637 CFeeRate feeRate = CFeeRate(fee, GetSerializeSize(CDataStream(SER_NETWORK, PROTOCOL_VERSION), *orders[i].ptx));
2638 marketOrders.insert(std::make_pair(feeRate.GetFeePerK(), orders[i]));
2642 assert(orders[i].IsLimit());
2643 // limit order, so put it in buy or sell
2644 if (orders[i].numBuys)
2646 limitBuys[orders[i].vRex[0].second.currencyID].insert(std::make_pair(orders[i].vRex[0].second.nLimit, orders[i]));
2650 assert(orders[i].numSells);
2651 limitSells[orders[i].vRex[0].second.currencyID].insert(std::make_pair(orders[i].vRex[0].second.nLimit, orders[i]));
2655 else if (orders[i].IsValid())
2657 expiredFillOrKills.push_back(&(orders[i]));
2661 rejects.push_back(&(orders[i]));
2665 // now we have all market orders in marketOrders multimap, sorted by feePerK, rejects that are expired or invalid in rejects
2666 // first, prune as many market orders as possible up to the maximum storage space available for market orders, which we calculate
2667 // as a functoin of the total space available and precentage of total orders that are market orders
2668 int64_t numLimitOrders = limitBuys.size() + limitSells.size();
2669 int64_t numMarketOrders = marketOrders.size();
2671 // if nothing to do, we are done, don't update anything
2672 if (!(reserveFills.size() + numLimitOrders + numMarketOrders))
2677 // 1. start from the current state and calculate what the price would be with market orders that fit, leaving room for limit orders
2678 // 2. add limit orders, first buys, as many as we can from the highest value and number downward, one at a time, until we run out or
2679 // cannot add any more due to not meeting the price. then we do the same for sells if there are any available, and alternate until
2680 // we either run out or cannot add from either side. within a specific limit, orders are sorted by largest first, which means
2681 // there is no point in retracing if an element fails to be added
2682 // 3. calculate a final order price
2683 // 4. create and return a new, updated CCoinbaseCurrencyState
2684 CCoinbaseCurrencyState newState(*(CCurrencyState *)this);
2686 conversionSizeOverhead = GetSerializeSize(mConversionTx, SER_NETWORK, PROTOCOL_VERSION);
2688 int64_t curSpace = maxSerializeSize - (totalSerializedSize + conversionSizeOverhead);
2692 printf("%s: no space available in block to include reserve orders\n", __func__);
2693 LogPrintf("%s: no space available in block to include reserve orders\n", __func__);
2697 // the ones that are passed in reserveFills may be any valid reserve related transaction
2698 // we need to first process those with the assumption that they are included
2699 for (auto it = reserveFills.begin(); it != reserveFills.end(); )
2703 // add up the starting point for conversions
2704 if (txDesc.IsReserveExchange())
2706 CMutableTransaction mtx;
2707 mtx = mConversionTx;
2708 txDesc.AddConversionInOuts(mtx, tmpConversionInputs, exchangeRateMap);
2709 conversionSizeOverhead = GetSerializeSize(mtx, SER_NETWORK, PROTOCOL_VERSION);
2710 int64_t tmpSpace = maxSerializeSize - (totalSerializedSize + conversionSizeOverhead);
2714 // can't fit, so it's a noFill
2715 noFills.push_back(txDesc);
2716 it = reserveFills.erase(it);
2720 curSpace = tmpSpace;
2722 totalNativeConversionFees += txDesc.nativeConversionFees;
2723 totalReserveConversionFees += txDesc.ReserveConversionFeesMap();
2727 newState.reserveIn = AddVectors(txDesc.ReserveOutConvertedVec(*this), newState.reserveIn);
2728 newState.nativeIn = AddVectors(txDesc.NativeOutConvertedVec(*this), newState.nativeIn);
2729 newState.nativeIn[0] += txDesc.NativeFees() + txDesc.nativeConversionFees;
2733 newState.reserveIn = AddVectors(
2734 AddVectors(txDesc.ReserveOutConvertedVec(*this), newState.reserveIn),
2735 AddVectors(txDesc.ReserveFees().AsCurrencyVector(currencies), txDesc.ReserveConversionFeesVec(*this))
2737 newState.nativeIn = AddVectors(txDesc.NativeOutConvertedVec(*this), newState.nativeIn);
2740 mConversionTx = mtx;
2746 if (feesAsReserve && newState.nativeIn.size())
2748 newState.nativeIn[0] += txDesc.NativeFees();
2752 newState.reserveIn = AddVectors(newState.reserveIn, txDesc.ReserveFees().AsCurrencyVector(currencies));
2758 int64_t marketOrdersSizeLimit = curSpace;
2759 int64_t limitOrdersSizeLimit = curSpace;
2760 if (numLimitOrders + numMarketOrders)
2762 marketOrdersSizeLimit = ((arith_uint256(numMarketOrders) * arith_uint256(curSpace)) / arith_uint256(numLimitOrders + numMarketOrders)).GetLow64();
2763 limitOrdersSizeLimit = curSpace - marketOrdersSizeLimit;
2766 if (limitOrdersSizeLimit < 1024 && maxSerializeSize > 2048)
2768 marketOrdersSizeLimit = maxSerializeSize - 1024;
2769 limitOrdersSizeLimit = 1024;
2772 for (auto marketOrder : marketOrders)
2774 // add as many as we can fit, if we are able to, consider the output transaction overhead as well
2775 int64_t thisSerializeSize = GetSerializeSize(*marketOrder.second.ptx, SER_NETWORK, PROTOCOL_VERSION);
2776 CMutableTransaction mtx;
2777 mtx = mConversionTx;
2778 marketOrder.second.AddConversionInOuts(mtx, tmpConversionInputs, exchangeRateMap);
2779 conversionSizeOverhead = GetSerializeSize(mtx, SER_NETWORK, PROTOCOL_VERSION);
2780 if ((totalSerializedSize + thisSerializeSize + conversionSizeOverhead) <= marketOrdersSizeLimit)
2782 totalNativeConversionFees += marketOrder.second.nativeConversionFees;
2783 totalReserveConversionFees += marketOrder.second.ReserveConversionFeesMap();
2784 mConversionTx = mtx;
2788 newState.reserveIn = AddVectors(marketOrder.second.ReserveOutConvertedVec(*this), newState.reserveIn);
2789 newState.nativeIn = AddVectors(marketOrder.second.NativeOutConvertedVec(*this), newState.nativeIn);
2790 newState.nativeIn[0] += marketOrder.second.NativeFees() + marketOrder.second.nativeConversionFees;
2794 newState.reserveIn = AddVectors(
2795 AddVectors(marketOrder.second.ReserveOutConvertedVec(*this), newState.reserveIn),
2796 AddVectors(marketOrder.second.ReserveFees().AsCurrencyVector(currencies), marketOrder.second.ReserveConversionFeesVec(*this))
2798 newState.nativeIn = AddVectors(marketOrder.second.NativeOutConvertedVec(*this), newState.nativeIn);
2801 reserveFills.push_back(marketOrder.second);
2802 totalSerializedSize += thisSerializeSize;
2806 // can't fit, no fill
2807 noFills.push_back(marketOrder.second);
2811 // iteratively add limit orders first buy, then sell, until we no longer have anything to add
2812 // this must iterate because each time we add a buy, it may put another sell's limit within reach and
2814 std::map<uint160, std::pair<std::multimap<CAmount, CReserveTransactionDescriptor>::iterator, CAmount>> buyPartial; // valid in loop for partial fill
2815 std::map<uint160, std::pair<std::multimap<CAmount, CReserveTransactionDescriptor>::iterator, CAmount>> sellPartial;
2817 limitOrdersSizeLimit = maxSerializeSize - totalSerializedSize;
2818 int64_t buyLimitSizeLimit = numLimitOrders ? totalSerializedSize + ((arith_uint256(limitBuys.size()) * arith_uint256(limitOrdersSizeLimit)) / arith_uint256(numLimitOrders)).GetLow64() : limitOrdersSizeLimit;
2820 CCurrencyState latestState = newState;
2822 // TODO - finish limit orders, which are significantly more complex after moving to
2823 // multi-reserves. until then, skip limit orders.
2825 for (bool tryagain = true; tryagain; )
2829 exchangeRates = ConvertAmounts(newState.reserveIn, newState.nativeIn, latestState);
2831 // starting with that exchange rate, add buys one at a time until we run out of buys to add or reach the limit,
2832 // possibly in a partial fill, then see if we can add any sells. we go back and forth until we have stopped being able to add
2833 // new orders. it would be possible to recognize a state where we are able to simultaneously fill a buy and a sell that are
2834 // the the same or very minimally overlapping limits
2836 for (auto limitBuysIt = limitBuys.rbegin(); limitBuysIt != limitBuys.rend() && exchangeRate > limitBuysIt->second.vRex.front().second.nLimit; limitBuysIt = limitBuys.rend())
2838 // any time there are entries above the lower bound, we only actually look at the end, since we mutate the
2839 // search space each iteration and remove anything we've already added, making the end always the most in-the-money
2840 CReserveTransactionDescriptor ¤tBuy = limitBuysIt->second;
2842 // it must first fit, space-wise
2843 int64_t thisSerializeSize = GetSerializeSize(*(currentBuy.ptx), SER_NETWORK, PROTOCOL_VERSION);
2845 CMutableTransaction mtx;
2848 mtx = *pConversionTx;
2849 currentBuy.AddConversionInOuts(mtx, tmpConversionInputs);
2850 conversionSizeOverhead = GetSerializeSize(mtx, SER_NETWORK, PROTOCOL_VERSION);
2852 if ((totalSerializedSize + thisSerializeSize + conversionSizeOverhead) <= buyLimitSizeLimit)
2854 CAmount newReserveIn, newNativeIn;
2857 newReserveIn = currentBuy.reserveOutConverted;
2858 newNativeIn = currentBuy.nativeOutConverted + currentBuy.NativeFees() + currentBuy.nativeConversionFees;
2862 newReserveIn = currentBuy.reserveOutConverted + currentBuy.ReserveFees() + currentBuy.reserveConversionFees;
2863 newNativeIn = currentBuy.nativeOutConverted;
2866 // calculate fresh with all conversions together to see if we still meet the limit
2867 CAmount newExchange = ConvertAmounts(newState.ReserveIn + newReserveIn, newState.NativeIn + newNativeIn, latestState);
2869 if (newExchange <= currentBuy.vRex[0].second.nLimit)
2871 totalNativeConversionFees += currentBuy.nativeConversionFees;
2872 totalReserveConversionFees += currentBuy.reserveConversionFees;
2874 // update conversion transaction if we have one
2877 *pConversionTx = mtx;
2880 // add to the current buys, we will never do something to disqualify this, since all orders left are either
2881 // the same limit or lower
2882 reserveFills.push_back(currentBuy);
2883 totalSerializedSize += thisSerializeSize;
2885 newState.ReserveIn += newReserveIn;
2886 newState.NativeIn += newNativeIn;
2888 exchangeRate = newExchange;
2889 limitBuys.erase(--limitBuys.end());
2893 // TODO:PBAAS support partial fills
2899 // now, iterate from lowest/most qualified sell limit first to highest/least qualified and attempt to put all in
2900 // if we can only fill an order partially, then do so
2901 for (auto limitSellsIt = limitSells.begin(); limitSellsIt != limitSells.end() && exchangeRate > limitSellsIt->second.vRex.front().second.nLimit; limitSellsIt = limitSells.begin())
2903 CReserveTransactionDescriptor ¤tSell = limitSellsIt->second;
2905 int64_t thisSerializeSize = GetSerializeSize(*currentSell.ptx, SER_NETWORK, PROTOCOL_VERSION);
2907 CMutableTransaction mtx;
2910 mtx = *pConversionTx;
2911 currentSell.AddConversionInOuts(mtx, tmpConversionInputs);
2912 conversionSizeOverhead = GetSerializeSize(mtx, SER_NETWORK, PROTOCOL_VERSION);
2915 if ((totalSerializedSize + thisSerializeSize + conversionSizeOverhead) <= maxSerializeSize)
2917 CAmount newReserveIn, newNativeIn;
2920 newReserveIn = currentSell.reserveOutConverted;
2921 newNativeIn = currentSell.nativeOutConverted + currentSell.NativeFees() + currentSell.nativeConversionFees;
2925 newReserveIn = currentSell.reserveOutConverted + currentSell.ReserveFees() + currentSell.reserveConversionFees;
2926 newNativeIn = currentSell.nativeOutConverted;
2929 // calculate fresh with all conversions together to see if we still meet the limit
2930 CAmount newExchange = ConvertAmounts(newState.ReserveIn + newReserveIn, newState.NativeIn + newNativeIn, latestState);
2931 if (newExchange >= currentSell.vRex.front().second.nLimit)
2933 totalNativeConversionFees += currentSell.nativeConversionFees;
2934 totalReserveConversionFees += currentSell.reserveConversionFees;
2936 // update conversion transaction if we have one
2939 *pConversionTx = mtx;
2942 // add to the current sells, we will never do something to disqualify this, since all orders left are either
2943 // the same limit or higher
2944 reserveFills.push_back(currentSell);
2945 totalSerializedSize += thisSerializeSize;
2946 exchangeRate = newExchange;
2948 newState.ReserveIn += newReserveIn;
2949 newState.NativeIn += newNativeIn;
2951 limitSells.erase(limitSellsIt);
2956 // we must be done with buys whether we created a partial or not
2961 buyLimitSizeLimit = maxSerializeSize - totalSerializedSize;
2965 // we can calculate total fees now, but to avoid a rounding error when converting native to reserve or vice versa on fees,
2966 // we must loop through once more to calculate all fees for the currency state
2967 auto curMap = GetReserveMap();
2968 CCurrencyValueMap totalReserveFees;
2969 CAmount totalNativeFees = 0;
2970 CCurrencyValueMap reserveOutVal;
2971 CMutableTransaction mtx = pConversionTx ? *pConversionTx : CMutableTransaction();
2972 for (auto fill : reserveFills)
2974 fill.AddConversionInOuts(mtx, conversionInputs, exchangeRateMap);
2975 reserveOutVal += NativeToReserveRaw(fill.NativeOutConvertedVec(latestState), exchangeRates);
2978 totalReserveFees += fill.AllFeesAsReserve(newState, exchangeRates);
2982 totalNativeFees += fill.AllFeesAsNative(newState, exchangeRates);
2987 *pConversionTx = mtx;
2991 totalReserveConversionFees.valueMap[currencies[0]] += NativeToReserve(totalNativeConversionFees, exchangeRates[0]);
2992 reserveOutVal += totalReserveConversionFees;
2993 totalReserveFees += totalReserveConversionFees;
2997 totalNativeConversionFees = totalNativeConversionFees + ReserveToNativeRaw(totalReserveConversionFees, exchangeRates);
2998 totalNativeFees += totalNativeConversionFees;
3001 newState = CCoinbaseCurrencyState(latestState,
3002 totalNativeFees, totalNativeConversionFees,
3005 newState.reserveOut,
3007 totalReserveFees.AsCurrencyVector(currencies),
3008 totalReserveConversionFees.AsCurrencyVector(currencies));
3010 prices = exchangeRates;
3012 for (auto &curBuys : limitBuys)
3014 for (auto &entry : curBuys.second)
3016 noFills.push_back(entry.second);
3020 for (auto &curSells : limitSells)
3022 for (auto &entry : curSells.second)
3024 noFills.push_back(entry.second);
3028 // if no matches, no state updates
3029 if (!reserveFills.size())
3035 if (pInOutTotalSerializeSize)
3037 *pInOutTotalSerializeSize = totalSerializedSize;
3039 printf("%s: %s\n", __func__, newState.ToUniValue().write(1, 2).c_str());
3044 CAmount CCurrencyState::CalculateConversionFee(CAmount inputAmount, bool convertToNative, int currencyIndex) const
3046 arith_uint256 bigAmount(inputAmount);
3047 arith_uint256 bigSatoshi(SATOSHIDEN);
3049 // we need to calculate a fee based either on the amount to convert or the last price
3050 // times the reserve
3051 if (convertToNative)
3054 cpp_dec_float_50 priceInReserve = PriceInReserveDecFloat50(currencyIndex);
3055 if (!to_int64(priceInReserve, price))
3059 bigAmount = price ? (bigAmount * bigSatoshi) / arith_uint256(price) : 0;
3063 fee = ((bigAmount * arith_uint256(CReserveExchange::SUCCESS_FEE)) / bigSatoshi).GetLow64();
3064 if (fee < CReserveExchange::MIN_SUCCESS_FEE)
3066 fee = CReserveExchange::MIN_SUCCESS_FEE;
3071 CAmount CReserveTransactionDescriptor::CalculateConversionFee(CAmount inputAmount)
3073 arith_uint256 bigAmount(inputAmount);
3074 arith_uint256 bigSatoshi(SATOSHIDEN);
3077 fee = ((bigAmount * arith_uint256(CReserveExchange::SUCCESS_FEE)) / bigSatoshi).GetLow64();
3078 if (fee < CReserveExchange::MIN_SUCCESS_FEE)
3080 fee = CReserveExchange::MIN_SUCCESS_FEE;
3085 // this calculates a fee that will be added to an amount and result in the same percentage as above,
3086 // such that a total of the inputAmount + this returned fee, if passed to CalculateConversionFee, would return
3088 CAmount CReserveTransactionDescriptor::CalculateAdditionalConversionFee(CAmount inputAmount)
3090 arith_uint256 bigAmount(inputAmount);
3091 arith_uint256 bigSatoshi(SATOSHIDEN);
3092 arith_uint256 conversionFee(CReserveExchange::SUCCESS_FEE);
3094 CAmount newAmount = ((bigAmount * bigSatoshi) / (bigSatoshi - conversionFee)).GetLow64();
3095 CAmount fee = CalculateConversionFee(newAmount);
3096 newAmount = inputAmount + fee;
3097 fee = CalculateConversionFee(newAmount); // again to account for minimum fee
3098 fee += inputAmount - (newAmount - fee); // add any additional difference