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"
20 // calculate fees required in one currency to pay in another
21 CAmount CReserveTransfer::CalculateTransferFee(const CTransferDestination &destination, uint32_t flags)
23 if ((flags & FEE_OUTPUT) || (!(flags & PRECONVERT) && (flags & CONVERT)))
27 return CReserveTransfer::DEFAULT_PER_STEP_FEE << 1 + ((CReserveTransfer::DEFAULT_PER_STEP_FEE << 1) * (destination.destination.size() / DESTINATION_BYTE_DIVISOR));
30 CAmount CReserveTransfer::CalculateTransferFee() const
32 // determine fee for this send
33 return CalculateTransferFee(destination, flags);
36 CCurrencyValueMap CReserveTransfer::TotalTransferFee() const
38 CCurrencyValueMap retVal;
39 CAmount transferFee = nFees;
40 if (destination.HasGatewayLeg() && destination.fees)
42 transferFee += destination.fees;
44 retVal.valueMap[feeCurrencyID] += transferFee;
48 CCurrencyValueMap CReserveTransfer::ConversionFee() const
50 CCurrencyValueMap retVal;
51 // add conversion fees in source currency for conversions or pre-conversions
52 if (IsConversion() || IsPreConversion())
54 for (auto &oneCur : reserveValues.valueMap)
56 retVal.valueMap[oneCur.first] += CReserveTransactionDescriptor::CalculateConversionFee(oneCur.second);
58 if (IsReserveToReserve())
66 CCurrencyValueMap CReserveTransfer::CalculateFee(uint32_t flags, CAmount transferTotal) const
68 CCurrencyValueMap feeMap;
70 feeMap.valueMap[feeCurrencyID] = CalculateTransferFee();
72 // add conversion fees in source currency for conversions or pre-conversions
73 if (IsConversion() || IsPreConversion())
75 for (auto &oneCur : reserveValues.valueMap)
77 feeMap.valueMap[oneCur.first] += CReserveTransactionDescriptor::CalculateConversionFee(oneCur.second);
79 if (IsReserveToReserve())
85 // consider extra-leg pricing here
90 CCrossChainImport::CCrossChainImport(const CScript &script)
93 if (IsPayToCryptoCondition(script, p) && p.IsValid())
95 // always take the first for now
96 if (p.evalCode == EVAL_CROSSCHAIN_IMPORT && p.vData.size())
98 FromVector(p.vData[0], *this);
103 CCrossChainImport::CCrossChainImport(const CTransaction &tx, int32_t *pOutNum)
105 for (int i = 0; i < tx.vout.size(); i++)
108 if (IsPayToCryptoCondition(tx.vout[i].scriptPubKey, p) && p.IsValid())
110 // always take the first for now
111 if (p.evalCode == EVAL_CROSSCHAIN_IMPORT && p.vData.size())
113 FromVector(p.vData[0], *this);
124 bool CCrossChainExport::GetExportInfo(const CTransaction &exportTx,
126 int &primaryExportOutNumOut,
128 CPBaaSNotarization &exportNotarization,
129 std::vector<CReserveTransfer> &reserveTransfers,
130 CValidationState &state) const
132 // we can assume that to get here, we have decoded the first output, which is the export output
133 // specified in numExportOut, our "this" pointer
135 // if this is called directly to get info, though it is a supplemental output, it is currently an error
136 if (IsSupplemental())
138 return state.Error(strprintf("%s: cannot get export data directly from a supplemental data output. must be in context",__func__));
141 auto hw = CMMRNode<>::GetHashWriter();
143 // this can be called passing either a system export or a normal currency export, and it will always
144 // retrieve information from the same normal currency export in either case and return the primary output num
145 int numOutput = IsSystemThreadExport() ? numExportOut - 1 : numExportOut;
148 return state.Error(strprintf("%s: invalid output index for export out or invalid export transaction",__func__));
150 primaryExportOutNumOut = numOutput;
152 // if this export is from our system
153 if (sourceSystemID == ASSETCHAINS_CHAINID)
155 // if we're exporting off-chain and not directly to the system currency,
156 // the system currency is added as a system export output, which ensures export serialization from this system
157 // to the other. the system export output will be after our currency export. if so skip it.
158 if (destSystemID != sourceSystemID && destCurrencyID != destSystemID)
163 // retrieve reserve transfers from export transaction inputs
166 for (int i = firstInput; i < (firstInput + numInputs); i++)
172 if (!(myGetTransaction(exportTx.vin[i].prevout.hash, rtTx, hashBlk) &&
173 exportTx.vin[i].prevout.n < rtTx.vout.size() &&
174 rtTx.vout[exportTx.vin[i].prevout.n].scriptPubKey.IsPayToCryptoCondition(rtP) &&
176 rtP.evalCode == EVAL_RESERVE_TRANSFER &&
178 (rt = CReserveTransfer(rtP.vData[0])).IsValid()))
180 return state.Error(strprintf("%s: invalid reserve transfer for export",__func__));
183 reserveTransfers.push_back(rt);
189 // this is coming from another chain or system.
190 // the proof of this export must already have been checked, so we are
191 // only interested in the reserve transfers for this and any supplements
192 CCrossChainExport rtExport = *this;
193 while (rtExport.IsValid())
196 for (auto &oneRt : rtExport.reserveTransfers)
199 reserveTransfers.push_back(oneRt);
201 if (rtExport.HasSupplement() ||
202 (!rtExport.IsSameChain() && rtExport.numInputs > 0))
205 if (!(exportTx.vout.size() > numOutput &&
206 exportTx.vout[numOutput].scriptPubKey.IsPayToCryptoCondition(p) &&
208 p.evalCode == EVAL_CROSSCHAIN_EXPORT &&
210 (rtExport = CCrossChainExport(p.vData[0])).IsValid() &&
211 rtExport.IsSupplemental()))
214 rtExport = CCrossChainExport();
219 // no more supplements, done
220 rtExport = CCrossChainExport();
225 // now, we should have accurate reserve transfers
226 uint256 rtHash = reserveTransfers.size() ? hw.GetHash() : uint256();
227 if (rtHash != hashReserveTransfers)
229 return state.Error(strprintf("%s: reserve transfers do not match reserve transfer hash in export",__func__));
232 exportNotarization = CPBaaSNotarization();
234 if (IsSameChain() && !IsChainDefinition())
236 if (IsClearLaunch() || !IsPrelaunch())
240 // we have an export finalization to verify/skip
241 if (!(exportTx.vout.size() > numOutput &&
242 exportTx.vout[numOutput].scriptPubKey.IsPayToCryptoCondition(p) &&
244 p.evalCode == EVAL_FINALIZE_EXPORT &&
246 (CObjectFinalization(p.vData[0])).IsValid()))
248 return state.Error(strprintf("%s: invalid export finalization",__func__));
251 if ((IsPrelaunch() || IsClearLaunch()))
253 // in same chain before launch, we expect a notarization to follow
256 if (!(exportTx.vout.size() > numOutput &&
257 exportTx.vout[numOutput].scriptPubKey.IsPayToCryptoCondition(p) &&
259 (p.evalCode == EVAL_ACCEPTEDNOTARIZATION || p.evalCode == EVAL_EARNEDNOTARIZATION) &&
261 (exportNotarization = CPBaaSNotarization(p.vData[0])).IsValid()))
263 return state.Error(strprintf("%s: invalid export notarization",__func__));
267 nextOutput = numOutput + 1;
271 bool CCrossChainExport::GetExportInfo(const CTransaction &exportTx,
273 int &primaryExportOutNumOut,
275 CPBaaSNotarization &exportNotarization,
276 std::vector<CReserveTransfer> &reserveTransfers) const
278 CValidationState state;
279 return GetExportInfo(exportTx, numExportOut, primaryExportOutNumOut, nextOutput, exportNotarization, reserveTransfers, state);
283 bool CCrossChainImport::GetImportInfo(const CTransaction &importTx,
286 CCrossChainExport &ccx,
287 CCrossChainImport &sysCCI,
289 CPBaaSNotarization &importNotarization,
290 int32_t &importNotarizationOut,
291 int32_t &evidenceOutStart,
292 int32_t &evidenceOutEnd,
293 std::vector<CReserveTransfer> &reserveTransfers,
294 CValidationState &state) const
296 // we can assume that to get here, we have decoded the first output, which is the import output
297 // specified in numImportOut, our "this" pointer
299 // following that, we should find in order:
301 // 1. Optional system import output, present only if we are importing to non-gateway, non-native currency from an external system or PBaaS chain
303 // 2. any necessary export proof for the import, present only if we are coming from an external system or PBaaS chain
305 // 3. if we are coming from an external system or PBaaS chain, following outputs will include the reserve transfers for the export proof
307 // 4. Notarization for import currency, only present if this is fractional currency or first launch of new PBaaS chain
311 evidenceOutStart = -1;
314 CCrossChainImport sysCCITemp;
316 // we cannot assert that cs_main is held or take cs_main here due to the multi-threaded validation model,
317 // but we must either be holding the lock to enter here or in service of a smart transaction at this point.
320 uint32_t solutionVersion = CConstVerusSolutionVector::GetVersionByHeight(nHeight);
322 CCrossChainImport altImport;
323 const CCrossChainImport *pBaseImport = this;
325 // if this is a source system import, it comes after the actual import
326 // that we can parse on a transaction
327 if (pBaseImport->IsSourceSystemImport())
329 if (!(numImportOut-- > 0 &&
330 (altImport = CCrossChainImport(importTx.vout[numImportOut].scriptPubKey)).IsValid() &&
331 !(pBaseImport = &altImport)->IsSourceSystemImport()))
333 return state.Error(strprintf("%s: invalid import",__func__));
337 bool isPBaaSDefinitionOrLaunch = (!IsVerusActive() && pBaseImport->IsInitialLaunchImport()) ||
338 (pBaseImport->IsDefinitionImport() &&
339 pBaseImport->sourceSystemID != ASSETCHAINS_CHAINID);
341 importNotarizationOut = numImportOut + 1;
343 if (pBaseImport->IsSameChain())
345 // reserve transfers are available via the inputs to the matching export
346 CTransaction exportTx = pBaseImport->exportTxId.IsNull() ? importTx : CTransaction();
350 if (!((pBaseImport->exportTxId.IsNull() ? true : myGetTransaction(pBaseImport->exportTxId, exportTx, hashBlk)) &&
351 pBaseImport->IsDefinitionImport() ||
352 (pBaseImport->exportTxOutNum >= 0 &&
353 exportTx.vout.size() > pBaseImport->exportTxOutNum &&
354 exportTx.vout[pBaseImport->exportTxOutNum].scriptPubKey.IsPayToCryptoCondition(p) &&
356 p.evalCode == EVAL_CROSSCHAIN_EXPORT &&
358 (ccx = CCrossChainExport(p.vData[0])).IsValid())))
360 return state.Error(strprintf("%s: cannot retrieve export transaction for import",__func__));
363 if (!pBaseImport->IsDefinitionImport())
366 CPBaaSNotarization xNotarization;
367 int primaryOutNumOut;
368 if (!ccx.GetExportInfo(exportTx, pBaseImport->exportTxOutNum, primaryOutNumOut, nextOutput, xNotarization, reserveTransfers, state))
373 // next output after import out is notarization
379 // PBaaS launch imports do not spend a separate sys import thread, since we are also importing
380 // system currency on the same tx and and the coinbase has no inputs anyhow
381 if (!isPBaaSDefinitionOrLaunch)
383 // next output should be the import for the system from which this export comes
385 sysCCIOut = numImportOut + 1;
386 if (!(sysCCIOut >= 0 &&
387 importTx.vout.size() > sysCCIOut &&
388 importTx.vout[sysCCIOut].scriptPubKey.IsPayToCryptoCondition(p) &&
390 p.evalCode == EVAL_CROSSCHAIN_IMPORT &&
392 (sysCCITemp = CCrossChainImport(p.vData[0])).IsValid()))
394 return state.Error(strprintf("%s: cannot retrieve export evidence for import",__func__));
397 importNotarizationOut++;
400 if (!isPBaaSDefinitionOrLaunch ||
401 pBaseImport->importCurrencyID == ASSETCHAINS_CHAINID ||
402 (!pBaseImport->importCurrencyID.IsNull() && pBaseImport->importCurrencyID == ConnectedChains.ThisChain().GatewayConverterID()))
404 // next output should be export in evidence output followed by supplemental reserve transfers for the export
405 evidenceOutStart = importNotarizationOut + 1;
406 CNotaryEvidence evidence;
408 if (!(evidenceOutStart >= 0 &&
409 importTx.vout.size() > evidenceOutStart &&
410 importTx.vout[evidenceOutStart].scriptPubKey.IsPayToCryptoCondition(p) &&
412 p.evalCode == EVAL_NOTARY_EVIDENCE &&
414 (evidence = CNotaryEvidence(p.vData[0])).IsValid() &&
415 evidence.IsPartialTxProof() &&
416 evidence.evidence.size()))
418 return state.Error(strprintf("%s: cannot retrieve export evidence for import", __func__));
421 CTransaction exportTx;
423 if (!(!evidence.evidence[0].GetPartialTransaction(exportTx).IsNull() &&
424 evidence.evidence[0].TransactionHash() == exportTxId &&
425 exportTx.vout.size() > exportTxOutNum &&
426 exportTx.vout[exportTxOutNum].scriptPubKey.IsPayToCryptoCondition(p) &&
429 (ccx = CCrossChainExport(p.vData[0])).IsValid()))
431 return state.Error(strprintf("%s: invalid export evidence for import", __func__));
434 CPBaaSNotarization xNotarization;
435 int primaryOutNumOut;
436 if (!ccx.GetExportInfo(importTx, evidenceOutStart, primaryOutNumOut, nextOutput, xNotarization, reserveTransfers))
438 //UniValue jsonTx(UniValue::VOBJ);
439 //TxToUniv(importTx, uint256(), jsonTx);
440 //printf("%s: importTx:\n%s\n", __func__, jsonTx.write(1,2).c_str());
441 return state.Error(strprintf("%s: invalid export evidence for import 1",__func__));
444 // evidence out end points to the last evidence out, not beyond
445 evidenceOutEnd = nextOutput - 1;
449 if (!(importTx.vout.size() > importNotarizationOut &&
450 importTx.vout[importNotarizationOut].scriptPubKey.IsPayToCryptoCondition(p) &&
452 (p.evalCode == EVAL_ACCEPTEDNOTARIZATION || p.evalCode == EVAL_EARNEDNOTARIZATION) &&
454 (importNotarization = CPBaaSNotarization(p.vData[0])).IsValid()))
456 return state.Error(strprintf("%s: invalid import notarization for import",__func__));
458 if (sysCCITemp.IsValid())
465 bool CCrossChainImport::GetImportInfo(const CTransaction &importTx,
468 CCrossChainExport &ccx,
469 CCrossChainImport &sysCCI,
471 CPBaaSNotarization &importNotarization,
472 int32_t &importNotarizationOut,
473 int32_t &evidenceOutStart,
474 int32_t &evidenceOutEnd,
475 std::vector<CReserveTransfer> &reserveTransfers) const
477 CValidationState state;
478 return GetImportInfo(importTx,
485 importNotarizationOut,
492 bool CCrossChainImport::ValidateImport(const CTransaction &tx,
495 CCrossChainExport &ccx,
496 CPBaaSNotarization &importNotarization,
497 std::vector<CReserveTransfer> &reserveTransfers,
498 CValidationState &state) const
503 bool CCrossChainImport::ValidateImport(const CTransaction &tx,
506 CCrossChainExport &ccx,
507 CPBaaSNotarization &importNotarization,
508 std::vector<CReserveTransfer> &reserveTransfers) const
510 CValidationState state;
511 return ValidateImport(tx, numImportin, numImportOut, ccx, importNotarization, reserveTransfers, state);
514 CCurrencyState::CCurrencyState(const UniValue &obj)
518 flags = uni_get_int(find_value(obj, "flags"));
519 version = uni_get_int(find_value(obj, "version"), VERSION_CURRENT);
521 std::string cIDStr = uni_get_str(find_value(obj, "currencyid"));
524 CTxDestination currencyDest = DecodeDestination(cIDStr);
525 currencyID = GetDestinationID(currencyDest);
528 auto CurrenciesArr = IsFractional() ? find_value(obj, "reservecurrencies") : find_value(obj, "launchcurrencies");
529 size_t numCurrencies = 0;
531 if (IsFractional() &&
532 (!CurrenciesArr.isArray() ||
533 !(numCurrencies = CurrenciesArr.size())))
535 version = VERSION_INVALID;
536 LogPrintf("%s: Failed to proplerly specify launch or reserve currencies in currency definition\n", __func__);
538 if (numCurrencies > MAX_RESERVE_CURRENCIES)
540 version = VERSION_INVALID;
541 LogPrintf("%s: More than %d launch or reserve currencies in currency definition\n", __func__, MAX_RESERVE_CURRENCIES);
544 // store currencies, weights, and reserves
545 if (CurrenciesArr.size())
549 for (int i = 0; i < CurrenciesArr.size(); i++)
551 uint160 currencyID = GetDestinationID(DecodeDestination(uni_get_str(find_value(CurrenciesArr[i], "currencyid"))));
552 if (currencyID.IsNull())
554 LogPrintf("Invalid currency ID\n");
555 version = VERSION_INVALID;
558 currencies.push_back(currencyID);
559 weights.push_back(AmountFromValueNoErr(find_value(CurrenciesArr[i], "weight")));
560 reserves.push_back(AmountFromValueNoErr(find_value(CurrenciesArr[i], "reserves")));
565 version = VERSION_INVALID;
566 LogPrintf("Invalid specification of currencies, weights, and/or reserves in initial definition of reserve currency\n");
570 if (version == VERSION_INVALID)
572 printf("Invalid currency specification, see debug.log for reason other than invalid flags\n");
573 LogPrintf("Invalid currency specification\n");
577 initialSupply = AmountFromValue(find_value(obj, "initialsupply"));
578 emitted = AmountFromValue(find_value(obj, "emitted"));
579 supply = AmountFromValue(find_value(obj, "supply"));
584 printf("Invalid currency specification, see debug.log for reason other than invalid flags\n");
585 LogPrintf("Invalid currency specification\n");
586 version = VERSION_INVALID;
590 CCoinbaseCurrencyState::CCoinbaseCurrencyState(const CTransaction &tx, int *pOutIdx)
593 int &i = pOutIdx ? *pOutIdx : localIdx;
594 for (i = 0; i < tx.vout.size(); i++)
597 if (IsPayToCryptoCondition(tx.vout[i].scriptPubKey, p))
599 if (p.evalCode == EVAL_CURRENCYSTATE && p.vData.size())
601 FromVector(p.vData[0], *this);
608 std::vector<std::vector<CAmount>> ValueColumnsFromUniValue(const UniValue &uni,
609 const std::vector<std::string> &rowNames,
610 const std::vector<std::string> &columnNames)
612 std::vector<std::vector<CAmount>> retVal;
613 for (int i = 0; i < rowNames.size(); i++)
615 UniValue row = find_value(uni, rowNames[i]);
618 for (int j = 0; j < columnNames.size(); j++)
620 if (retVal.size() == j)
622 retVal.emplace_back();
624 CAmount columnVal = 0;
625 columnVal = AmountFromValueNoErr(find_value(row, columnNames[j]));
626 retVal[j].push_back(columnVal);
634 CCoinbaseCurrencyState::CCoinbaseCurrencyState(const UniValue &obj) : CCurrencyState(obj)
638 std::vector<std::vector<CAmount>> columnAmounts;
640 std::vector<std::string> rowNames;
641 auto currenciesValue = find_value(obj, "currencies");
642 if (currenciesValue.isObject())
644 rowNames = currenciesValue.getKeys();
646 if (!currencies.size() && rowNames.size())
648 currencies.resize(rowNames.size());
649 weights.resize(rowNames.size());
650 reserves.resize(rowNames.size());
651 for (int i = 0; i < rowNames.size(); i++)
653 currencies[i] = GetDestinationID(DecodeDestination(rowNames[i]));
656 else if (currencies.size())
658 rowNames.resize(currencies.size());
659 for (int i = 0; i < rowNames.size(); i++)
661 rowNames[i] = EncodeDestination(CIdentityID(currencies[i]));
664 if (currencies.size() != rowNames.size())
666 LogPrintf("%s: mismatch currencies and reserve currencies\n", __func__);
667 version = VERSION_INVALID;
670 std::vector<std::string> columnNames({"reservein", "primarycurrencyin", "reserveout", "lastconversionprice", "viaconversionprice", "fees", "conversionfees", "priorweights"});
671 if (currenciesValue.isObject())
673 //printf("%s: currencies: %s\n", __func__, currenciesValue.write(1,2).c_str());
674 columnAmounts = ValueColumnsFromUniValue(currenciesValue, rowNames, columnNames);
675 if (columnAmounts.size() == columnNames.size())
677 reserveIn = columnAmounts[0];
678 primaryCurrencyIn = columnAmounts[1];
679 reserveOut = columnAmounts[2];
680 conversionPrice = columnAmounts[3];
681 viaConversionPrice = columnAmounts[4];
682 fees = columnAmounts[5];
683 conversionFees = columnAmounts[6];
684 priorWeights.resize(0);
685 for (auto oneColumnNum : columnAmounts[7])
687 priorWeights.push_back(oneColumnNum);
691 primaryCurrencyFees = AmountFromValueNoErr(find_value(obj, "primarycurrencyfees"));
692 primaryCurrencyConversionFees = AmountFromValueNoErr(find_value(obj, "primarycurrencyconversionfees"));
693 primaryCurrencyOut = AmountFromValueNoErr(find_value(obj, "primarycurrencyout"));
694 preConvertedOut = AmountFromValueNoErr(find_value(obj, "preconvertedout"));
698 version = VERSION_INVALID;
699 LogPrintf("%s: exception reading json CCoinbaseCurrencyState\n", __func__);
703 CAmount CalculateFractionalOut(CAmount NormalizedReserveIn, CAmount Supply, CAmount NormalizedReserve, int32_t reserveRatio)
705 static cpp_dec_float_50 one("1");
706 static cpp_dec_float_50 bigSatoshi("100000000");
707 cpp_dec_float_50 reservein(std::to_string(NormalizedReserveIn));
708 reservein = reservein / bigSatoshi;
709 cpp_dec_float_50 supply(std::to_string((Supply ? Supply : 1)));
710 supply = supply / bigSatoshi;
711 cpp_dec_float_50 reserve(std::to_string(NormalizedReserve ? NormalizedReserve : 1));
712 reserve = reserve / bigSatoshi;
713 cpp_dec_float_50 ratio(std::to_string(reserveRatio));
714 ratio = ratio / bigSatoshi;
716 //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());
718 int64_t fractionalOut = 0;
720 // first check if anything to buy
721 if (NormalizedReserveIn)
723 cpp_dec_float_50 supplyout = bigSatoshi * (supply * (pow((reservein / reserve) + one, ratio) - one));
724 //printf("supplyout: %s\n", supplyout.str(0, std::ios_base::fmtflags::_S_fixed).c_str());
726 if (!CCurrencyState::to_int64(supplyout, fractionalOut))
731 return fractionalOut;
734 CAmount CalculateReserveOut(CAmount FractionalIn, CAmount Supply, CAmount NormalizedReserve, int32_t reserveRatio)
736 static cpp_dec_float_50 one("1");
737 static cpp_dec_float_50 bigSatoshi("100000000");
738 cpp_dec_float_50 fractionalin(std::to_string(FractionalIn));
739 fractionalin = fractionalin / bigSatoshi;
740 cpp_dec_float_50 supply(std::to_string((Supply ? Supply : 1)));
741 supply = supply / bigSatoshi;
742 cpp_dec_float_50 reserve(std::to_string(NormalizedReserve ? NormalizedReserve : 1));
743 reserve = reserve / bigSatoshi;
744 cpp_dec_float_50 ratio(std::to_string(reserveRatio));
745 ratio = ratio / bigSatoshi;
747 //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());
749 int64_t reserveOut = 0;
751 // first check if anything to buy
754 cpp_dec_float_50 reserveout = bigSatoshi * (reserve * (one - pow(one - (fractionalin / supply), (one / ratio))));
755 //printf("reserveout: %s\n", reserveout.str(0, std::ios_base::fmtflags::_S_fixed).c_str());
757 if (!CCurrencyState::to_int64(reserveout, reserveOut))
765 // This can handle multiple aggregated, bidirectional conversions in one block of transactions. To determine the conversion price, it
766 // takes both input amounts of any number of reserves and the fractional currencies targeting those reserves to merge the conversion into one
767 // merged calculation with the same price across currencies for all transactions in the block. It returns the newly calculated
768 // conversion prices of the fractional reserve in the reserve currency.
769 std::vector<CAmount> CCurrencyState::ConvertAmounts(const std::vector<CAmount> &_inputReserves,
770 const std::vector<CAmount> &_inputFractional,
771 CCurrencyState &_newState,
772 std::vector<std::vector<CAmount>> const *pCrossConversions,
773 std::vector<CAmount> *pViaPrices) const
775 static arith_uint256 bigSatoshi(SATOSHIDEN);
777 int32_t numCurrencies = currencies.size();
778 std::vector<CAmount> inputReserves = _inputReserves;
779 std::vector<CAmount> inputFractional = _inputFractional;
781 CCurrencyState newState = *this;
782 std::vector<CAmount> rates(numCurrencies);
783 std::vector<CAmount> initialRates = PricesInReserve();
785 bool haveConversion = false;
787 if (inputReserves.size() == inputFractional.size() && inputReserves.size() == numCurrencies &&
788 (!pCrossConversions || pCrossConversions->size() == numCurrencies))
791 for (i = 0; i < numCurrencies; i++)
793 if (!pCrossConversions || (*pCrossConversions)[i].size() != numCurrencies)
798 if (!pCrossConversions || i == numCurrencies)
800 for (auto oneIn : inputReserves)
804 haveConversion = true;
810 for (auto oneIn : inputFractional)
814 haveConversion = true;
823 printf("%s: invalid parameters\n", __func__);
824 LogPrintf("%s: invalid parameters\n", __func__);
830 // not considered an error
831 _newState = newState;
835 // generally an overflow will cause a fail, which will result in leaving the _newState parameter untouched, making it
836 // possible to check if it is invalid as an overflow or formula failure check
839 for (auto oneIn : inputReserves)
844 printf("%s: invalid reserve input amount for conversion %ld\n", __func__, oneIn);
845 LogPrintf("%s: invalid reserve input amount for conversion %ld\n", __func__, oneIn);
849 for (auto oneIn : inputFractional)
854 printf("%s: invalid fractional input amount for conversion %ld\n", __func__, oneIn);
855 LogPrintf("%s: invalid fractional input amount for conversion %ld\n", __func__, oneIn);
865 // Create corresponding fractions of the supply for each currency to be used as starting calculation of that currency's value
866 // Determine the equivalent amount of input and output based on current values. Balance each such that each currency has only
867 // input or output, denominated in supply at the starting value.
869 // For each currency in either direction, sell to reserve or buy aggregate, we convert to a contribution of amount at the reserve
870 // percent value. For example, consider 4 currencies, r1...r4, which are all 25% reserves of currency fr1. For simplicity of example,
871 // assume 1000 reserve of each reserve currency, where all currencies are equal in value to each other at the outset, and a supply of
872 // 4000, where each fr1 is equal in value to 1 of each component reserve.
873 // Now, consider the following cases:
875 // 1. purchase fr1 with 100 r1
876 // This is treated as a single 25% fractional purchase with respect to amount purchased, ending price, and supply change
877 // 2. purchase fr1 with 100 r1, 100 r2, 100 r3, 100 r4
878 // This is treated as a common layer of purchase across 4 x 25% currencies, resulting in 100% fractional purchase divided 4 ways
879 // 3. purchase fr1 with 100 r1, 50 r2, 25 r3
880 // This is treated as 3 separate purchases in order:
881 // a. one of 25 units across 3 currencies (3 x 25%), making a 75% fractional purchase of 75 units divided equally across 3 currencies
882 // 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
883 // c. one purchase of 50 units in r1 at 25% fractional purchase
884 // 4. purchase fr1 with 100 r1, sell 100 fr1 to r2
885 // a. one fractional purchase of 100 units at 25%
886 // b. one fractional sell of 100 units at 25%
887 // c. do each in forward and reverse order and set conversion at mean between each
888 // 5. purchase fr1 with 100 r1, 50 r2, sell 100 fr1 to r3, 50 to r4
889 // This consists of one composite (multi-layer) buy and one composite sell
890 // a. Compose one two layer purchase of 50 r1 + 50 r2 at 50% and 50 r1 at 25%
891 // b. Compose one two layer sell of 50 r3 + 50 r4 at 50% and 50 r3 at 25%
892 // c. execute each operation of a and b in forward and reverse order and set conversion at mean between results
895 std::multimap<CAmount, std::pair<CAmount, uint160>> fractionalIn, fractionalOut;
897 // aggregate amounts of ins and outs across all currencies expressed in fractional values in both directions first buy/sell, then sell/buy
898 std::map<uint160, std::pair<CAmount, CAmount>> fractionalInMap, fractionalOutMap;
900 arith_uint256 bigSupply(supply);
902 int32_t totalReserveWeight = 0;
903 int32_t maxReserveRatio = 0;
905 for (auto weight : weights)
907 maxReserveRatio = weight > maxReserveRatio ? weight : maxReserveRatio;
908 totalReserveWeight += weight;
911 LogPrintf("%s: invalid, zero weight currency for conversion\n", __func__);
916 if (!maxReserveRatio)
918 LogPrintf("%s: attempting to convert amounts on non-fractional currency\n", __func__);
922 // it is currently an error to have > 100% reserve ratio currency
923 if (totalReserveWeight > bigSatoshi)
925 LogPrintf("%s: total currency backing weight exceeds 100%\n", __func__);
929 arith_uint256 bigMaxReserveRatio = arith_uint256(maxReserveRatio);
930 arith_uint256 bigTotalReserveWeight = arith_uint256(totalReserveWeight);
932 // reduce each currency change to a net inflow or outflow of fractional currency and
933 // store both negative and positive in structures sorted by the net amount, adjusted
934 // by the difference of the ratio between the weights of each currency
935 for (int64_t i = 0; i < numCurrencies; i++)
937 arith_uint256 weight(weights[i]);
938 //printf("%s: %ld\n", __func__, ReserveToNative(inputReserves[i], i));
939 CAmount asNative = ReserveToNative(inputReserves[i], i);
946 CAmount netFractional = inputFractional[i] - asNative;
948 arith_uint256 bigDeltaRatio;
949 if (netFractional > 0)
951 bigDeltaRatio = ((arith_uint256(netFractional) * bigMaxReserveRatio) / weight);
952 if (bigDeltaRatio > INT64_MAX)
957 deltaRatio = bigDeltaRatio.GetLow64();
958 fractionalIn.insert(std::make_pair(deltaRatio, std::make_pair(netFractional, currencies[i])));
960 else if (netFractional < 0)
962 netFractional = -netFractional;
963 bigDeltaRatio = ((arith_uint256(netFractional) * bigMaxReserveRatio) / weight);
964 if (bigDeltaRatio > INT64_MAX)
969 deltaRatio = bigDeltaRatio.GetLow64();
970 fractionalOut.insert(std::make_pair(deltaRatio, std::make_pair(netFractional, currencies[i])));
976 LogPrintf("%s: OVERFLOW in calculating changes in currency\n", __func__);
980 // create "layers" of equivalent value at different fractional percentages
981 // across currencies going in or out at the same time, enabling their effect on the aggregate
982 // to be represented by a larger fractional percent impact of "normalized reserve" on the currency,
983 // which results in accurate pricing impact simulating a basket of currencies.
985 // since we have all values sorted, the lowest non-zero value determines the first common layer, then next lowest, the next, etc.
986 std::vector<std::pair<int32_t, std::pair<CAmount, std::vector<uint160>>>> fractionalLayersIn, fractionalLayersOut;
987 auto reserveMap = GetReserveMap();
989 CAmount layerAmount = 0;
992 for (auto inFIT = fractionalIn.upper_bound(layerAmount); inFIT != fractionalIn.end(); inFIT = fractionalIn.upper_bound(layerAmount))
994 // make a common layer out of all entries from here until the end
995 int frIdx = fractionalLayersIn.size();
996 layerStart = layerAmount;
997 layerAmount = inFIT->first;
998 CAmount layerHeight = layerAmount - layerStart;
999 fractionalLayersIn.emplace_back(std::make_pair(0, std::make_pair(0, std::vector<uint160>())));
1000 for (auto it = inFIT; it != fractionalIn.end(); it++)
1002 // reverse the calculation from layer height to amount for this currency, based on currency weight
1003 int32_t weight = weights[reserveMap[it->second.second]];
1004 CAmount curAmt = ((arith_uint256(layerHeight) * arith_uint256(weight) / bigMaxReserveRatio)).GetLow64();
1005 it->second.first -= curAmt;
1007 if (it->second.first < 0)
1009 LogPrintf("%s: UNDERFLOW in calculating changes in currency\n", __func__);
1010 return initialRates;
1013 fractionalLayersIn[frIdx].first += weight;
1014 fractionalLayersIn[frIdx].second.first += curAmt;
1015 fractionalLayersIn[frIdx].second.second.push_back(it->second.second);
1020 for (auto outFIT = fractionalOut.upper_bound(layerAmount); outFIT != fractionalOut.end(); outFIT = fractionalOut.upper_bound(layerAmount))
1022 int frIdx = fractionalLayersOut.size();
1023 layerStart = layerAmount;
1024 layerAmount = outFIT->first;
1025 CAmount layerHeight = layerAmount - layerStart;
1026 fractionalLayersOut.emplace_back(std::make_pair(0, std::make_pair(0, std::vector<uint160>())));
1027 for (auto it = outFIT; it != fractionalOut.end(); it++)
1029 int32_t weight = weights[reserveMap[it->second.second]];
1030 arith_uint256 bigCurAmt = ((arith_uint256(layerHeight) * arith_uint256(weight) / bigMaxReserveRatio));
1031 if (bigCurAmt > INT64_MAX)
1033 LogPrintf("%s: OVERFLOW in calculating changes in currency\n", __func__);
1034 return initialRates;
1036 CAmount curAmt = bigCurAmt.GetLow64();
1037 it->second.first -= curAmt;
1038 assert(it->second.first >= 0);
1040 fractionalLayersOut[frIdx].first += weight;
1041 fractionalLayersOut[frIdx].second.first += curAmt;
1042 fractionalLayersOut[frIdx].second.second.push_back(it->second.second);
1046 int64_t supplyAfterBuy = 0, supplyAfterBuySell = 0, supplyAfterSell = 0, supplyAfterSellBuy = 0;
1047 int64_t reserveAfterBuy = 0, reserveAfterBuySell = 0, reserveAfterSell = 0, reserveAfterSellBuy = 0;
1049 // first, loop through all buys layer by layer. calculate and divide the proceeds between currencies
1050 // in each participating layer, in accordance with each currency's relative percentage
1051 CAmount addSupply = 0;
1052 CAmount addNormalizedReserves = 0;
1053 for (auto &layer : fractionalLayersOut)
1055 // each layer has a fractional percentage/weight and a total amount, determined by the total of all weights for that layer
1056 // and net amounts across all currencies in that layer. each layer also includes a list of all currencies.
1058 // calculate a fractional buy at the total layer ratio for the amount specified
1059 // and divide the value according to the relative weight of each currency, adding to each entry of fractionalOutMap
1060 arith_uint256 bigLayerWeight = arith_uint256(layer.first);
1061 CAmount totalLayerReserves = ((bigSupply * bigLayerWeight) / bigSatoshi).GetLow64() + addNormalizedReserves;
1062 addNormalizedReserves += layer.second.first;
1063 CAmount newSupply = CalculateFractionalOut(layer.second.first, supply + addSupply, totalLayerReserves, layer.first);
1066 LogPrintf("%s: currency supply OVERFLOW\n", __func__);
1067 return initialRates;
1069 arith_uint256 bigNewSupply(newSupply);
1070 addSupply += newSupply;
1071 for (auto &id : layer.second.second)
1073 auto idIT = fractionalOutMap.find(id);
1074 CAmount newSupplyForCurrency = ((bigNewSupply * weights[reserveMap[id]]) / bigLayerWeight).GetLow64();
1076 // initialize or add to the new supply for this currency
1077 if (idIT == fractionalOutMap.end())
1079 fractionalOutMap[id] = std::make_pair(newSupplyForCurrency, int64_t(0));
1083 idIT->second.first += newSupplyForCurrency;
1088 supplyAfterBuy = supply + addSupply;
1089 assert(supplyAfterBuy >= 0);
1091 reserveAfterBuy = supply + addNormalizedReserves;
1092 assert(reserveAfterBuy >= 0);
1095 addNormalizedReserves = 0;
1096 CAmount addNormalizedReservesBB = 0, addNormalizedReservesAB = 0;
1098 // calculate sell both before and after buy through this loop
1099 for (auto &layer : fractionalLayersIn)
1101 // first calculate sell before-buy, then after-buy
1102 arith_uint256 bigLayerWeight(layer.first);
1104 // before-buy starting point
1105 CAmount totalLayerReservesBB = ((bigSupply * bigLayerWeight) / bigSatoshi).GetLow64() + addNormalizedReservesBB;
1106 CAmount totalLayerReservesAB = ((arith_uint256(supplyAfterBuy) * bigLayerWeight) / bigSatoshi).GetLow64() + addNormalizedReservesAB;
1108 CAmount newNormalizedReserveBB = CalculateReserveOut(layer.second.first, supply + addSupply, totalLayerReservesBB + addNormalizedReservesBB, layer.first);
1109 CAmount newNormalizedReserveAB = CalculateReserveOut(layer.second.first, supplyAfterBuy + addSupply, totalLayerReservesAB + addNormalizedReservesAB, layer.first);
1111 // input fractional is burned and output reserves are removed from reserves
1112 addSupply -= layer.second.first;
1113 addNormalizedReservesBB -= newNormalizedReserveBB;
1114 addNormalizedReservesAB -= newNormalizedReserveAB;
1116 for (auto &id : layer.second.second)
1118 auto idIT = fractionalInMap.find(id);
1119 CAmount newReservesForCurrencyBB = ((arith_uint256(newNormalizedReserveBB) * arith_uint256(weights[reserveMap[id]])) / bigLayerWeight).GetLow64();
1120 CAmount newReservesForCurrencyAB = ((arith_uint256(newNormalizedReserveAB) * arith_uint256(weights[reserveMap[id]])) / bigLayerWeight).GetLow64();
1122 // initialize or add to the new supply for this currency
1123 if (idIT == fractionalInMap.end())
1125 fractionalInMap[id] = std::make_pair(newReservesForCurrencyBB, newReservesForCurrencyAB);
1129 idIT->second.first += newReservesForCurrencyBB;
1130 idIT->second.second += newReservesForCurrencyAB;
1135 supplyAfterSell = supply + addSupply;
1136 assert(supplyAfterSell >= 0);
1138 supplyAfterBuySell = supplyAfterBuy + addSupply;
1139 assert(supplyAfterBuySell >= 0);
1141 reserveAfterSell = supply + addNormalizedReservesBB;
1142 assert(reserveAfterSell >= 0);
1144 reserveAfterBuySell = reserveAfterBuy + addNormalizedReservesAB;
1145 assert(reserveAfterBuySell >= 0);
1148 addNormalizedReserves = 0;
1150 // now calculate buy after sell
1151 for (auto &layer : fractionalLayersOut)
1153 arith_uint256 bigLayerWeight = arith_uint256(layer.first);
1154 CAmount totalLayerReserves = ((arith_uint256(supplyAfterSell) * bigLayerWeight) / bigSatoshi).GetLow64() + addNormalizedReserves;
1155 addNormalizedReserves += layer.second.first;
1156 CAmount newSupply = CalculateFractionalOut(layer.second.first, supplyAfterSell + addSupply, totalLayerReserves, layer.first);
1157 arith_uint256 bigNewSupply(newSupply);
1158 addSupply += newSupply;
1159 for (auto &id : layer.second.second)
1161 auto idIT = fractionalOutMap.find(id);
1163 assert(idIT != fractionalOutMap.end());
1165 idIT->second.second += ((bigNewSupply * weights[reserveMap[id]]) / bigLayerWeight).GetLow64();
1169 // now loop through all currencies, calculate conversion rates for each based on mean of all prices that we calculate for
1170 // buy before sell and sell before buy
1171 std::vector<int64_t> fractionalSizes(numCurrencies,0);
1172 std::vector<int64_t> reserveSizes(numCurrencies,0);
1174 for (int i = 0; i < numCurrencies; i++)
1176 // each coin has an amount of reserve in, an amount of fractional in, and potentially two delta amounts in one of the
1177 // fractionalInMap or fractionalOutMap maps, one for buy before sell and one for sell before buy.
1178 // add the mean of the delta amounts to the appropriate side of the equation and calculate a price for each
1180 auto fractionalOutIT = fractionalOutMap.find(currencies[i]);
1181 auto fractionalInIT = fractionalInMap.find(currencies[i]);
1183 auto inputReserve = inputReserves[i];
1184 auto inputFraction = inputFractional[i];
1185 reserveSizes[i] = inputReserve;
1186 fractionalSizes[i] = inputFraction;
1188 CAmount fractionDelta = 0, reserveDelta = 0;
1190 if (fractionalOutIT != fractionalOutMap.end())
1192 arith_uint256 bigFractionDelta(fractionalOutIT->second.first);
1193 fractionDelta = ((bigFractionDelta + arith_uint256(fractionalOutIT->second.second)) >> 1).GetLow64();
1194 assert(inputFraction + fractionDelta > 0);
1196 fractionalSizes[i] += fractionDelta;
1197 rates[i] = ((arith_uint256(inputReserve) * bigSatoshi) / arith_uint256(fractionalSizes[i])).GetLow64();
1199 // add the new reserve and supply to the currency
1200 newState.supply += fractionDelta;
1202 // all reserves have been calculated using a substituted value, which was 1:1 for native initially
1203 newState.reserves[i] += inputFractional[i] ? NativeToReserveRaw(fractionDelta, rates[i]) : inputReserves[i];
1205 else if (fractionalInIT != fractionalInMap.end())
1207 arith_uint256 bigReserveDelta(fractionalInIT->second.first);
1208 CAmount adjustedReserveDelta = NativeToReserve(((bigReserveDelta + arith_uint256(fractionalInIT->second.second)) >> 1).GetLow64(), i);
1209 reserveSizes[i] += adjustedReserveDelta;
1210 assert(inputFraction > 0);
1212 rates[i] = ((arith_uint256(reserveSizes[i]) * bigSatoshi) / arith_uint256(inputFraction)).GetLow64();
1214 // subtract the fractional and reserve that has left the currency
1215 newState.supply -= inputFraction;
1216 newState.reserves[i] -= adjustedReserveDelta;
1220 // if we have cross conversions, complete a final conversion with the updated currency, including all of the
1221 // cross conversion outputs to their final currency destinations
1222 if (pCrossConversions)
1224 bool convertRToR = false;
1225 std::vector<CAmount> reservesRToR(numCurrencies, 0); // keep track of reserve inputs to convert to the fractional currency
1227 // now add all cross conversions, determine how much of the converted fractional should be converted back to each
1228 // reserve currency. after adding all together, convert all to each reserve and average the price again
1229 for (int i = 0; i < numCurrencies; i++)
1231 // add up all conversion amounts for each fractional to each reserve-to-reserve conversion
1232 for (int j = 0; j < numCurrencies; j++)
1234 // convert this much of currency indexed by i into currency indexed by j
1235 // figure out how much fractional the amount of currency represents and add it to the total
1236 // fractionalIn for the currency indexed by j
1237 if ((*pCrossConversions)[i][j])
1240 reservesRToR[i] += (*pCrossConversions)[i][j];
1247 std::vector<CAmount> scratchValues(numCurrencies, 0);
1248 std::vector<CAmount> fractionsToConvert(numCurrencies, 0);
1250 // add fractional created to be converted to its destination
1251 for (int i = 0; i < reservesRToR.size(); i++)
1253 if (reservesRToR[i])
1255 for (int j = 0; j < (*pCrossConversions)[i].size(); j++)
1257 if ((*pCrossConversions)[i][j])
1259 fractionsToConvert[j] += ReserveToNativeRaw((*pCrossConversions)[i][j], rates[i]);
1265 std::vector<CAmount> _viaPrices;
1266 std::vector<CAmount> &viaPrices(pViaPrices ? *pViaPrices : _viaPrices);
1267 CCurrencyState intermediateState = newState;
1268 viaPrices = intermediateState.ConvertAmounts(scratchValues, fractionsToConvert, newState);
1274 _newState = newState;
1277 for (int i = 0; i < rates.size(); i++)
1281 rates[i] = PriceInReserve(i);
1287 CAmount CCurrencyState::ConvertAmounts(CAmount inputReserve, CAmount inputFraction, CCurrencyState &newState, int32_t reserveIndex) const
1289 int32_t numCurrencies = currencies.size();
1290 if (reserveIndex >= numCurrencies)
1292 printf("%s: reserve index out of range\n", __func__);
1295 std::vector<CAmount> inputReserves(numCurrencies);
1296 inputReserves[reserveIndex] = inputReserve;
1297 std::vector<CAmount> inputFractional(numCurrencies);
1298 inputFractional[reserveIndex] = inputFraction;
1299 std::vector<CAmount> retVal = ConvertAmounts(inputReserves,
1302 return retVal[reserveIndex];
1305 UniValue CReserveInOuts::ToUniValue() const
1307 UniValue retVal(UniValue::VOBJ);
1308 retVal.push_back(Pair("reservein", reserveIn));
1309 retVal.push_back(Pair("reserveout", reserveOut));
1310 retVal.push_back(Pair("reserveoutconverted", reserveOutConverted));
1311 retVal.push_back(Pair("nativeoutconverted", nativeOutConverted));
1312 retVal.push_back(Pair("reserveconversionfees", reserveConversionFees));
1316 UniValue CReserveTransactionDescriptor::ToUniValue() const
1318 UniValue retVal(UniValue::VOBJ);
1319 UniValue inOuts(UniValue::VARR);
1320 for (auto &oneInOut : currencies)
1322 UniValue oneIOUni(UniValue::VOBJ);
1323 oneIOUni.push_back(Pair("currency", EncodeDestination(CIdentityID(oneInOut.first))));
1324 oneIOUni.push_back(Pair("inouts", oneInOut.second.ToUniValue()));
1325 inOuts.push_back(oneIOUni);
1327 retVal.push_back(Pair("inouts", inOuts));
1328 retVal.push_back(Pair("nativein", nativeIn));
1329 retVal.push_back(Pair("nativeout", nativeOut));
1330 retVal.push_back(Pair("nativeconversionfees", nativeConversionFees));
1334 void CReserveTransactionDescriptor::AddReserveInput(const uint160 ¤cy, CAmount value)
1336 //printf("adding %ld:%s reserve input\n", value, EncodeDestination(CIdentityID(currency)).c_str());
1337 currencies[currency].reserveIn += value;
1340 void CReserveTransactionDescriptor::AddReserveOutput(const uint160 ¤cy, CAmount value)
1342 //printf("adding %ld:%s reserve output\n", value, EncodeDestination(CIdentityID(currency)).c_str());
1343 currencies[currency].reserveOut += value;
1346 void CReserveTransactionDescriptor::AddReserveOutConverted(const uint160 ¤cy, CAmount value)
1348 currencies[currency].reserveOutConverted += value;
1351 void CReserveTransactionDescriptor::AddNativeOutConverted(const uint160 ¤cy, CAmount value)
1353 currencies[currency].nativeOutConverted += value;
1356 void CReserveTransactionDescriptor::AddReserveConversionFees(const uint160 ¤cy, CAmount value)
1358 currencies[currency].reserveConversionFees += value;
1361 void CReserveTransactionDescriptor::AddReserveOutput(const CTokenOutput &ro)
1363 flags |= IS_RESERVE;
1364 for (auto &oneCur : ro.reserveValues.valueMap)
1366 if (oneCur.first != ASSETCHAINS_CHAINID && oneCur.second)
1368 AddReserveOutput(oneCur.first, oneCur.second);
1373 void CReserveTransactionDescriptor::AddReserveTransfer(const CReserveTransfer &rt)
1375 flags |= IS_RESERVE;
1376 for (auto &oneCur : rt.TotalCurrencyOut().valueMap)
1378 if (oneCur.first != ASSETCHAINS_CHAINID && oneCur.second)
1380 AddReserveOutput(oneCur.first, oneCur.second);
1385 CAmount CReserveTransactionDescriptor::AllFeesAsNative(const CCurrencyState ¤cyState) const
1387 CAmount nativeFees = NativeFees();
1388 CCurrencyValueMap reserveFees = ReserveFees();
1389 for (int i = 0; i < currencyState.currencies.size(); i++)
1391 auto it = reserveFees.valueMap.find(currencyState.currencies[i]);
1392 if (it != reserveFees.valueMap.end())
1394 nativeFees += currencyState.ReserveToNative(it->second, i);
1400 CAmount CReserveTransactionDescriptor::AllFeesAsNative(const CCurrencyState ¤cyState, const std::vector<CAmount> &exchangeRates) const
1402 assert(exchangeRates.size() == currencyState.currencies.size());
1403 CAmount nativeFees = NativeFees();
1404 CCurrencyValueMap reserveFees = ReserveFees();
1405 for (int i = 0; i < currencyState.currencies.size(); i++)
1407 auto it = reserveFees.valueMap.find(currencyState.currencies[i]);
1408 if (it != reserveFees.valueMap.end())
1410 nativeFees += currencyState.ReserveToNativeRaw(it->second, exchangeRates[i]);
1416 CCurrencyValueMap CReserveTransactionDescriptor::ReserveFees(const uint160 &nativeID) const
1418 uint160 id = nativeID.IsNull() ? ASSETCHAINS_CHAINID : nativeID;
1419 CCurrencyValueMap retFees;
1420 for (auto &one : currencies)
1423 if (one.first != id)
1425 CAmount oneFee = one.second.reserveIn - (one.second.reserveOut - one.second.reserveOutConverted);
1428 retFees.valueMap[one.first] = oneFee;
1435 CAmount CReserveTransactionDescriptor::NativeFees() const
1437 return nativeIn - nativeOut;
1440 CCurrencyValueMap CReserveTransactionDescriptor::AllFeesAsReserve(const CCurrencyState ¤cyState, int defaultReserve) const
1442 CCurrencyValueMap reserveFees = ReserveFees();
1444 auto it = reserveFees.valueMap.find(currencyState.currencies[defaultReserve]);
1445 if (it != reserveFees.valueMap.end())
1447 it->second += currencyState.NativeToReserve(NativeFees(), defaultReserve);
1451 reserveFees.valueMap[currencyState.currencies[defaultReserve]] = NativeFees();
1456 CCurrencyValueMap CReserveTransactionDescriptor::AllFeesAsReserve(const CCurrencyState ¤cyState, const std::vector<CAmount> &exchangeRates, int defaultReserve) const
1458 CCurrencyValueMap reserveFees = ReserveFees();
1460 auto it = reserveFees.valueMap.find(currencyState.currencies[defaultReserve]);
1461 if (it != reserveFees.valueMap.end())
1463 it->second += currencyState.NativeToReserveRaw(NativeFees(), exchangeRates[defaultReserve]);
1467 reserveFees.valueMap[currencyState.currencies[defaultReserve]] = NativeFees();
1473 * Checks all structural aspects of the reserve part of a transaction that may have reserve inputs and/or outputs
1475 CReserveTransactionDescriptor::CReserveTransactionDescriptor(const CTransaction &tx, const CCoinsViewCache &view, int32_t nHeight) :
1483 nativeConversionFees(0)
1485 // market conversions can have any number of both buy and sell conversion outputs, this is used to make efficient, aggregated
1486 // reserve transfer operations with conversion
1488 // limit conversion outputs may have multiple outputs with different input amounts and destinations,
1489 // but they must not be mixed in a transaction with any dissimilar set of conditions on the output,
1490 // including mixing with market orders, parity of buy or sell, limit value and validbefore values,
1491 // or the transaction is considered invalid
1493 // no inputs are valid at height 0
1500 int32_t solutionVersion = CConstVerusSolutionVector::activationHeight.ActiveVersion(nHeight);
1502 // reserve descriptor transactions cannot run until identity activates
1503 if (!chainActive.LastTip() || solutionVersion < CConstVerusSolutionVector::activationHeight.ACTIVATE_IDENTITY)
1508 bool isPBaaS = solutionVersion >= CActivationHeight::ACTIVATE_PBAAS;
1509 bool isPBaaSActivation = CConstVerusSolutionVector::activationHeight.IsActivationHeight(CActivationHeight::ACTIVATE_PBAAS, nHeight);
1510 bool loadedCurrencies = false;
1512 CNameReservation nameReservation;
1515 std::vector<CPBaaSNotarization> notarizations;
1516 CCurrencyValueMap importGeneratedCurrency;
1520 for (int i = 0; i < tx.vout.size(); i++)
1524 if (tx.vout[i].scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid())
1528 case EVAL_IDENTITY_RESERVATION:
1530 // one name reservation per transaction
1531 if (p.version < p.VERSION_V3 || !p.vData.size() || nameReservation.IsValid() || !(nameReservation = CNameReservation(p.vData[0])).IsValid())
1537 if (identity.IsValid())
1539 if (identity.name == nameReservation.name)
1541 flags |= IS_IDENTITY_DEFINITION + IS_HIGH_FEE;
1553 case EVAL_IDENTITY_PRIMARY:
1555 // one identity per transaction, unless we are first block coinbase on a PBaaS chain
1557 if (p.version < p.VERSION_V3 ||
1559 (solutionVersion < CActivationHeight::ACTIVATE_PBAAS && identity.IsValid()) ||
1560 !(identity = CIdentity(p.vData[0])).IsValid())
1566 flags |= IS_IDENTITY;
1567 if (nameReservation.IsValid())
1569 if (identity.name == nameReservation.name)
1571 flags |= IS_IDENTITY_DEFINITION + IS_HIGH_FEE;
1583 case EVAL_RESERVE_DEPOSIT:
1586 if (!p.vData.size() || !(rd = CReserveDeposit(p.vData[0])).IsValid())
1592 for (auto &oneCur : rd.reserveValues.valueMap)
1594 if (oneCur.first != ASSETCHAINS_CHAINID)
1596 AddReserveOutput(oneCur.first, oneCur.second);
1602 case EVAL_RESERVE_OUTPUT:
1605 if (!p.vData.size() || !(ro = CTokenOutput(p.vData[0])).IsValid())
1611 for (auto &oneCur : ro.reserveValues.valueMap)
1613 if (oneCur.first != ASSETCHAINS_CHAINID && oneCur.second)
1615 AddReserveOutput(oneCur.first, oneCur.second);
1621 case EVAL_RESERVE_TRANSFER:
1623 CReserveTransfer rt;
1624 if (!p.vData.size() || !(rt = CReserveTransfer(p.vData[0])).IsValid())
1630 AddReserveTransfer(rt);
1634 case EVAL_RESERVE_EXCHANGE:
1642 case EVAL_CROSSCHAIN_IMPORT:
1650 //UniValue jsonTx(UniValue::VOBJ);
1651 //TxToUniv(tx, uint256(), jsonTx);
1652 //printf("%s: Coinbase transaction:\n%s\n", __func__, jsonTx.write(1,2).c_str());
1653 CCurrencyDefinition oneCurDef;
1655 for (int j = 0; j < tx.vout.size(); j++)
1657 if (tx.vout[j].scriptPubKey.IsPayToCryptoCondition(tempP) &&
1659 tempP.evalCode == EVAL_CURRENCY_DEFINITION &&
1660 tempP.vData.size() &&
1661 (oneCurDef = CCurrencyDefinition(tempP.vData[0])).IsValid())
1663 //printf("%s: Adding currency:\n%s\n", __func__, oneCurDef.ToUniValue().write(1,2).c_str());
1664 ConnectedChains.currencyDefCache.insert(std::make_pair(oneCurDef.GetID(), oneCurDef));
1667 loadedCurrencies = true;
1670 CCrossChainImport cci, sysCCI;
1672 // if this is an import, add the amount imported to the reserve input and the amount of reserve output as
1673 // the amount available to take from this transaction in reserve as an import fee
1674 if (!p.vData.size() || !(cci = CCrossChainImport(p.vData[0])).IsValid())
1681 flags |= (IS_IMPORT + IS_HIGH_FEE);
1683 CCurrencyDefinition importCurrencyDef, sourceSystemDef;
1684 CCrossChainExport ccx;
1686 notarizations.push_back(CPBaaSNotarization());
1687 CPBaaSNotarization &importNotarization = notarizations.back();
1689 int32_t importNotarizationOut;
1690 int32_t eOutStart, eOutEnd;
1691 std::vector<CReserveTransfer> importTransfers;
1693 // if this is the source system for a cci that we already processed, skip it
1694 if ((cci.flags & cci.FLAG_SOURCESYSTEM) || (cci.flags & cci.FLAG_DEFINITIONIMPORT))
1699 if (!cci.IsDefinitionImport())
1701 if (!cci.GetImportInfo(tx, nHeight, i, ccx, sysCCI, sysCCIOut, importNotarization, importNotarizationOut, eOutStart, eOutEnd, importTransfers))
1708 importCurrencyDef = ConnectedChains.GetCachedCurrency(cci.importCurrencyID);
1709 sourceSystemDef = ConnectedChains.GetCachedCurrency(cci.sourceSystemID);
1711 if (!sourceSystemDef.IsValid() || !importCurrencyDef.IsValid())
1718 // get the chain definition of the chain we are importing
1719 std::vector<CTxOut> checkOutputs;
1720 CCurrencyValueMap importedCurrency, gatewayDeposits, spentCurrencyOut;
1722 CCoinbaseCurrencyState checkState = importNotarization.currencyState;
1723 CCoinbaseCurrencyState newState;
1725 /* if (tx.IsCoinBase())
1727 printf("%s: currency state before revert: %s\n", __func__, checkState.ToUniValue().write(1,2).c_str());
1730 checkState.RevertReservesAndSupply();
1731 if (!cci.IsPostLaunch() && cci.IsInitialLaunchImport())
1733 checkState.SetLaunchClear();
1736 /*if (tx.IsCoinBase())
1738 printf("%s: currency state after revert: %s\n", __func__, checkState.ToUniValue().write(1,2).c_str());
1741 CReserveTransactionDescriptor rtxd;
1742 if (!rtxd.AddReserveTransferImportOutputs(sourceSystemDef,
1743 ConnectedChains.thisChain,
1758 /*if (tx.IsCoinBase())
1760 printf("%s: currency state after import: %s\n", __func__, newState.ToUniValue().write(1,2).c_str());
1761 printf("%s: coinbase rtxd: %s\n", __func__, rtxd.ToUniValue().write(1,2).c_str());
1764 importGeneratedCurrency += importedCurrency;
1765 if (newState.primaryCurrencyOut)
1767 importGeneratedCurrency.valueMap[cci.importCurrencyID] = newState.primaryCurrencyOut;
1769 if (nHeight == 1 && cci.importCurrencyID == ASSETCHAINS_CHAINID)
1771 importGeneratedCurrency.valueMap[ASSETCHAINS_CHAINID] += gatewayDeposits.valueMap[ASSETCHAINS_CHAINID];
1775 printf("%s: importGeneratedCurrency:\n%s\nnewState:\n%s\n",
1777 importGeneratedCurrency.ToUniValue().write(1,2).c_str(),
1778 newState.ToUniValue().write(1,2).c_str());
1781 for (auto &oneOutCur : cci.totalReserveOutMap.valueMap)
1783 AddReserveOutput(oneOutCur.first, oneOutCur.second);
1789 // this check will need to be made complete by preventing mixing both here and where the others
1791 case EVAL_CROSSCHAIN_EXPORT:
1793 CCrossChainExport ccx;
1794 if (!p.vData.size() ||
1795 !(ccx = CCrossChainExport(p.vData[0])).IsValid())
1801 //printf("%s: ccx: %s\n", __func__, ccx.ToUniValue().write(1,2).c_str());
1802 importGeneratedCurrency -= ccx.totalBurned;
1807 case EVAL_CURRENCY_DEFINITION:
1809 CCurrencyDefinition cDef;
1810 if (!p.vData.size() ||
1811 !(cDef = CCurrencyDefinition(p.vData[0])).IsValid())
1822 CCurrencyValueMap output = tx.vout[i].scriptPubKey.ReserveOutValue();
1823 output.valueMap.erase(ASSETCHAINS_CHAINID);
1824 for (auto &oneOutCur : output.valueMap)
1826 AddReserveOutput(oneOutCur.first, oneOutCur.second);
1831 /*if (flags & IS_IMPORT)
1833 printf("currencies after proccessing code %d:\n", p.evalCode);
1834 for (auto &oneInOut : currencies)
1836 printf("{\"currency\":\"%s\",\"nativeOutConverted\":\"%ld\",\"reserveConversionFees\":\"%ld\",\"reserveIn\":\"%ld\",\"reserveOut\":\"%ld\",\"reserveOutConverted\":\"%ld\"}\n",
1837 EncodeDestination(CIdentityID(oneInOut.first)).c_str(),
1838 oneInOut.second.nativeOutConverted,
1839 oneInOut.second.reserveConversionFees,
1840 oneInOut.second.reserveIn,
1841 oneInOut.second.reserveOut,
1842 oneInOut.second.reserveOutConverted);
1847 // we have all inputs, outputs, and fees, if check inputs, we can check all for consistency
1848 // inputs may be in the memory pool or on the blockchain
1849 CAmount dummyInterest;
1850 nativeOut = tx.GetValueOut();
1851 nativeIn = view.GetValueIn(nHeight, &dummyInterest, tx);
1853 if (importGeneratedCurrency.valueMap.count(ASSETCHAINS_CHAINID))
1855 nativeIn += importGeneratedCurrency.valueMap[ASSETCHAINS_CHAINID];
1856 importGeneratedCurrency.valueMap.erase(ASSETCHAINS_CHAINID);
1859 // if it is a conversion to reserve, the amount in is accurate, since it is from the native coin, if converting to
1860 // the native PBaaS coin, the amount input is a sum of all the reserve token values of all of the inputs
1861 auto reservesIn = (view.GetReserveValueIn(nHeight, tx) + importGeneratedCurrency).CanonicalMap();
1863 /* if (flags & IS_IMPORT || flags & IS_EXPORT)
1865 printf("%s: importGeneratedCurrency:\n%s\nreservesIn:\n%s\n", __func__, importGeneratedCurrency.ToUniValue().write(1,2).c_str(),
1866 reservesIn.ToUniValue().write(1,2).c_str());
1869 for (auto &oneCur : currencies)
1871 oneCur.second.reserveIn = 0;
1873 if (reservesIn.valueMap.size())
1875 flags |= IS_RESERVE;
1876 for (auto &oneCur : reservesIn.valueMap)
1878 currencies[oneCur.first].reserveIn = oneCur.second;
1882 if (!IsReserve() && ReserveOutputMap().valueMap.size())
1884 flags |= IS_RESERVE;
1890 // this is only valid when used after AddReserveTransferImportOutputs on an empty CReserveTransactionDwescriptor
1891 CCurrencyValueMap CReserveTransactionDescriptor::GeneratedImportCurrency(const uint160 &fromSystemID, const uint160 &importSystemID, const uint160 &importCurrencyID) const
1893 // only currencies that are controlled by the exporting chain or created in conversion by the importing currency
1894 // can be created from nothing
1895 // add newly created currency here that meets those criteria
1896 CCurrencyValueMap retVal;
1897 for (auto one : currencies)
1899 bool isImportCurrency = one.first == importCurrencyID;
1900 if ((one.second.nativeOutConverted && isImportCurrency) ||
1901 (one.second.reserveIn && fromSystemID != ASSETCHAINS_CHAINID && ConnectedChains.GetCachedCurrency(one.first).systemID == fromSystemID))
1903 retVal.valueMap[one.first] = isImportCurrency ? one.second.nativeOutConverted : one.second.reserveIn;
1909 CReserveTransfer CReserveTransfer::GetRefundTransfer() const
1911 CReserveTransfer rt = *this;
1914 if (rt.IsImportToSource())
1916 newDest = rt.FirstCurrency();
1920 newDest = rt.destCurrencyID;
1923 // convert full ID destinations to normal ID outputs, since it's refund, full ID will be on this chain already
1924 if (rt.destination.type == CTransferDestination::DEST_FULLID)
1926 CIdentity(rt.destination.destination);
1927 rt.destination = CTransferDestination(CTransferDestination::DEST_ID, rt.destination.destination);
1930 // turn it into a normal transfer, which will create an unconverted output
1931 rt.flags &= ~(CReserveTransfer::DOUBLE_SEND |
1932 CReserveTransfer::PRECONVERT |
1933 CReserveTransfer::CONVERT |
1934 CReserveTransfer::CROSS_SYSTEM |
1935 CReserveTransfer::IMPORT_TO_SOURCE);
1939 rt.flags &= ~CReserveTransfer::MINT_CURRENCY;
1940 rt.reserveValues.valueMap.begin()->second = 0;
1942 rt.flags |= rt.REFUND;
1943 rt.destCurrencyID = newDest;
1947 bool CReserveTransfer::GetTxOut(const CCurrencyValueMap &reserves, int64_t nativeAmount, CTxOut &txOut) const
1949 bool makeNormalOutput = true;
1952 makeNormalOutput = false;
1953 CReserveTransfer nextLegTransfer = CReserveTransfer(CReserveTransfer::VERSION_INVALID);
1955 // if we have a nested transfer, use it
1956 if (destination.TypeNoFlags() == destination.DEST_NESTEDTRANSFER)
1958 printf("%s: Nested currency transfers not yet supported\n", __func__);
1960 // get the reserve transfer from the raw data and
1961 CReserveTransfer rt(destination.destination);
1964 // input currency, not fees, come from the output of the
1965 // last leg. fees are converted and transfered independently.
1966 rt.reserveValues = reserves;
1967 rt.feeCurrencyID = destination.gatewayID;
1968 rt.destination.fees = destination.fees;
1969 nextLegTransfer = rt;
1974 // make an output to the gateway ID, which should be another system, since there is
1975 // no reserve transfer left for instructions to do anything else worth another leg
1976 // we need to have correct fees in the destination currency available
1977 CTransferDestination lastLegDest = CTransferDestination(destination);
1978 lastLegDest.ClearGatewayLeg();
1979 uint32_t newFlags = CReserveTransfer::VALID;
1980 if (destination.gatewayID != ASSETCHAINS_CHAINID)
1982 newFlags |= CReserveTransfer::CROSS_SYSTEM;
1983 CCurrencyValueMap newReserves = reserves;
1986 newReserves.valueMap[ASSETCHAINS_CHAINID] = nativeAmount;
1988 nextLegTransfer = CReserveTransfer(newFlags,
1990 destination.gatewayID,
1992 destination.gatewayID,
1995 destination.gatewayID);
1999 // if our destination is here, add unused fees to native output and drop through to make normal output
2000 nativeAmount += destination.fees;
2001 makeNormalOutput = true;
2004 if (nextLegTransfer.IsValid())
2006 // emit a reserve exchange output
2008 CCcontract_info *cp;
2009 cp = CCinit(&CC, EVAL_RESERVE_TRANSFER);
2010 CPubKey pk = CPubKey(ParseHex(CC.CChexstr));
2012 // transfer it back to the source chain and to our address
2013 std::vector<CTxDestination> dests = std::vector<CTxDestination>({pk.GetID()});
2014 txOut = CTxOut(nativeAmount, MakeMofNCCScript(CConditionObj<CReserveTransfer>(EVAL_RESERVE_TRANSFER, dests, 1, &nextLegTransfer)));
2018 if (makeNormalOutput)
2020 // make normal output to the destination, which must be valid
2021 if (!reserves.valueMap.size() && nativeAmount)
2023 CTxDestination dest = TransferDestinationToDestination(destination);
2024 if (dest.which() == COptCCParams::ADDRTYPE_ID ||
2025 dest.which() == COptCCParams::ADDRTYPE_PK ||
2026 dest.which() == COptCCParams::ADDRTYPE_PKH ||
2027 dest.which() == COptCCParams::ADDRTYPE_SH)
2029 txOut = CTxOut(nativeAmount, GetScriptForDestination(dest));
2035 CTxDestination dest = TransferDestinationToDestination(destination);
2036 if (dest.which() == COptCCParams::ADDRTYPE_ID || dest.which() == COptCCParams::ADDRTYPE_PK || dest.which() == COptCCParams::ADDRTYPE_PKH)
2038 std::vector<CTxDestination> dests = std::vector<CTxDestination>({TransferDestinationToDestination(destination)});
2039 CTokenOutput ro = CTokenOutput(reserves);
2040 txOut = CTxOut(nativeAmount, MakeMofNCCScript(CConditionObj<CTokenOutput>(EVAL_RESERVE_OUTPUT, dests, 1, &ro)));
2048 CReserveTransfer RefundExport(const CBaseChainObject *objPtr)
2050 if (objPtr->objectType == CHAINOBJ_RESERVETRANSFER)
2052 return ((CChainObject<CReserveTransfer> *)objPtr)->object.GetRefundTransfer();
2054 return CReserveTransfer();
2057 // the source currency indicates the system from which the import comes, but the imports may contain additional
2058 // currencies that are supported in that system and are not limited to the native currency. Fees are assumed to
2059 // be covered by the native currency of the source or source currency, if this is a reserve conversion. That
2060 // means that all explicit fees are assumed to be in the currency of the source.
2061 bool CReserveTransactionDescriptor::AddReserveTransferImportOutputs(const CCurrencyDefinition &systemSource,
2062 const CCurrencyDefinition &systemDest,
2063 const CCurrencyDefinition &importCurrencyDef,
2064 const CCoinbaseCurrencyState &importCurrencyState,
2065 const std::vector<CReserveTransfer> &exportObjects,
2066 std::vector<CTxOut> &vOutputs,
2067 CCurrencyValueMap &importedCurrency,
2068 CCurrencyValueMap &gatewayDepositsIn,
2069 CCurrencyValueMap &spentCurrencyOut,
2070 CCoinbaseCurrencyState *pNewCurrencyState)
2072 // easy way to refer to return currency state or a dummy without conditionals
2073 CCoinbaseCurrencyState _newCurrencyState;
2074 if (!pNewCurrencyState)
2076 pNewCurrencyState = &_newCurrencyState;
2078 CCoinbaseCurrencyState &newCurrencyState = *pNewCurrencyState;
2080 // prepare to update ins, outs, emissions, and last pricing
2081 newCurrencyState = importCurrencyState;
2082 newCurrencyState.ClearForNextBlock();
2084 bool isFractional = importCurrencyDef.IsFractional();
2086 // reserve currency amounts converted to fractional
2087 CCurrencyValueMap reserveConverted;
2089 // fractional currency amount and the reserve it is converted to
2090 CCurrencyValueMap fractionalConverted;
2092 CCurrencyValueMap newConvertedReservePool;
2094 std::map<uint160,int32_t> currencyIndexMap = importCurrencyDef.GetCurrenciesMap();
2096 uint160 systemSourceID = systemSource.GetID();
2097 uint160 systemDestID = systemDest.GetID(); // native on destination system
2098 uint160 importCurrencyID = importCurrencyDef.GetID();
2100 // this matrix tracks n-way currency conversion
2101 // each entry contains the original amount of the row's (dim 0) currency to be converted to the currency position of its column
2102 int32_t numCurrencies = importCurrencyDef.currencies.size();
2103 std::vector<std::vector<CAmount>> crossConversions(numCurrencies, std::vector<CAmount>(numCurrencies, 0));
2104 int32_t systemDestIdx = currencyIndexMap.count(systemDestID) ? currencyIndexMap[systemDestID] : -1;
2106 // used to keep track of burned fractional currency. this currency is subtracted from the
2107 // currency supply, but not converted. In doing so, it can either raise the price of the fractional
2108 // currency in all other currencies, or increase the reserve ratio of all currencies by some amount.
2109 CAmount burnedChangePrice = 0;
2110 CAmount burnedChangeWeight = 0;
2111 CAmount secondaryBurned = 0;
2113 // this is cached here, but only used for pre-conversions
2114 CCurrencyValueMap preConvertedOutput;
2115 CCurrencyValueMap preConvertedReserves;
2116 CAmount preAllocTotal = 0;
2118 // determine if we are importing from a gateway currency
2119 // if so, we can use it to mint gateway currencies via the gateway, and deal with fees and conversions on
2120 // our converter currency
2121 uint160 nativeSourceCurrencyID = systemSource.IsGateway() ? systemSource.gatewayID : systemSource.systemID;
2122 if (nativeSourceCurrencyID != systemSourceID)
2124 printf("%s: systemSource import %s is not from either gateway, PBaaS chain, or other system level currency\n", __func__, systemSource.name.c_str());
2125 LogPrintf("%s: systemSource import %s is not from either gateway, PBaaS chain, or other system level currency\n", __func__, systemSource.name.c_str());
2128 bool isCrossSystemImport = nativeSourceCurrencyID != systemDestID;
2132 for (auto &oneInOut : currencies)
2134 oneInOut.second.reserveIn = 0;
2135 oneInOut.second.reserveOut = 0;
2139 CCcontract_info *cp;
2141 CCurrencyValueMap transferFees; // calculated fees based on all transfers/conversions, etc.
2142 CCurrencyValueMap convertedFees; // post conversion transfer fees
2143 CCurrencyValueMap liquidityFees; // for fractionals, this value is added to the currency itself
2145 bool feeOutputStart = false; // fee outputs must come after all others, this indicates they have started
2146 int nFeeOutputs = 0; // number of fee outputs
2148 int32_t totalCarveOut = importCurrencyDef.GetTotalCarveOut();
2149 CCurrencyValueMap totalCarveOuts;
2150 CAmount totalMinted = 0;
2151 CAmount exporterReward = 0;
2152 CAmount currencyRegistrationFee = 0;
2153 CAmount totalNativeFee = 0;
2154 CAmount totalVerusFee = 0;
2156 for (int i = 0; i <= exportObjects.size(); i++)
2158 CReserveTransfer curTransfer;
2160 if (i == exportObjects.size())
2162 // this will be the primary fee output
2163 curTransfer = CReserveTransfer(CReserveTransfer::VALID + CReserveTransfer::FEE_OUTPUT,
2169 CTransferDestination());
2171 else if (importCurrencyState.IsRefunding() ||
2172 (exportObjects[i].IsPreConversion() && importCurrencyState.IsLaunchCompleteMarker()) ||
2173 (exportObjects[i].IsConversion() && !exportObjects[i].IsPreConversion() && !importCurrencyState.IsLaunchCompleteMarker()))
2175 curTransfer = exportObjects[i].GetRefundTransfer();
2179 curTransfer = exportObjects[i];
2182 if (((importCurrencyID != curTransfer.FirstCurrency()) && curTransfer.IsImportToSource()) ||
2183 ((importCurrencyID != curTransfer.destCurrencyID) && !curTransfer.IsImportToSource()))
2185 printf("%s: Importing to source currency w/o flag or importing to destination w/source flag:\n%s\n", __func__, curTransfer.ToUniValue().write(1,2).c_str());
2186 LogPrintf("%s: Importing to source currency without flag or importing to destination with source flag\n", __func__);
2190 //printf("currency transfer #%d:\n%s\n", i, curTransfer.ToUniValue().write(1,2).c_str());
2191 CCurrencyDefinition _currencyDest;
2192 const CCurrencyDefinition ¤cyDest = curTransfer.IsRefund() ?
2193 (_currencyDest = ConnectedChains.GetCachedCurrency(curTransfer.FirstCurrency())) :
2194 (importCurrencyID == curTransfer.destCurrencyID) ?
2196 (_currencyDest = ConnectedChains.GetCachedCurrency(curTransfer.destCurrencyID));
2198 if (!currencyDest.IsValid())
2200 printf("%s: invalid currency or currency not found %s\n", __func__, EncodeDestination(CIdentityID(curTransfer.destCurrencyID)).c_str());
2201 LogPrintf("%s: invalid currency or currency not found %s\n", __func__, EncodeDestination(CIdentityID(curTransfer.destCurrencyID)).c_str());
2205 //printf("%s: transferFees: %s\n", __func__, transferFees.ToUniValue().write(1,2).c_str());
2207 if (i == exportObjects.size() || curTransfer.IsValid())
2211 // at the end, make our fee outputs
2212 if (i == exportObjects.size())
2214 // only tokens release pre-allocations here
2215 // PBaaS chain pre-allocations and initial pre-conversion
2216 // supply come out of the coinbase, since we don't mint
2217 // native currency out of a non-coinbase import
2218 if (importCurrencyState.IsLaunchClear())
2220 // we need to pay 1/2 of the launch cost for the launch system in launch fees
2221 // remainder was paid when the currency is defined
2222 currencyRegistrationFee = systemSource.LaunchFeeImportShare(importCurrencyDef.options);
2223 transferFees.valueMap[importCurrencyDef.launchSystemID] += currencyRegistrationFee;
2224 if (importCurrencyDef.launchSystemID != systemDestID)
2226 // this fee input was injected into the currency at definition
2227 importedCurrency.valueMap[importCurrencyDef.launchSystemID] += currencyRegistrationFee;
2228 AddReserveInput(importCurrencyDef.launchSystemID, currencyRegistrationFee);
2232 if (importCurrencyDef.systemID != systemDestID && importCurrencyState.IsRefunding())
2234 gatewayDepositsIn.valueMap[systemDestID] += currencyRegistrationFee;
2236 nativeIn += currencyRegistrationFee;
2239 if (importCurrencyState.IsLaunchConfirmed())
2241 if (importCurrencyState.IsPrelaunch())
2243 // first time with launch clear on prelaunch, start supply at initial supply
2244 newCurrencyState.supply = newCurrencyState.initialSupply;
2247 // if we have finished importing all pre-launch exports, create all pre-allocation outputs
2248 for (auto &onePreAlloc : importCurrencyDef.preAllocation)
2250 // we need to make one output for each pre-allocation
2251 AddNativeOutConverted(importCurrencyID, onePreAlloc.second);
2252 if (importCurrencyID != systemDestID)
2254 AddReserveOutConverted(importCurrencyID, onePreAlloc.second);
2257 preAllocTotal += onePreAlloc.second;
2259 std::vector<CTxDestination> dests;
2260 if (onePreAlloc.first.IsNull())
2262 // if pre-alloc/pre-mine goes to NULL, send it to fee recipient who mines the final export
2263 dests = std::vector<CTxDestination>({TransferDestinationToDestination(curTransfer.destination)});
2267 dests = std::vector<CTxDestination>({CTxDestination(CIdentityID(onePreAlloc.first))});
2270 if (importCurrencyID == systemDestID)
2272 vOutputs.push_back(CTxOut(onePreAlloc.second, GetScriptForDestination(dests[0])));
2273 nativeOut += onePreAlloc.second;
2277 AddReserveOutput(importCurrencyID, onePreAlloc.second);
2278 CTokenOutput ro = CTokenOutput(importCurrencyID, onePreAlloc.second);
2279 vOutputs.push_back(CTxOut(0, MakeMofNCCScript(CConditionObj<CTokenOutput>(EVAL_RESERVE_OUTPUT, dests, 1, &ro))));
2282 if (importCurrencyDef.gatewayConverterIssuance)
2284 if (importCurrencyDef.IsPBaaSChain())
2286 preAllocTotal += importCurrencyDef.gatewayConverterIssuance;
2287 AddNativeOutConverted(importCurrencyID, importCurrencyDef.gatewayConverterIssuance);
2288 nativeOut += importCurrencyDef.gatewayConverterIssuance;
2294 // convert all fees to the system currency of the import
2295 // fees that started in fractional are already converted, so not considered
2296 CCurrencyValueMap conversionFees = ReserveConversionFeesMap().CanonicalMap();
2298 newCurrencyState.fees = transferFees.AsCurrencyVector(newCurrencyState.currencies);
2299 newCurrencyState.conversionFees = conversionFees.AsCurrencyVector(newCurrencyState.currencies);
2300 newCurrencyState.primaryCurrencyFees = transferFees.valueMap.count(importCurrencyID) ? transferFees.valueMap[importCurrencyID] : 0;
2301 newCurrencyState.primaryCurrencyConversionFees =
2302 conversionFees.valueMap.count(importCurrencyID) ? transferFees.valueMap[importCurrencyID] : 0;
2304 if (importCurrencyState.IsLaunchConfirmed() &&
2306 importCurrencyState.reserves[systemDestIdx])
2308 // 1/2 of all conversion fees go directly into the fractional currency itself
2309 liquidityFees = conversionFees / 2;
2310 transferFees -= liquidityFees;
2312 // setup conversion matrix for fees that are converted to
2313 // native (or launch currency of a PBaaS chain) from another reserve
2314 std::vector<std::pair<std::pair<uint160,CAmount>, std::pair<uint160,CAmount>>> feeConversions;
2316 // printf("%s: transferFees: %s\nreserveConverted: %s\nliquidityFees: %s\n", __func__, transferFees.ToUniValue().write(1,2).c_str(), reserveConverted.ToUniValue().write(1,2).c_str(), liquidityFees.ToUniValue().write(1,2).c_str());
2317 for (auto &oneFee : transferFees.valueMap)
2319 // only convert through "via" if we are going from one reserve to the system ID
2320 if (oneFee.first != importCurrencyID && oneFee.first != systemDestID)
2322 auto curIt = currencyIndexMap.find(oneFee.first);
2323 if (curIt == currencyIndexMap.end())
2325 printf("%s: Invalid fee currency for %s\n", __func__, curTransfer.ToUniValue().write(1,2).c_str());
2326 LogPrintf("%s: Invalid fee currency for %s\n", __func__, curTransfer.ToUniValue().write(1,2).c_str());
2329 int curIdx = curIt->second;
2331 // printf("%s: *this 1: %s\n", __func__, ToUniValue().write(1,2).c_str());
2333 CAmount oneFeeValue = 0;
2334 reserveConverted.valueMap[oneFee.first] += oneFee.second;
2335 crossConversions[curIdx][systemDestIdx] += oneFee.second;
2336 CAmount conversionPrice = importCurrencyState.IsLaunchCompleteMarker() ?
2337 importCurrencyState.conversionPrice[curIdx] :
2338 importCurrencyState.viaConversionPrice[curIdx];
2339 oneFeeValue = importCurrencyState.ReserveToNativeRaw(oneFee.second, conversionPrice);
2341 if (systemDestID == importCurrencyID)
2343 AddNativeOutConverted(oneFee.first, oneFeeValue);
2344 totalNativeFee += oneFeeValue;
2348 // if fractional currency is not native, one more conversion to native
2350 CCurrencyState::NativeToReserveRaw(oneFeeValue, importCurrencyState.viaConversionPrice[systemDestIdx]);
2351 totalNativeFee += oneFeeValue;
2352 newConvertedReservePool.valueMap[systemDestID] += oneFeeValue;
2353 AddReserveOutConverted(systemDestID, oneFeeValue);
2356 feeConversions.push_back(std::make_pair(std::make_pair(oneFee.first, oneFee.second),
2357 std::make_pair(systemDestID, oneFeeValue)));
2358 // printf("%s: *this 2: %s\n", __func__, ToUniValue().write(1,2).c_str());
2360 else if (oneFee.first == systemDestID)
2362 totalNativeFee += oneFee.second;
2364 else if (oneFee.first == importCurrencyID)
2366 // convert from fractional to system ID in the first, non-via stage, since this was
2367 // already fractional to begin with
2368 fractionalConverted.valueMap[systemDestID] += oneFee.second;
2369 AddNativeOutConverted(oneFee.first, -oneFee.second);
2371 CAmount convertedFractionalFee = CCurrencyState::NativeToReserveRaw(oneFee.second, importCurrencyState.conversionPrice[systemDestIdx]);
2372 totalNativeFee += convertedFractionalFee;
2373 newConvertedReservePool.valueMap[systemDestID] += convertedFractionalFee;
2374 AddReserveOutConverted(systemDestID, convertedFractionalFee);
2375 feeConversions.push_back(std::make_pair(std::make_pair(oneFee.first, oneFee.second),
2376 std::make_pair(systemDestID, convertedFractionalFee)));
2379 // loop through, subtract "from" and add "to"
2380 convertedFees = transferFees;
2381 if (feeConversions.size())
2383 for (auto &conversionPairs : feeConversions)
2385 convertedFees.valueMap[conversionPairs.first.first] -= conversionPairs.first.second;
2386 convertedFees.valueMap[conversionPairs.second.first] += conversionPairs.second.second;
2388 convertedFees = convertedFees.CanonicalMap();
2393 // since there is no support for taking reserves as fees, split any available
2394 // reserves fee from the launch chain, for example, between us and the exporter
2395 CTxDestination addr = CIdentityID(importCurrencyID);
2396 extern std::string NOTARY_PUBKEY;
2397 if (mapArgs.count("-mineraddress"))
2399 addr = DecodeDestination(mapArgs["-mineraddress"]);
2401 else if (!VERUS_NOTARYID.IsNull())
2403 addr = VERUS_NOTARYID;
2405 else if (!VERUS_DEFAULTID.IsNull())
2407 addr = VERUS_DEFAULTID;
2409 else if (!VERUS_NODEID.IsNull())
2411 addr = CIdentityID(VERUS_NODEID);
2413 else if (!NOTARY_PUBKEY.empty())
2416 std::vector<unsigned char> hexKey = ParseHex(NOTARY_PUBKEY);
2417 pkey.Set(hexKey.begin(), hexKey.end());
2418 addr = pkey.GetID();
2421 std::vector<CTxDestination> dests({addr});
2422 for (auto &oneFee : transferFees.valueMap)
2424 if (oneFee.first != systemDestID && oneFee.first != VERUS_CHAINID && oneFee.second)
2426 CAmount resExportFee = CCrossChainExport::CalculateExportFeeRaw(oneFee.second, numTransfers);
2427 CAmount exportSplit = CCrossChainExport::ExportReward(resExportFee);
2428 AddReserveOutput(oneFee.first, oneFee.second);
2430 CTokenOutput ro = CTokenOutput(oneFee.first, oneFee.second);
2431 //vOutputs.push_back(CTxOut(0, MakeMofNCCScript(CConditionObj<CTokenOutput>(EVAL_RESERVE_OUTPUT, dests, 1, &ro))));
2433 else if (oneFee.second)
2435 if (oneFee.first == systemDestID)
2437 totalNativeFee += oneFee.second;
2439 else if (importCurrencyState.IsLaunchConfirmed() && oneFee.first == VERUS_CHAINID)
2441 totalVerusFee += oneFee.second;
2445 convertedFees = transferFees;
2448 // export fee is sent to the export pool of the sending
2449 // system, exporter reward directly to the exporter
2450 CAmount exportFee = CCrossChainExport::CalculateExportFeeRaw(totalNativeFee, numTransfers);
2451 exporterReward = CCrossChainExport::ExportReward(exportFee);
2453 for (auto &oneFee : convertedFees.valueMap)
2455 if (oneFee.first == systemDestID)
2457 nativeOut += oneFee.second;
2461 AddReserveOutput(oneFee.first, oneFee.second);
2465 if (!curTransfer.destination.IsValid() || !exporterReward)
2469 curTransfer = CReserveTransfer(CReserveTransfer::VALID + CReserveTransfer::FEE_OUTPUT,
2470 systemDestID, exporterReward, systemDestID, 0, systemDestID, curTransfer.destination);
2476 CAmount explicitFees = curTransfer.nFees;
2477 transferFees.valueMap[curTransfer.feeCurrencyID] += explicitFees;
2479 // see if our destination is for a gateway or other blockchain and see if we are reserving some
2480 // fees for additional routing. if so, add those fees to the pass-through fees, which will get converted
2481 // to the target native currency and subtracted from this leg
2482 if (curTransfer.destination.HasGatewayLeg() && curTransfer.destination.fees)
2484 // we keep the destination fees in the same currency as the normal transfer fee, but
2485 // convert it as we move through systems and only use it for delivery to the system
2486 // of the destination.
2487 if (curTransfer.destination.fees)
2489 explicitFees += curTransfer.destination.fees;
2492 // convert fees to next destination native, if necessary/possible
2493 CCurrencyDefinition curNextDest = ConnectedChains.GetCachedCurrency(curTransfer.destination.gatewayID);
2494 uint160 nextDestSysID = curNextDest.IsGateway() ? curNextDest.gatewayID : curNextDest.systemID;
2496 // if it's already in the correct currency, nothing to do, otherwise convert if we can
2497 if (curTransfer.feeCurrencyID != nextDestSysID)
2499 if (!isFractional ||
2500 !currencyIndexMap.count(nextDestSysID) ||
2501 !currencyIndexMap.count(curTransfer.feeCurrencyID))
2503 printf("%s: next leg fee currency %s unavailable for conversion using %s\n", __func__, curNextDest.name.c_str(), importCurrencyDef.name.c_str());
2504 LogPrintf("%s: next leg fee currency %s unavailable for conversion using %s\n", __func__, curNextDest.name.c_str(), importCurrencyDef.name.c_str());
2507 // now, convert next leg fees, which are currently in the fee currency, to the next destination system ID,
2508 // adjust curTransfer values to reflect the new state, and continue
2509 // while we won't change the fee currency ID in the curTransfer, all pass through fees are assumed to be in
2510 // the next leg's system currency by the time it is ready to produce an output
2512 CAmount oneFeeValue = 0;
2513 int feeCurIdx = currencyIndexMap[curTransfer.feeCurrencyID];
2514 int nextDestIdx = currencyIndexMap[nextDestSysID];
2516 // all pass-through conversions pay a reserve-to-reserve fee for the conversion
2517 CAmount passThroughFee = CalculateConversionFeeNoMin(curTransfer.destination.fees) << 1;
2518 curTransfer.destination.fees -= passThroughFee;
2520 AddReserveConversionFees(curTransfer.feeCurrencyID, passThroughFee);
2522 transferFees.valueMap[curTransfer.feeCurrencyID] += passThroughFee;
2524 reserveConverted.valueMap[curTransfer.feeCurrencyID] += curTransfer.destination.fees;
2525 crossConversions[feeCurIdx][nextDestIdx] += curTransfer.destination.fees;
2526 oneFeeValue = importCurrencyState.ReserveToNativeRaw(curTransfer.destination.fees,
2527 importCurrencyState.IsLaunchCompleteMarker() ?
2528 importCurrencyState.conversionPrice[feeCurIdx] :
2529 importCurrencyState.viaConversionPrice[feeCurIdx]);
2531 // one more conversion to destination native
2532 CAmount reserveFromFrac = CCurrencyState::NativeToReserveRaw(oneFeeValue, importCurrencyState.viaConversionPrice[nextDestIdx]);
2533 curTransfer.destination.fees = reserveFromFrac;
2534 newConvertedReservePool.valueMap[nextDestSysID] += reserveFromFrac;
2536 AddReserveOutput(nextDestSysID, reserveFromFrac);
2537 AddReserveOutConverted(nextDestSysID, reserveFromFrac);
2540 if (curTransfer.feeCurrencyID == systemDestID)
2542 nativeOut = curTransfer.destination.fees;
2546 AddReserveOutput(nextDestSysID, curTransfer.destination.fees);
2551 if (curTransfer.feeCurrencyID == systemDestID)
2553 nativeIn += explicitFees;
2557 // if the input will go into our currency as reserves, we only record it once on export/pre-launch
2558 AddReserveInput(curTransfer.feeCurrencyID, explicitFees);
2561 // if it's from a gateway, we need to be sure that the currency it is importing is valid for the current chain
2562 // all pre-conversions
2563 if (isCrossSystemImport || systemSourceID != importCurrencyDef.systemID)
2565 uint160 inputID = curTransfer.FirstCurrency();
2566 CAmount totalCurrencyInput = curTransfer.FirstValue();
2568 // if this currency is under control of the gateway, it is minted on the way in, otherwise, it will be
2569 // on the gateway's reserve deposits, which can be spent by imports from the gateway's converter
2571 // source system currency is imported, dest system must come from deposits
2572 if (curTransfer.feeCurrencyID == systemSourceID)
2574 // if it's not a reserve of this currency, we can't process this transfer's fee
2575 if (!((isFractional &&
2576 currencyIndexMap.count(systemSourceID)) ||
2577 (systemSourceID == importCurrencyDef.launchSystemID)))
2579 printf("%s: currency transfer fees invalid for receiving system\n", __func__);
2580 LogPrintf("%s: currency transfer fees invalid for receiving system\n", __func__);
2583 if (importCurrencyState.IsRefunding())
2585 gatewayDepositsIn.valueMap[systemSourceID] += explicitFees;
2589 importedCurrency.valueMap[systemSourceID] += explicitFees;
2592 else if (curTransfer.feeCurrencyID == systemDestID)
2594 gatewayDepositsIn.valueMap[systemDestID] += explicitFees;
2596 else if (!(curTransfer.feeCurrencyID == curTransfer.FirstCurrency() &&
2598 currencyIndexMap.count(curTransfer.feeCurrencyID) &&
2599 importCurrencyState.IsLaunchConfirmed()))
2601 printf("%s: pass-through fees invalid\n", __func__);
2602 LogPrintf("%s: pass-through fees invalid\n", __func__);
2606 CCurrencyDefinition inputDef = ConnectedChains.GetCachedCurrency(inputID);
2607 if (!inputDef.IsValid())
2609 printf("%s: Invalid or unregistered currency for import from %s\n", __func__, curTransfer.ToUniValue().write().c_str());
2612 if (curTransfer.IsMint())
2614 printf("%s: Invalid mint operation from %s\n", __func__, curTransfer.ToUniValue().write().c_str());
2618 if (totalCurrencyInput)
2620 // all currency input must either come from being minted on the import or existing gateway deposits
2621 if (!(importCurrencyState.IsRefunding() && importCurrencyDef.launchSystemID == systemDestID) &&
2622 (inputDef.systemID == systemSourceID ||
2623 (inputDef.IsGateway() && inputDef.gatewayID == systemSourceID)))
2625 importedCurrency.valueMap[inputID] += totalCurrencyInput;
2629 gatewayDepositsIn.valueMap[inputID] += totalCurrencyInput;
2632 if (inputID == systemDestID)
2634 nativeIn += totalCurrencyInput;
2638 AddReserveInput(inputID, totalCurrencyInput);
2644 // now, fees are either in the destination native currency, or this is a fractional currency, and
2645 // we convert to see if we meet fee minimums
2646 CAmount feeEquivalent;
2647 uint160 feeCurrency;
2648 if (curTransfer.IsConversion() && !curTransfer.IsPreConversion())
2650 feeCurrency = curTransfer.nFees ? curTransfer.feeCurrencyID : curTransfer.FirstCurrency();
2651 feeEquivalent = CReserveTransactionDescriptor::CalculateConversionFee(curTransfer.FirstValue()) + curTransfer.nFees;
2655 feeCurrency = curTransfer.feeCurrencyID;
2656 feeEquivalent = curTransfer.nFees;
2659 if (feeCurrency != systemDestID)
2661 if (!importCurrencyDef.IsFractional() || !(currencyIndexMap.count(feeCurrency) || feeCurrency == importCurrencyID))
2663 printf("%s: Invalid fee currency for transfer %s\n", __func__, curTransfer.ToUniValue().write().c_str());
2664 LogPrintf("%s: Invalid fee currency for transfer %s\n", __func__, curTransfer.ToUniValue().write().c_str());
2667 if (curTransfer.feeCurrencyID != importCurrencyID)
2669 feeEquivalent = importCurrencyState.ReserveToNativeRaw(feeEquivalent, importCurrencyState.conversionPrice[currencyIndexMap[feeCurrency]]);
2671 feeEquivalent = importCurrencyState.NativeToReserveRaw(feeEquivalent, importCurrencyState.viaConversionPrice[systemDestIdx]);
2674 if (feeEquivalent < curTransfer.CalculateTransferFee())
2676 printf("%s: Incorrect fee sent with export %s\n", __func__, curTransfer.ToUniValue().write().c_str());
2677 LogPrintf("%s: Incorrect fee sent with export %s\n", __func__, curTransfer.ToUniValue().write().c_str());
2681 if (curTransfer.FirstCurrency() == systemDestID && !curTransfer.IsMint())
2683 nativeIn += curTransfer.FirstValue();
2687 if (curTransfer.IsMint())
2689 AddReserveInput(curTransfer.destCurrencyID, curTransfer.FirstValue());
2693 AddReserveInput(curTransfer.FirstCurrency(), curTransfer.FirstValue());
2699 if (curTransfer.IsPreConversion())
2701 // pre-conversions can only come from our launch system
2702 if (importCurrencyDef.launchSystemID != systemSourceID)
2704 printf("%s: Invalid source system for preconversion %s\n", __func__, curTransfer.ToUniValue().write().c_str());
2705 LogPrintf("%s: Invalid source system for preconversion %s\n", __func__, curTransfer.ToUniValue().write().c_str());
2709 if (importCurrencyState.IsLaunchCompleteMarker())
2711 printf("%s: Invalid preconversion after launch %s\n", __func__, curTransfer.ToUniValue().write().c_str());
2712 LogPrintf("%s: Invalid preconversion after launch %s\n", __func__, curTransfer.ToUniValue().write().c_str());
2716 uint160 convertFromID = curTransfer.FirstCurrency();
2718 // either the destination currency must be fractional or the source currency
2720 if (!isFractional && convertFromID != importCurrencyDef.launchSystemID)
2722 printf("%s: Invalid conversion %s. Source must be launch system native or destinaton must be fractional.\n", __func__, curTransfer.ToUniValue().write().c_str());
2723 LogPrintf("%s: Invalid conversion %s. Source must be launch system native or destinaton must be fractional\n", __func__, curTransfer.ToUniValue().write().c_str());
2727 // get currency index
2728 auto curIndexIt = currencyIndexMap.find(convertFromID);
2729 if (curIndexIt == currencyIndexMap.end())
2731 printf("%s: Invalid currency for conversion %s\n", __func__, curTransfer.ToUniValue().write().c_str());
2732 LogPrintf("%s: Invalid currency for conversion %s\n", __func__, curTransfer.ToUniValue().write().c_str());
2735 int curIdx = curIndexIt->second;
2737 // output the converted amount, minus fees, and generate a normal output that spends the net input of the import as native
2738 // difference between all potential value out and what was taken unconverted as a fee in our fee output
2739 CAmount preConversionFee = 0;
2740 CAmount newCurrencyConverted = 0;
2741 CAmount valueOut = curTransfer.FirstValue();
2743 preConversionFee = CalculateConversionFee(curTransfer.FirstValue());
2744 if (preConversionFee > curTransfer.FirstValue())
2746 preConversionFee = curTransfer.FirstValue();
2749 valueOut -= preConversionFee;
2751 AddReserveConversionFees(curTransfer.FirstCurrency(), preConversionFee);
2752 transferFees.valueMap[curTransfer.FirstCurrency()] += preConversionFee;
2754 newCurrencyConverted = importCurrencyState.ReserveToNativeRaw(valueOut, importCurrencyState.conversionPrice[curIdx]);
2756 if (newCurrencyConverted == -1)
2758 // if we have an overflow, this isn't going to work
2759 newCurrencyConverted = 0;
2762 if (newCurrencyConverted)
2764 uint160 firstCurID = curTransfer.FirstCurrency();
2765 reserveConverted.valueMap[firstCurID] += valueOut;
2766 preConvertedReserves.valueMap[firstCurID] += valueOut;
2767 if (isFractional && isCrossSystemImport && importedCurrency.valueMap.count(firstCurID))
2769 // TODO: look into 100% rollup of launch fees and resolution at launch.
2770 // Right now, only fees are imported after the first coinbase
2771 // reserves in the currency are already on chain as of block 1 and fees come in
2772 // and get converted with imports
2773 importedCurrency.valueMap[firstCurID] -= valueOut;
2776 if (totalCarveOut > 0 && totalCarveOut < SATOSHIDEN)
2778 CAmount newReserveIn = CCurrencyState::NativeToReserveRaw(valueOut, SATOSHIDEN - totalCarveOut);
2779 totalCarveOuts.valueMap[curTransfer.FirstCurrency()] += valueOut - newReserveIn;
2780 valueOut = newReserveIn;
2783 if (curTransfer.FirstCurrency() != systemDestID)
2785 // if this is a fractional currency, everything but fees and carveouts stay in reserve deposit
2786 // else all that would be reserves is sent to chain ID
2789 AddReserveOutput(curTransfer.FirstCurrency(), valueOut);
2790 std::vector<CTxDestination> dests({CIdentityID(importCurrencyID)});
2791 CTokenOutput ro = CTokenOutput(curTransfer.FirstCurrency(), valueOut);
2792 vOutputs.push_back(CTxOut(0, MakeMofNCCScript(CConditionObj<CTokenOutput>(EVAL_RESERVE_OUTPUT, dests, 1, &ro))));
2797 // if it is not fractional, send proceeds to currency ID, else leave it in reserve deposit
2800 nativeOut += valueOut;
2801 vOutputs.push_back(CTxOut(valueOut, GetScriptForDestination(CIdentityID(importCurrencyID))));
2805 preConvertedOutput.valueMap[curTransfer.FirstCurrency()] += newCurrencyConverted;
2806 AddNativeOutConverted(curTransfer.FirstCurrency(), newCurrencyConverted);
2807 AddNativeOutConverted(curTransfer.destCurrencyID, newCurrencyConverted);
2808 if (curTransfer.destCurrencyID == systemDestID)
2810 nativeOut += newCurrencyConverted;
2811 if (!importCurrencyState.IsLaunchConfirmed())
2813 nativeIn += newCurrencyConverted;
2815 curTransfer.GetTxOut(CCurrencyValueMap(), newCurrencyConverted, newOut);
2817 else // all conversions are to primary currency
2819 AddReserveOutConverted(curTransfer.destCurrencyID, newCurrencyConverted);
2820 AddReserveOutput(curTransfer.destCurrencyID, newCurrencyConverted);
2821 if (!importCurrencyState.IsLaunchConfirmed())
2823 AddReserveInput(curTransfer.destCurrencyID, newCurrencyConverted);
2825 curTransfer.GetTxOut(CCurrencyValueMap(std::vector<uint160>({curTransfer.destCurrencyID}), std::vector<int64_t>({newCurrencyConverted})),
2830 else if (curTransfer.IsConversion())
2832 if (curTransfer.FirstCurrency() == curTransfer.destCurrencyID)
2834 printf("%s: Conversion does not specify two currencies\n", __func__);
2835 LogPrintf("%s: Conversion does not specify two currencies\n", __func__);
2839 // either the source or destination must be a reserve currency of the other fractional currency
2840 // if destination is a fractional currency of a reserve, we will mint currency
2841 // if not, we will burn currency
2842 bool toFractional = importCurrencyID == curTransfer.destCurrencyID &&
2843 currencyDest.IsFractional() &&
2844 currencyIndexMap.count(curTransfer.FirstCurrency());
2846 CCurrencyDefinition sourceCurrency = ConnectedChains.GetCachedCurrency(curTransfer.FirstCurrency());
2848 if (!sourceCurrency.IsValid())
2850 printf("%s: Currency specified for conversion not found\n", __func__);
2851 LogPrintf("%s: Currency specified for conversion not found\n", __func__);
2855 if (!(toFractional ||
2856 (importCurrencyID == curTransfer.FirstCurrency() &&
2857 sourceCurrency.IsFractional() &&
2858 currencyIndexMap.count(curTransfer.destCurrencyID))))
2860 printf("%s: Conversion must be between a fractional currency and one of its reserves\n", __func__);
2861 LogPrintf("%s: Conversion must be between a fractional currency and one of its reserves\n", __func__);
2865 if (curTransfer.IsReserveToReserve() &&
2867 curTransfer.secondReserveID.IsNull() ||
2868 curTransfer.secondReserveID == curTransfer.FirstCurrency() ||
2869 !currencyIndexMap.count(curTransfer.secondReserveID)))
2871 printf("%s: Invalid reserve to reserve transaction %s\n", __func__, curTransfer.ToUniValue().write().c_str());
2872 LogPrintf("%s: Invalid reserve to reserve transaction %s\n", __func__, curTransfer.ToUniValue().write().c_str());
2876 const CCurrencyDefinition &fractionalCurrency = toFractional ? currencyDest : sourceCurrency;
2877 const CCurrencyDefinition &reserveCurrency = toFractional ? sourceCurrency : currencyDest;
2878 int reserveIdx = currencyIndexMap[reserveCurrency.GetID()];
2880 assert(fractionalCurrency.IsValid() &&
2881 reserveCurrency.IsValid() &&
2882 fractionalCurrency.currencies[reserveIdx] == reserveCurrency.GetID());
2884 // now, we know that we are converting from the source currency to the
2885 // destination currency and also that one of them is a reserve of the other
2886 // we convert using the provided currency state, and we update the currency
2887 // state to include newly minted or burned currencies.
2888 CAmount valueOut = curTransfer.FirstValue();
2889 CAmount oneConversionFee = 0;
2890 CAmount newCurrencyConverted = 0;
2892 if (!(curTransfer.flags & curTransfer.FEE_OUTPUT))
2894 oneConversionFee = CalculateConversionFee(curTransfer.FirstValue());
2895 if (curTransfer.IsReserveToReserve())
2897 oneConversionFee <<= 1;
2899 if (oneConversionFee > curTransfer.FirstValue())
2901 oneConversionFee = curTransfer.FirstValue();
2903 valueOut -= oneConversionFee;
2904 AddReserveConversionFees(curTransfer.FirstCurrency(), oneConversionFee);
2905 transferFees.valueMap[curTransfer.FirstCurrency()] += oneConversionFee;
2910 reserveConverted.valueMap[curTransfer.FirstCurrency()] += valueOut;
2911 newCurrencyConverted = importCurrencyState.ReserveToNativeRaw(valueOut, importCurrencyState.conversionPrice[reserveIdx]);
2915 fractionalConverted.valueMap[curTransfer.destCurrencyID] += valueOut;
2916 newCurrencyConverted = importCurrencyState.NativeToReserveRaw(valueOut, importCurrencyState.conversionPrice[reserveIdx]);
2919 if (newCurrencyConverted)
2921 uint160 outputCurrencyID;
2923 if (curTransfer.IsReserveToReserve())
2925 // we need to convert once more from fractional to a reserve currency
2926 // we burn 0.025% of the fractional that was converted, and convert the rest to
2927 // the specified reserve. since the burn depends on the first conversion, which
2928 // it is not involved in, it is tracked separately and applied after the first conversion
2929 outputCurrencyID = curTransfer.secondReserveID;
2930 int32_t outputCurrencyIdx = currencyIndexMap[outputCurrencyID];
2931 newCurrencyConverted = CCurrencyState::NativeToReserveRaw(newCurrencyConverted, importCurrencyState.viaConversionPrice[outputCurrencyIdx]);
2932 crossConversions[reserveIdx][outputCurrencyIdx] += valueOut;
2936 outputCurrencyID = curTransfer.destCurrencyID;
2939 if (toFractional && !curTransfer.IsReserveToReserve())
2941 AddNativeOutConverted(curTransfer.FirstCurrency(), newCurrencyConverted);
2942 AddNativeOutConverted(curTransfer.destCurrencyID, newCurrencyConverted);
2943 if (curTransfer.destCurrencyID == systemDestID)
2945 nativeOut += newCurrencyConverted;
2949 AddReserveOutConverted(curTransfer.destCurrencyID, newCurrencyConverted);
2950 AddReserveOutput(curTransfer.destCurrencyID, newCurrencyConverted);
2955 AddReserveOutConverted(outputCurrencyID, newCurrencyConverted);
2956 newConvertedReservePool.valueMap[outputCurrencyID] += newCurrencyConverted;
2957 if (outputCurrencyID == systemDestID)
2959 nativeOut += newCurrencyConverted;
2963 AddReserveOutput(outputCurrencyID, newCurrencyConverted);
2966 // if this originated as input fractional, burn the input currency
2967 // if it was reserve to reserve, it was never added, and it's fee
2968 // value is left behind in the currency
2969 if (!toFractional && !curTransfer.IsReserveToReserve())
2971 AddNativeOutConverted(importCurrencyID, -valueOut);
2975 if (outputCurrencyID == systemDestID)
2977 curTransfer.GetTxOut(CCurrencyValueMap(), newCurrencyConverted, newOut);
2981 curTransfer.GetTxOut(CCurrencyValueMap(std::vector<uint160>({outputCurrencyID}), std::vector<int64_t>({newCurrencyConverted})),
2988 // if we are supposed to burn a currency, it must be the import currency, and it
2989 // is removed from the supply, which would change all calculations for price
2990 if (curTransfer.IsBurn())
2992 // if the source is fractional currency, it is burned
2993 if (curTransfer.FirstCurrency() != importCurrencyID || !(isFractional || importCurrencyDef.IsToken()))
2995 CCurrencyDefinition sourceCurrency = ConnectedChains.GetCachedCurrency(curTransfer.FirstCurrency());
2996 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());
2997 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());
3000 if (curTransfer.flags & curTransfer.IsBurnChangeWeight())
3002 printf("%s: burning %s to change weight is not supported\n", __func__, importCurrencyDef.name.c_str());
3003 LogPrintf("%s: burning %s to change weight is not supported\n", __func__, importCurrencyDef.name.c_str());
3006 // burn the input fractional currency
3007 AddNativeOutConverted(curTransfer.FirstCurrency(), -curTransfer.FirstValue());
3008 burnedChangePrice += curTransfer.FirstValue();
3010 else if (!curTransfer.IsMint() && systemDestID == curTransfer.FirstCurrency())
3012 nativeOut += curTransfer.FirstValue();
3013 if (!curTransfer.GetTxOut(CCurrencyValueMap(), curTransfer.FirstValue(), newOut) || newOut.nValue == -1)
3015 printf("%s: invalid transfer %s\n", __func__, curTransfer.ToUniValue().write(1,2).c_str());
3016 LogPrintf("%s: invalid transfer %s\n", __func__, curTransfer.ToUniValue().write().c_str());
3022 // if this is a minting of currency
3023 // this is used for both pre-allocation and also centrally, algorithmically, or externally controlled currencies
3025 if (curTransfer.IsMint() && curTransfer.destCurrencyID == importCurrencyID)
3027 // minting is emitted in new currency state
3028 destCurID = curTransfer.destCurrencyID;
3029 totalMinted += curTransfer.FirstValue();
3030 AddNativeOutConverted(curTransfer.destCurrencyID, curTransfer.FirstValue());
3031 if (curTransfer.destCurrencyID != systemDestID)
3033 AddReserveOutConverted(curTransfer.destCurrencyID, curTransfer.FirstValue());
3038 destCurID = curTransfer.FirstCurrency();
3040 AddReserveOutput(destCurID, curTransfer.FirstValue());
3041 curTransfer.GetTxOut(CCurrencyValueMap(std::vector<uint160>({destCurID}), std::vector<int64_t>({curTransfer.FirstValue()})),
3045 if (newOut.nValue < 0)
3047 // if we get here, we have absorbed the entire transfer
3048 LogPrintf("%s: skip creating output for import to %s\n", __func__, currencyDest.name.c_str());
3052 vOutputs.push_back(newOut);
3057 printf("%s: Invalid reserve transfer on export\n", __func__);
3058 LogPrintf("%s: Invalid reserve transfer on export\n", __func__);
3063 if (importCurrencyState.IsRefunding())
3065 importedCurrency = CCurrencyValueMap();
3067 else if ((totalCarveOuts = totalCarveOuts.CanonicalMap()).valueMap.size())
3069 // add carveout outputs
3070 for (auto &oneCur : totalCarveOuts.valueMap)
3072 // if we are creating a reserve import for native currency, it must be spent from native inputs on the destination system
3073 if (oneCur.first == systemDestID)
3075 nativeOut += oneCur.second;
3076 vOutputs.push_back(CTxOut(oneCur.second, GetScriptForDestination(CIdentityID(importCurrencyID))));
3080 // generate a reserve output of the amount indicated, less fees
3081 // we will send using a reserve output, fee will be paid through coinbase by converting from reserve or not, depending on currency settings
3082 std::vector<CTxDestination> dests = std::vector<CTxDestination>({CIdentityID(importCurrencyID)});
3083 CTokenOutput ro = CTokenOutput(oneCur.first, oneCur.second);
3084 AddReserveOutput(oneCur.first, oneCur.second);
3085 vOutputs.push_back(CTxOut(0, MakeMofNCCScript(CConditionObj<CTokenOutput>(EVAL_RESERVE_OUTPUT, dests, 1, &ro))));
3090 // input of primary currency is sources in and output is sinks
3091 CAmount netPrimaryIn = 0;
3092 CAmount netPrimaryOut = 0;
3093 spentCurrencyOut.valueMap.clear();
3094 CCurrencyValueMap ReserveInputs;
3096 // remove burned currency from supply
3098 // check to see if liquidity fees include currency to burn and burn if so
3099 if (liquidityFees.valueMap.count(importCurrencyID))
3101 CAmount primaryLiquidityFees = liquidityFees.valueMap[importCurrencyID];
3102 newCurrencyState.primaryCurrencyOut -= primaryLiquidityFees;
3103 liquidityFees.valueMap.erase(importCurrencyID);
3105 if (burnedChangePrice > 0)
3107 if (!(burnedChangePrice <= newCurrencyState.supply))
3109 printf("%s: Invalid burn amount %ld\n", __func__, burnedChangePrice);
3110 LogPrintf("%s: Invalid burn amount %ld\n", __func__, burnedChangePrice);
3113 newCurrencyState.supply -= burnedChangePrice;
3116 CCurrencyValueMap adjustedReserveConverted = reserveConverted - preConvertedReserves;
3118 if (isFractional && newCurrencyState.IsLaunchConfirmed())
3120 CCoinbaseCurrencyState scratchCurrencyState = importCurrencyState;
3122 if (scratchCurrencyState.IsPrelaunch() && preConvertedReserves > CCurrencyValueMap())
3124 // add all pre-converted reserves before calculating pricing for fee conversions
3125 for (auto &oneReserve : preConvertedReserves.valueMap)
3127 if (oneReserve.second)
3129 scratchCurrencyState.reserves[currencyIndexMap[oneReserve.first]] += oneReserve.second;
3134 if (importCurrencyState.IsLaunchClear() && importCurrencyState.IsLaunchConfirmed())
3138 scratchCurrencyState.ApplyCarveouts(totalCarveOut);
3140 if (importCurrencyDef.preLaunchDiscount)
3142 scratchCurrencyState.ApplyCarveouts(importCurrencyDef.preLaunchDiscount);
3146 if (adjustedReserveConverted.CanonicalMap().valueMap.size() || fractionalConverted.CanonicalMap().valueMap.size())
3148 CCurrencyState dummyCurState;
3149 std::vector<int64_t> newPrices =
3150 scratchCurrencyState.ConvertAmounts(adjustedReserveConverted.AsCurrencyVector(importCurrencyState.currencies),
3151 fractionalConverted.AsCurrencyVector(importCurrencyState.currencies),
3154 &newCurrencyState.viaConversionPrice);
3155 if (!dummyCurState.IsValid())
3157 printf("%s: Invalid currency conversions for import to %s : %s\n", __func__, importCurrencyDef.name.c_str(), EncodeDestination(CIdentityID(importCurrencyDef.GetID())).c_str());
3158 LogPrintf("%s: Invalid currency conversions for import to %s : %s\n", __func__, importCurrencyDef.name.c_str(), EncodeDestination(CIdentityID(importCurrencyDef.GetID())).c_str());
3161 if (!newCurrencyState.IsLaunchCompleteMarker())
3163 // make viaconversion prices the dynamic prices and conversion prices remain initial pricing
3164 for (int i = 0; i < newPrices.size(); i++)
3166 if (i != systemDestIdx)
3168 newCurrencyState.viaConversionPrice[i] = newPrices[i];
3174 newCurrencyState.conversionPrice = newPrices;
3179 if (newCurrencyState.IsPrelaunch())
3181 adjustedReserveConverted = reserveConverted;
3184 newCurrencyState.preConvertedOut = 0;
3185 if (!newCurrencyState.IsRefunding())
3187 for (auto &oneVal : preConvertedOutput.valueMap)
3189 newCurrencyState.preConvertedOut += oneVal.second;
3190 if (newCurrencyState.IsLaunchConfirmed() && !isFractional)
3192 newCurrencyState.supply += oneVal.second;
3197 std::vector<CAmount> vResConverted;
3198 std::vector<CAmount> vResOutConverted;
3199 std::vector<CAmount> vFracConverted;
3200 std::vector<CAmount> vFracOutConverted;
3202 CCurrencyValueMap reserveBalanceInMap;
3204 // liquidity fees that are in the import currency are burned above
3205 std::vector<CAmount> vLiquidityFees = liquidityFees.AsCurrencyVector(newCurrencyState.currencies);
3207 if (newCurrencyState.IsLaunchConfirmed())
3209 vResConverted = adjustedReserveConverted.AsCurrencyVector(newCurrencyState.currencies);
3210 vResOutConverted = (ReserveOutConvertedMap(importCurrencyID) + totalCarveOuts).AsCurrencyVector(newCurrencyState.currencies);
3211 vFracConverted = fractionalConverted.AsCurrencyVector(newCurrencyState.currencies);
3212 vFracOutConverted = (NativeOutConvertedMap() - preConvertedOutput).AsCurrencyVector(newCurrencyState.currencies);
3213 CAmount totalNewFrac = 0;
3214 for (int i = 0; i < newCurrencyState.currencies.size(); i++)
3216 newCurrencyState.reserveIn[i] = vResConverted[i] + vLiquidityFees[i];
3217 newCurrencyState.reserveOut[i] = vResOutConverted[i];
3218 CAmount newReservesIn = isFractional ? (vResConverted[i] - vResOutConverted[i]) + vLiquidityFees[i] : 0;
3219 newCurrencyState.reserves[i] += newReservesIn;
3222 reserveBalanceInMap.valueMap[newCurrencyState.currencies[i]] = newReservesIn;
3224 netPrimaryIn += (newCurrencyState.primaryCurrencyIn[i] = vFracConverted[i]);
3225 netPrimaryOut += vFracOutConverted[i];
3226 totalNewFrac += vFracOutConverted[i];
3229 newCurrencyState.supply += (netPrimaryOut - netPrimaryIn);
3230 netPrimaryIn += totalNewFrac;
3234 vResConverted = adjustedReserveConverted.AsCurrencyVector(newCurrencyState.currencies);
3235 vResOutConverted = ReserveOutConvertedMap(importCurrencyID).AsCurrencyVector(newCurrencyState.currencies);
3236 vFracConverted = fractionalConverted.AsCurrencyVector(newCurrencyState.currencies);
3237 vFracOutConverted = preConvertedOutput.AsCurrencyVector(newCurrencyState.currencies);
3238 for (int i = 0; i < newCurrencyState.currencies.size(); i++)
3240 newCurrencyState.reserveIn[i] = vResConverted[i] + vLiquidityFees[i];
3243 newCurrencyState.reserves[i] += (vResConverted[i] - vResOutConverted[i]) + vLiquidityFees[i];
3247 CAmount newPrimaryOut = vFracOutConverted[i] - vFracConverted[i];
3248 newCurrencyState.supply += newPrimaryOut;
3249 netPrimaryIn += newPrimaryOut;
3250 netPrimaryOut += newPrimaryOut;
3255 // launch clear or not confirmed, we have straight prices, fees get formula based conversion, but
3256 // price is not recorded in state so that initial currency always has initial prices
3257 if (!newCurrencyState.IsLaunchCompleteMarker())
3261 if (newCurrencyState.IsLaunchConfirmed())
3263 // calculate launch prices and ensure that conversion prices remain constant until
3264 // launch is complete
3265 if (newCurrencyState.IsLaunchClear() && newCurrencyState.IsPrelaunch())
3267 CCoinbaseCurrencyState tempCurrencyState = importCurrencyState;
3269 if (preConvertedReserves > CCurrencyValueMap())
3271 tempCurrencyState.reserves =
3273 tempCurrencyState.currencies, tempCurrencyState.reserves) + preConvertedReserves).AsCurrencyVector(tempCurrencyState.currencies);
3276 /* printf("%s: importCurrencyState:\n%s\nnewCurrencyState:\n%s\nrevertedState:\n%s\n",
3278 importCurrencyState.ToUniValue().write(1,2).c_str(),
3279 newCurrencyState.ToUniValue().write(1,2).c_str(),
3280 tempCurrencyState.ToUniValue().write(1,2).c_str());
3281 printf("%s: liquidityfees:\n%s\n", __func__, liquidityFees.ToUniValue().write(1,2).c_str());
3282 printf("%s: preConvertedReserves:\n%s\n", __func__, preConvertedReserves.ToUniValue().write(1,2).c_str()); */
3284 tempCurrencyState.supply = importCurrencyDef.initialFractionalSupply;
3286 if (importCurrencyDef.launchSystemID == importCurrencyDef.systemID)
3288 newCurrencyState.conversionPrice = tempCurrencyState.PricesInReserve(true);
3292 CAmount systemDestPrice = tempCurrencyState.PriceInReserve(systemDestIdx);
3293 tempCurrencyState.currencies.erase(tempCurrencyState.currencies.begin() + systemDestIdx);
3294 tempCurrencyState.reserves.erase(tempCurrencyState.reserves.begin() + systemDestIdx);
3295 int32_t sysWeight = tempCurrencyState.weights[systemDestIdx];
3296 tempCurrencyState.weights.erase(tempCurrencyState.weights.begin() + systemDestIdx);
3297 int32_t oneExtraWeight = sysWeight / tempCurrencyState.weights.size();
3298 int32_t weightRemainder = sysWeight % tempCurrencyState.weights.size();
3299 for (auto &oneWeight : tempCurrencyState.weights)
3301 oneWeight += oneExtraWeight;
3302 if (weightRemainder)
3308 std::vector<CAmount> launchPrices = tempCurrencyState.PricesInReserve(true);
3309 launchPrices.insert(launchPrices.begin() + systemDestIdx, systemDestPrice);
3310 newCurrencyState.conversionPrice = launchPrices;
3315 newCurrencyState.conversionPrice = importCurrencyState.conversionPrice;
3318 else if (importCurrencyState.IsPrelaunch() && !importCurrencyState.IsRefunding())
3320 newCurrencyState.viaConversionPrice = newCurrencyState.PricesInReserve(true);
3321 CCoinbaseCurrencyState tempCurrencyState = newCurrencyState;
3322 // via prices are used for fees on launch clear and include the converter issued currency
3323 // normal prices on launch clear for a gateway or PBaaS converter do not include the new native
3324 // currency until after pre-conversions are processed
3325 if (importCurrencyDef.launchSystemID == importCurrencyDef.systemID)
3327 newCurrencyState.conversionPrice = tempCurrencyState.PricesInReserve(true);
3331 tempCurrencyState.currencies.erase(tempCurrencyState.currencies.begin() + systemDestIdx);
3332 tempCurrencyState.reserves.erase(tempCurrencyState.reserves.begin() + systemDestIdx);
3333 int32_t sysWeight = tempCurrencyState.weights[systemDestIdx];
3334 tempCurrencyState.weights.erase(tempCurrencyState.weights.begin() + systemDestIdx);
3335 int32_t oneExtraWeight = sysWeight / tempCurrencyState.weights.size();
3336 int32_t weightRemainder = sysWeight % tempCurrencyState.weights.size();
3337 for (auto &oneWeight : tempCurrencyState.weights)
3339 oneWeight += oneExtraWeight;
3340 if (weightRemainder)
3346 std::vector<CAmount> launchPrices = tempCurrencyState.PricesInReserve(true);
3347 launchPrices.insert(launchPrices.begin() + systemDestIdx, newCurrencyState.viaConversionPrice[systemDestIdx]);
3348 newCurrencyState.conversionPrice = launchPrices;
3354 // if this is a PBaaS launch, mint all required preconversion along with preallocation
3355 CAmount extraPreconverted = 0;
3356 if (importCurrencyDef.IsPBaaSChain() && newCurrencyState.IsLaunchClear() && newCurrencyState.IsLaunchConfirmed())
3358 if (importCurrencyState.IsPrelaunch())
3360 extraPreconverted = newCurrencyState.preConvertedOut;
3361 // if this is our launch currency issue any necessary pre-converted supply and add it to reserve deposits
3362 if (importCurrencyID == ASSETCHAINS_CHAINID &&
3363 importCurrencyDef.IsPBaaSChain() &&
3364 importCurrencyState.reserveIn.size() == 1 &&
3365 importCurrencyState.reserveIn[0])
3367 // add new native currency to reserve deposits for imports
3368 // total converted in this import should be added to the total from before
3369 CAmount oldReservesIn = newCurrencyState.reserveIn[0] - importCurrencyState.reserveIn[0];
3370 extraPreconverted += newCurrencyState.ReserveToNativeRaw(oldReservesIn, newCurrencyState.conversionPrice[0]);
3372 newCurrencyState.preConvertedOut = extraPreconverted;
3376 extraPreconverted = importCurrencyState.preConvertedOut - newCurrencyState.preConvertedOut;
3380 if (newCurrencyState.IsRefunding())
3384 extraPreconverted = 0;
3387 if (isFractional && importCurrencyState.IsLaunchClear() && importCurrencyState.IsLaunchConfirmed())
3389 //printf("currency state: %s\n", newCurrencyState.ToUniValue().write(1,2).c_str());
3392 //printf("%s: applying carveout %d to weights\n", __func__, totalCarveOut);
3393 newCurrencyState.ApplyCarveouts(totalCarveOut);
3395 if (importCurrencyDef.preLaunchDiscount)
3397 //printf("%s: applying prelaunch discount %d to weights\n", __func__, importCurrencyDef.preLaunchDiscount);
3398 newCurrencyState.ApplyCarveouts(importCurrencyDef.preLaunchDiscount);
3400 //printf("new currency state: %s\n", newCurrencyState.ToUniValue().write(1,2).c_str());
3403 if (totalMinted || preAllocTotal)
3405 newCurrencyState.UpdateWithEmission(totalMinted + preAllocTotal);
3406 netPrimaryOut += (totalMinted + preAllocTotal);
3407 netPrimaryIn += (totalMinted + preAllocTotal);
3410 if (newCurrencyState.IsLaunchConfirmed())
3412 netPrimaryOut += newCurrencyState.preConvertedOut;
3413 netPrimaryIn += newCurrencyState.preConvertedOut + extraPreconverted;
3416 if (extraPreconverted)
3418 gatewayDepositsIn.valueMap[importCurrencyID] += extraPreconverted;
3421 // double check that the export fee taken as the fee output matches the export fee that should have been taken
3422 CAmount systemOutConverted = 0;
3424 //printf("%s currencies: %s\n", __func__, ToUniValue().write(1,2).c_str());
3428 ReserveInputs.valueMap[importCurrencyID] += netPrimaryIn;
3433 spentCurrencyOut.valueMap[importCurrencyID] += netPrimaryOut;
3436 newCurrencyState.primaryCurrencyOut = netPrimaryOut;
3437 if (importCurrencyDef.IsPBaaSChain() && importCurrencyState.IsLaunchConfirmed() && !importCurrencyState.IsLaunchClear())
3439 // pre-conversions should already be on this chain as gateway deposits on behalf of the
3441 newCurrencyState.primaryCurrencyOut -= newCurrencyState.preConvertedOut;
3442 gatewayDepositsIn.valueMap[importCurrencyID] += newCurrencyState.preConvertedOut;
3443 importedCurrency = (importedCurrency - preConvertedReserves).CanonicalMap();
3444 gatewayDepositsIn += preConvertedReserves;
3447 for (auto &oneInOut : currencies)
3449 if (oneInOut.first == importCurrencyID)
3451 if (oneInOut.first == systemDestID)
3453 systemOutConverted += oneInOut.second.nativeOutConverted;
3458 if (oneInOut.first == systemDestID)
3460 systemOutConverted += oneInOut.second.reserveOutConverted;
3462 if (oneInOut.second.reserveIn)
3464 ReserveInputs.valueMap[oneInOut.first] += oneInOut.second.reserveIn;
3466 if (liquidityFees.valueMap.count(oneInOut.first))
3468 ReserveInputs.valueMap[oneInOut.first] += liquidityFees.valueMap[oneInOut.first];
3470 if (oneInOut.first != systemDestID)
3472 if (oneInOut.second.reserveOut)
3474 spentCurrencyOut.valueMap[oneInOut.first] += oneInOut.second.reserveOut;
3482 if (importCurrencyID == systemDestID)
3484 ReserveInputs.valueMap[systemDestID] += (nativeIn - netPrimaryIn);
3488 ReserveInputs.valueMap[systemDestID] += nativeIn;
3493 if (importCurrencyID == systemDestID)
3495 spentCurrencyOut.valueMap[systemDestID] += (nativeOut - netPrimaryOut);
3499 spentCurrencyOut.valueMap[systemDestID] += nativeOut;
3503 if (systemOutConverted && importCurrencyID != systemDestID)
3505 // this does not have meaning besides a store of the system currency output that was converted
3506 currencies[importCurrencyID].reserveOutConverted = systemOutConverted;
3509 CCurrencyValueMap checkAgainstInputs(spentCurrencyOut);
3511 if (((ReserveInputs + newConvertedReservePool) - checkAgainstInputs).HasNegative())
3513 printf("importCurrencyState: %s\nnewCurrencyState: %s\n", importCurrencyState.ToUniValue().write(1,2).c_str(), newCurrencyState.ToUniValue().write(1,2).c_str());
3514 printf("newConvertedReservePool: %s\n", newConvertedReservePool.ToUniValue().write(1,2).c_str());
3515 printf("ReserveInputs: %s\nspentCurrencyOut: %s\nReserveInputs - spentCurrencyOut: %s\ncheckAgainstInputs: %s\nreserveBalanceInMap: %s\ntotalNativeFee: %ld, totalVerusFee: %ld\n",
3516 ReserveInputs.ToUniValue().write(1,2).c_str(),
3517 spentCurrencyOut.ToUniValue().write(1,2).c_str(),
3518 (ReserveInputs - spentCurrencyOut).ToUniValue().write(1,2).c_str(),
3519 checkAgainstInputs.ToUniValue().write(1,2).c_str(),
3520 reserveBalanceInMap.ToUniValue().write(1,2).c_str(),
3525 /*UniValue jsonTx(UniValue::VOBJ);
3526 CMutableTransaction mtx;
3527 mtx.vout = vOutputs;
3528 TxToUniv(mtx, uint256(), jsonTx);
3529 printf("%s: outputsOnTx:\n%s\n", __func__, jsonTx.write(1,2).c_str());
3532 printf("%s: Too much fee taken by export, ReserveInputs: %s\nReserveOutputs: %s\n", __func__,
3533 ReserveInputs.ToUniValue().write(1,2).c_str(),
3534 spentCurrencyOut.ToUniValue().write(1,2).c_str());
3535 LogPrintf("%s: Too much fee taken by export, ReserveInputs: %s\nReserveOutputs: %s\n", __func__,
3536 ReserveInputs.ToUniValue().write(1,2).c_str(),
3537 spentCurrencyOut.ToUniValue().write(1,2).c_str());
3543 CCurrencyValueMap CReserveTransactionDescriptor::ReserveInputMap(const uint160 &nativeID) const
3545 CCurrencyValueMap retVal;
3546 uint160 id = nativeID.IsNull() ? ASSETCHAINS_CHAINID : nativeID;
3547 for (auto &oneInOut : currencies)
3550 if (oneInOut.first != id)
3552 if (oneInOut.second.reserveIn)
3554 retVal.valueMap[oneInOut.first] = oneInOut.second.reserveIn;
3557 if (oneInOut.second.nativeOutConverted)
3559 retVal.valueMap[oneInOut.first] = oneInOut.second.nativeOutConverted;
3565 CCurrencyValueMap CReserveTransactionDescriptor::ReserveOutputMap(const uint160 &nativeID) const
3567 CCurrencyValueMap retVal;
3568 uint160 id = nativeID.IsNull() ? ASSETCHAINS_CHAINID : nativeID;
3569 for (auto &oneInOut : currencies)
3572 if (oneInOut.first != id)
3574 if (oneInOut.second.reserveOut)
3576 retVal.valueMap[oneInOut.first] = oneInOut.second.reserveOut;
3583 CCurrencyValueMap CReserveTransactionDescriptor::ReserveOutConvertedMap(const uint160 &nativeID) const
3585 CCurrencyValueMap retVal;
3586 uint160 id = nativeID.IsNull() ? ASSETCHAINS_CHAINID : nativeID;
3587 for (auto &oneInOut : currencies)
3590 if (oneInOut.first != id)
3592 if (oneInOut.second.reserveOutConverted)
3594 retVal.valueMap[oneInOut.first] = oneInOut.second.reserveOutConverted;
3601 CCurrencyValueMap CReserveTransactionDescriptor::NativeOutConvertedMap() const
3603 CCurrencyValueMap retVal;
3604 for (auto &oneInOut : currencies)
3606 if (oneInOut.second.nativeOutConverted)
3608 retVal.valueMap[oneInOut.first] = oneInOut.second.nativeOutConverted;
3614 CCurrencyValueMap CReserveTransactionDescriptor::ReserveConversionFeesMap() const
3616 CCurrencyValueMap retVal;
3617 for (auto &oneInOut : currencies)
3619 if (oneInOut.second.reserveConversionFees)
3621 retVal.valueMap[oneInOut.first] = oneInOut.second.reserveConversionFees;
3627 std::vector<CAmount> CReserveTransactionDescriptor::ReserveInputVec(const CCurrencyState &cState) const
3629 std::vector<CAmount> retVal(cState.currencies.size());
3630 std::map<uint160, int> curMap = cState.GetReserveMap();
3631 for (auto &oneInOut : currencies)
3633 retVal[curMap[oneInOut.first]] = oneInOut.second.reserveIn;
3638 std::vector<CAmount> CReserveTransactionDescriptor::ReserveOutputVec(const CCurrencyState &cState) const
3640 std::vector<CAmount> retVal(cState.currencies.size());
3641 std::map<uint160, int> curMap = cState.GetReserveMap();
3642 for (auto &oneInOut : currencies)
3644 retVal[curMap[oneInOut.first]] = oneInOut.second.reserveOut;
3649 std::vector<CAmount> CReserveTransactionDescriptor::ReserveOutConvertedVec(const CCurrencyState &cState) const
3651 std::vector<CAmount> retVal(cState.currencies.size());
3652 std::map<uint160, int> curMap = cState.GetReserveMap();
3653 for (auto &oneInOut : currencies)
3655 retVal[curMap[oneInOut.first]] = oneInOut.second.reserveOutConverted;
3660 std::vector<CAmount> CReserveTransactionDescriptor::NativeOutConvertedVec(const CCurrencyState &cState) const
3662 std::vector<CAmount> retVal(cState.currencies.size());
3663 std::map<uint160, int> curMap = cState.GetReserveMap();
3664 for (auto &oneInOut : currencies)
3666 retVal[curMap[oneInOut.first]] = oneInOut.second.nativeOutConverted;
3671 std::vector<CAmount> CReserveTransactionDescriptor::ReserveConversionFeesVec(const CCurrencyState &cState) const
3673 std::vector<CAmount> retVal(cState.currencies.size());
3674 std::map<uint160, int> curMap = cState.GetReserveMap();
3675 for (auto &oneInOut : currencies)
3677 retVal[curMap[oneInOut.first]] = oneInOut.second.reserveConversionFees;
3682 // this should be done no more than once to prepare a currency state to be updated to the next state
3683 // emission occurs for a block before any conversion or exchange and that impact on the currency state is calculated
3684 CCoinbaseCurrencyState &CCoinbaseCurrencyState::UpdateWithEmission(CAmount toEmit)
3688 // if supply is 0, reserve must be zero, and we cannot function as a reserve currency
3689 if (!IsFractional() || supply <= 0 || CCurrencyValueMap(currencies, reserves) <= CCurrencyValueMap())
3693 emitted = supply = toEmit;
3705 // first determine current ratio by adding up all currency weights
3706 CAmount InitialRatio = 0;
3707 for (auto weight : weights)
3709 InitialRatio += weight;
3712 // to balance rounding with truncation, we statistically add a satoshi to the initial ratio
3713 static arith_uint256 bigSatoshi(SATOSHIDEN);
3714 arith_uint256 bigInitial(InitialRatio);
3715 arith_uint256 bigEmission(toEmit);
3716 arith_uint256 bigSupply(supply);
3718 arith_uint256 bigScratch = (bigInitial * bigSupply * bigSatoshi) / (bigSupply + bigEmission);
3719 arith_uint256 bigRatio = bigScratch / bigSatoshi;
3721 if (bigRatio >= bigSatoshi)
3723 bigScratch = arith_uint256(SATOSHIDEN) * arith_uint256(SATOSHIDEN);
3724 bigRatio = bigSatoshi;
3727 int64_t newRatio = bigRatio.GetLow64();
3728 int64_t remainder = (bigScratch - (bigRatio * SATOSHIDEN)).GetLow64();
3729 // form of bankers rounding, if odd, round up at half, if even, round down at half
3730 if (remainder > (SATOSHIDEN >> 1) || (remainder == (SATOSHIDEN >> 1) && newRatio & 1))
3735 // now, we must update all weights accordingly, based on the new, total ratio, by dividing the total among all the
3736 // weights, according to their current relative weight. because this also can be a source of rounding error, we will
3737 // distribute any modulus excess randomly among the currencies
3738 std::vector<CAmount> extraWeight(currencies.size());
3739 arith_uint256 bigRatioDelta(InitialRatio - newRatio);
3740 CAmount totalUpdates = 0;
3742 for (auto &weight : weights)
3744 CAmount weightDelta = (bigRatioDelta * arith_uint256(weight) / bigSatoshi).GetLow64();
3745 weight -= weightDelta;
3746 totalUpdates += weightDelta;
3749 CAmount updateExtra = (InitialRatio - newRatio) - totalUpdates;
3751 // if we have any extra, distribute it evenly and any mod, both deterministically and pseudorandomly
3754 CAmount forAll = updateExtra / currencies.size();
3755 CAmount forSome = updateExtra % currencies.size();
3757 // get deterministic seed for linear congruential pseudorandom number for shuffle
3758 int64_t seed = supply + forAll + forSome;
3759 auto prandom = std::minstd_rand0(seed);
3761 for (int i = 0; i < extraWeight.size(); i++)
3763 extraWeight[i] = forAll;
3770 // distribute the extra as evenly as possible
3771 std::shuffle(extraWeight.begin(), extraWeight.end(), prandom);
3772 for (int i = 0; i < weights.size(); i++)
3774 weights[i] -= extraWeight[i];
3778 // update initial supply from what we currently have
3785 CCoinbaseCurrencyState &CCoinbaseCurrencyState::ApplyCarveouts(int32_t carveOut)
3787 if (carveOut && carveOut < SATOSHIDEN)
3789 // first determine current ratio by adding up all currency weights
3790 CAmount InitialRatio = 0;
3791 for (auto weight : weights)
3793 InitialRatio += weight;
3796 static arith_uint256 bigSatoshi(SATOSHIDEN);
3797 arith_uint256 bigInitial(InitialRatio);
3798 arith_uint256 bigCarveOut((int64_t)carveOut);
3799 arith_uint256 bigScratch = (bigInitial * (bigSatoshi - bigCarveOut));
3800 arith_uint256 bigNewRatio = bigScratch / bigSatoshi;
3802 int64_t newRatio = bigNewRatio.GetLow64();
3804 int64_t remainder = (bigScratch - (bigNewRatio * bigSatoshi)).GetLow64();
3805 // form of bankers rounding, if odd, round up at half, if even, round down at half
3806 if (remainder > (SATOSHIDEN >> 1) || (remainder == (SATOSHIDEN >> 1) && newRatio & 1))
3808 if (newRatio < SATOSHIDEN)
3814 // now, we must update all weights accordingly, based on the new, total ratio, by dividing the total among all the
3815 // weights, according to their current relative weight. because this also can be a source of rounding error, we will
3816 // distribute any modulus excess randomly among the currencies
3817 std::vector<CAmount> extraWeight(currencies.size());
3818 arith_uint256 bigRatioDelta(InitialRatio - newRatio);
3819 CAmount totalUpdates = 0;
3821 for (auto &weight : weights)
3823 CAmount weightDelta = (bigRatioDelta * arith_uint256(weight) / bigSatoshi).GetLow64();
3824 weight -= weightDelta;
3825 totalUpdates += weightDelta;
3828 CAmount updateExtra = (InitialRatio - newRatio) - totalUpdates;
3830 // if we have any extra, distribute it evenly and any mod, both deterministically and pseudorandomly
3833 CAmount forAll = updateExtra / currencies.size();
3834 CAmount forSome = updateExtra % currencies.size();
3836 // get deterministic seed for linear congruential pseudorandom number for shuffle
3837 int64_t seed = supply + forAll + forSome;
3838 auto prandom = std::minstd_rand0(seed);
3840 for (int i = 0; i < extraWeight.size(); i++)
3842 extraWeight[i] = forAll;
3849 // distribute the extra weight loss as evenly as possible
3850 std::shuffle(extraWeight.begin(), extraWeight.end(), prandom);
3851 for (int i = 0; i < weights.size(); i++)
3853 weights[i] -= extraWeight[i];
3861 void CCoinbaseCurrencyState::RevertFees(const std::vector<CAmount> &normalConversionPrice,
3862 const std::vector<CAmount> &outgoingConversionPrice,
3863 const uint160 &systemID)
3865 auto reserveIndexMap = GetReserveMap();
3866 if (IsFractional() && reserveIndexMap.count(systemID) && reserveIndexMap.find(systemID)->second)
3869 // all liquidity fees except blockchain native currency go into the reserves for conversion
3870 // native currency gets burned
3871 CCurrencyValueMap allConvertedFees(currencies, fees);
3872 if (primaryCurrencyFees)
3874 allConvertedFees.valueMap[GetID()] = primaryCurrencyFees;
3877 CCurrencyValueMap liquidityFees(CCurrencyValueMap(currencies, conversionFees));
3878 if (primaryCurrencyConversionFees)
3880 liquidityFees.valueMap[systemID] = primaryCurrencyConversionFees;
3883 liquidityFees = liquidityFees / 2;
3885 //printf("%s: liquidityfees/2:\n%s\n", __func__, liquidityFees.ToUniValue().write(1,2).c_str());
3887 for (auto &oneLiquidityFee : liquidityFees.valueMap)
3889 // importCurrency as liquidity fee will have gotten burned, so add it back to supply
3890 if (oneLiquidityFee.first == GetID())
3892 supply += oneLiquidityFee.second;
3896 // otherwise, the currency went in to reserves, so remove it
3897 reserves[reserveIndexMap[oneLiquidityFee.first]] -= oneLiquidityFee.second;
3901 // the rest of the fees should have been converted to native and paid out
3902 // from native. calculate an exact amount of converted native fee by converting
3903 // according to the prices supplied. The rest of the fees are transfer fees or
3904 // something else that does not affect currency reserves.
3905 allConvertedFees -= liquidityFees;
3906 CAmount totalConvertedNativeFee = 0;
3907 int systemDestIdx = reserveIndexMap[systemID];
3908 for (auto &oneFee : allConvertedFees.valueMap)
3910 // fees are not converted from the system currency, only to it
3911 // for that reason, skip system in the loop and calculate the amount
3912 // that was converted to it to determine the amount to replenish
3913 if (oneFee.first != systemID)
3915 if (reserveIndexMap.count(oneFee.first))
3917 reserves[reserveIndexMap[oneFee.first]] -= oneFee.second;
3918 totalConvertedNativeFee +=
3919 NativeToReserveRaw(ReserveToNativeRaw(oneFee.second, normalConversionPrice[reserveIndexMap[oneFee.first]]),
3920 outgoingConversionPrice[systemDestIdx]);
3922 else if (oneFee.first == GetID())
3924 totalConvertedNativeFee +=
3925 NativeToReserveRaw(oneFee.second, normalConversionPrice[systemDestIdx]);
3929 reserves[systemDestIdx] += totalConvertedNativeFee;
3931 //printf("%s: currencyState:\n%s\n", __func__, ToUniValue().write(1,2).c_str());
3935 CCurrencyValueMap CCoinbaseCurrencyState::CalculateConvertedFees(const std::vector<CAmount> &normalConversionPrice,
3936 const std::vector<CAmount> &outgoingConversionPrice,
3937 const uint160 &systemID,
3938 bool &feesConverted,
3939 CCurrencyValueMap &liquidityFees,
3940 CCurrencyValueMap &convertedFees) const
3942 CCurrencyValueMap originalFees(currencies, fees);
3943 auto reserveIndexMap = GetReserveMap();
3944 feesConverted = false;
3945 if (IsFractional() && reserveIndexMap.count(systemID) && reserveIndexMap.find(systemID)->second)
3947 feesConverted = true;
3949 CCurrencyValueMap allConvertedFees(currencies, fees);
3950 if (primaryCurrencyFees)
3952 allConvertedFees.valueMap[GetID()] = primaryCurrencyFees;
3955 liquidityFees = CCurrencyValueMap(CCurrencyValueMap(currencies, conversionFees));
3956 if (primaryCurrencyConversionFees)
3958 liquidityFees.valueMap[systemID] = primaryCurrencyConversionFees;
3961 liquidityFees = liquidityFees / 2;
3963 allConvertedFees -= liquidityFees;
3964 CAmount totalNativeFee = 0;
3965 if (allConvertedFees.valueMap.count(systemID))
3967 totalNativeFee += allConvertedFees.valueMap[systemID];
3969 int systemDestIdx = reserveIndexMap[systemID];
3970 for (auto &oneFee : allConvertedFees.valueMap)
3972 // fees are not converted from the system currency, only to it
3973 // for that reason, skip system in the loop and calculate the amount
3974 // that was converted to it to determine the amount to replenish
3975 if (oneFee.first != systemID)
3977 if (reserveIndexMap.count(oneFee.first))
3980 NativeToReserveRaw(ReserveToNativeRaw(oneFee.second, normalConversionPrice[reserveIndexMap[oneFee.first]]),
3981 outgoingConversionPrice[systemDestIdx]);
3983 else if (oneFee.first == GetID())
3986 NativeToReserveRaw(oneFee.second, normalConversionPrice[systemDestIdx]);
3990 convertedFees.valueMap[systemID] += totalNativeFee;
3992 //printf("%s: liquidityfees:\n%s\n", __func__, liquidityFees.ToUniValue().write(1,2).c_str());
3993 //printf("%s: allConvertedFees:\n%s\n", __func__, allConvertedFees.ToUniValue().write(1,2).c_str());
3994 //printf("%s: convertedFees:\n%s\n", __func__, convertedFees.ToUniValue().write(1,2).c_str());
3995 return originalFees;
3998 void CCoinbaseCurrencyState::RevertReservesAndSupply()
4002 // between prelaunch and postlaunch, we only revert fees since preConversions are accounted for differently
4003 auto reserveMap = GetReserveMap();
4004 if (IsLaunchClear() && !IsPrelaunch() && reserveMap.count(ASSETCHAINS_CHAINID) && reserves[reserveMap[ASSETCHAINS_CHAINID]])
4006 // leave all currencies in
4007 // revert only fees at launch pricing
4008 RevertFees(viaConversionPrice, viaConversionPrice, ASSETCHAINS_CHAINID);
4012 // reverse last changes
4013 auto currencyMap = GetReserveMap();
4015 // revert changes in reserves and supply to pre conversion state, add reserve outs and subtract reserve ins
4016 for (auto &oneCur : currencyMap)
4018 reserves[oneCur.second] += (reserveOut[oneCur.second] - reserveIn[oneCur.second]);
4019 supply += primaryCurrencyIn[oneCur.second];
4023 // if this is the last launch clear pre-launch, it will emit and create the correct supply starting
4024 // from the initial supply, which was more for display. reset to initial supply as a starting point
4027 supply -= primaryCurrencyOut;
4031 supply -= (primaryCurrencyOut - preConvertedOut);
4033 weights = priorWeights;
4036 CAmount CCurrencyState::CalculateConversionFee(CAmount inputAmount, bool convertToNative, int currencyIndex) const
4038 arith_uint256 bigAmount(inputAmount);
4039 arith_uint256 bigSatoshi(SATOSHIDEN);
4041 // we need to calculate a fee based either on the amount to convert or the last price
4042 // times the reserve
4043 if (convertToNative)
4046 cpp_dec_float_50 priceInReserve = PriceInReserveDecFloat50(currencyIndex);
4047 if (!to_int64(priceInReserve, price))
4051 bigAmount = price ? (bigAmount * bigSatoshi) / arith_uint256(price) : 0;
4055 fee = ((bigAmount * arith_uint256(CReserveTransfer::SUCCESS_FEE)) / bigSatoshi).GetLow64();
4056 if (fee < CReserveTransfer::MIN_SUCCESS_FEE)
4058 fee = CReserveTransfer::MIN_SUCCESS_FEE;
4063 CAmount CReserveTransactionDescriptor::CalculateConversionFeeNoMin(CAmount inputAmount)
4065 arith_uint256 bigAmount(inputAmount);
4066 arith_uint256 bigSatoshi(SATOSHIDEN);
4067 return ((bigAmount * arith_uint256(CReserveTransfer::SUCCESS_FEE)) / bigSatoshi).GetLow64();
4070 CAmount CReserveTransactionDescriptor::CalculateConversionFee(CAmount inputAmount)
4072 CAmount fee = CalculateConversionFeeNoMin(inputAmount);
4073 if (fee < CReserveTransfer::MIN_SUCCESS_FEE)
4075 fee = CReserveTransfer::MIN_SUCCESS_FEE;
4080 // this calculates a fee that will be added to an amount and result in the same percentage as above,
4081 // such that a total of the inputAmount + this returned fee, if passed to CalculateConversionFee, would return
4083 CAmount CReserveTransactionDescriptor::CalculateAdditionalConversionFee(CAmount inputAmount)
4085 arith_uint256 bigAmount(inputAmount);
4086 arith_uint256 bigSatoshi(SATOSHIDEN);
4087 arith_uint256 conversionFee(CReserveTransfer::SUCCESS_FEE);
4089 CAmount newAmount = ((bigAmount * bigSatoshi) / (bigSatoshi - conversionFee)).GetLow64();
4090 if (newAmount - inputAmount < CReserveTransfer::MIN_SUCCESS_FEE)
4092 newAmount = inputAmount + CReserveTransfer::MIN_SUCCESS_FEE;
4094 CAmount fee = CalculateConversionFee(newAmount);
4095 newAmount = inputAmount + fee;
4096 fee = CalculateConversionFee(newAmount); // again to account for minimum fee
4097 fee += inputAmount - (newAmount - fee); // add any additional difference
4101 bool CFeePool::GetCoinbaseFeePool(CFeePool &feePool, uint32_t height)
4104 CTransaction coinbaseTx;
4105 feePool.SetInvalid();
4106 if (!height || chainActive.Height() < height)
4108 height = chainActive.Height();
4114 if (ReadBlockFromDisk(block, chainActive[height], Params().GetConsensus()))
4116 coinbaseTx = block.vtx[0];
4123 for (auto &txOut : coinbaseTx.vout)
4126 if (txOut.scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode == EVAL_FEE_POOL && p.vData.size())
4128 feePool = CFeePool(p.vData[0]);
4134 CFeePool::CFeePool(const CTransaction &coinbaseTx)
4136 nVersion = VERSION_INVALID;
4137 if (coinbaseTx.IsCoinBase())
4139 for (auto &txOut : coinbaseTx.vout)
4142 if (txOut.scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode == EVAL_FEE_POOL && p.vData.size())
4144 ::FromVector(p.vData[0], *this);
4150 bool ValidateFeePool(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
4152 // fee pool output is unspendable
4156 bool IsFeePoolInput(const CScript &scriptSig)
4161 bool PrecheckFeePool(const CTransaction &tx, int32_t outNum, CValidationState &state, uint32_t height)
4166 bool PrecheckReserveTransfer(const CTransaction &tx, int32_t outNum, CValidationState &state, uint32_t height)
4168 // do a basic sanity check that this reserve transfer's values are consistent
4170 CReserveTransfer rt;
4171 return (tx.vout[outNum].scriptPubKey.IsPayToCryptoCondition(p) &&
4173 p.evalCode == EVAL_RESERVE_TRANSFER &&
4175 (rt = CReserveTransfer(p.vData[0])).IsValid() &&
4176 rt.TotalCurrencyOut().valueMap[ASSETCHAINS_CHAINID] == tx.vout[outNum].nValue);