]> Git Repo - VerusCoin.git/blob - src/pbaas/reserves.cpp
Merge pull request #97 from miketout/dev
[VerusCoin.git] / src / pbaas / reserves.cpp
1 /********************************************************************
2  * (C) 2019 Michael Toutonghi
3  * 
4  * Distributed under the MIT software license, see the accompanying
5  * file COPYING or http://www.opensource.org/licenses/mit-license.php.
6  * 
7  * This provides reserve currency functions, leveraging the multi-precision boost libraries to calculate reserve currency conversions.
8  * 
9  */
10
11 #include "main.h"
12 #include "pbaas/pbaas.h"
13 #include "pbaas/reserves.h"
14 #include "pbaas/notarization.h"
15 #include "rpc/server.h"
16 #include "key_io.h"
17 #include <random>
18
19
20 // calculate fees required in one currency to pay in another
21 CAmount CReserveTransfer::CalculateTransferFee(const CTransferDestination &destination, uint32_t flags)
22 {
23     if ((flags & FEE_OUTPUT) || (!(flags & PRECONVERT) && (flags & CONVERT)))
24     {
25         return 0;
26     }
27     return CReserveTransfer::DEFAULT_PER_STEP_FEE << 1 + ((CReserveTransfer::DEFAULT_PER_STEP_FEE << 1) * (destination.destination.size() / DESTINATION_BYTE_DIVISOR));
28 }
29
30 CAmount CReserveTransfer::CalculateTransferFee() const
31 {
32     // determine fee for this send
33     return CalculateTransferFee(destination, flags);
34 }
35
36 CCurrencyValueMap CReserveTransfer::TotalTransferFee() const
37 {
38     CCurrencyValueMap retVal;
39     CAmount transferFee = nFees;
40     if (destination.HasGatewayLeg() && destination.fees)
41     {
42         transferFee += destination.fees;
43     }
44     retVal.valueMap[feeCurrencyID] += transferFee;
45     return retVal;
46 }
47
48 CCurrencyValueMap CReserveTransfer::ConversionFee() const
49 {
50     CCurrencyValueMap retVal;
51     // add conversion fees in source currency for conversions or pre-conversions
52     if (IsConversion() || IsPreConversion())
53     {
54         for (auto &oneCur : reserveValues.valueMap)
55         {
56             retVal.valueMap[oneCur.first] += CReserveTransactionDescriptor::CalculateConversionFee(oneCur.second);
57         }
58         if (IsReserveToReserve())
59         {
60             retVal = retVal * 2;
61         }
62     }
63     return retVal;
64 }
65
66 CCurrencyValueMap CReserveTransfer::CalculateFee(uint32_t flags, CAmount transferTotal) const
67 {
68     CCurrencyValueMap feeMap;
69
70     feeMap.valueMap[feeCurrencyID] = CalculateTransferFee();
71
72     // add conversion fees in source currency for conversions or pre-conversions
73     if (IsConversion() || IsPreConversion())
74     {
75         for (auto &oneCur : reserveValues.valueMap)
76         {
77             feeMap.valueMap[oneCur.first] += CReserveTransactionDescriptor::CalculateConversionFee(oneCur.second);
78         }
79         if (IsReserveToReserve())
80         {
81             feeMap = feeMap * 2;
82         }
83     }
84
85     // consider extra-leg pricing here
86
87     return feeMap;
88 }
89
90 CCrossChainImport::CCrossChainImport(const CScript &script)
91 {
92     COptCCParams p;
93     if (IsPayToCryptoCondition(script, p) && p.IsValid())
94     {
95         // always take the first for now
96         if (p.evalCode == EVAL_CROSSCHAIN_IMPORT && p.vData.size())
97         {
98             FromVector(p.vData[0], *this);
99         }
100     }
101 }
102
103 CCrossChainImport::CCrossChainImport(const CTransaction &tx, int32_t *pOutNum)
104 {
105     for (int i = 0; i < tx.vout.size(); i++)
106     {
107         COptCCParams p;
108         if (IsPayToCryptoCondition(tx.vout[i].scriptPubKey, p) && p.IsValid())
109         {
110             // always take the first for now
111             if (p.evalCode == EVAL_CROSSCHAIN_IMPORT && p.vData.size())
112             {
113                 FromVector(p.vData[0], *this);
114                 if (pOutNum)
115                 {
116                     *pOutNum = i;
117                 }
118                 break;
119             }
120         }
121     }
122 }
123
124 bool CCrossChainExport::GetExportInfo(const CTransaction &exportTx, 
125                                       int numExportOut,
126                                       int &primaryExportOutNumOut,
127                                       int32_t &nextOutput,
128                                       CPBaaSNotarization &exportNotarization,
129                                       std::vector<CReserveTransfer> &reserveTransfers,
130                                       CValidationState &state) const
131 {
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
134
135     // if this is called directly to get info, though it is a supplemental output, it is currently an error
136     if (IsSupplemental())
137     {
138         return state.Error(strprintf("%s: cannot get export data directly from a supplemental data output. must be in context",__func__));
139     }
140
141     auto hw = CMMRNode<>::GetHashWriter();
142
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;
146     if (numOutput < 0)
147     {
148         return state.Error(strprintf("%s: invalid output index for export out or invalid export transaction",__func__));
149     }
150     primaryExportOutNumOut = numOutput;
151
152     // if this export is from our system
153     if (sourceSystemID == ASSETCHAINS_CHAINID)
154     {
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)
159         {
160             numOutput++;
161         }
162
163         // retrieve reserve transfers from export transaction inputs
164         if (numInputs > 0)
165         {
166             for (int i = firstInput; i < (firstInput + numInputs); i++)
167             {
168                 CTransaction rtTx;
169                 COptCCParams rtP;
170                 CReserveTransfer rt;
171                 uint256 hashBlk;
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) &&
175                         rtP.IsValid() &&
176                         rtP.evalCode == EVAL_RESERVE_TRANSFER &&
177                         rtP.vData.size() &&
178                         (rt = CReserveTransfer(rtP.vData[0])).IsValid()))
179                 {
180                     return state.Error(strprintf("%s: invalid reserve transfer for export",__func__));
181                 }
182                 hw << rt;
183                 reserveTransfers.push_back(rt);
184             }
185         }
186     }
187     else
188     {
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())
194         {
195             COptCCParams p;
196             for (auto &oneRt : rtExport.reserveTransfers)
197             {
198                 hw << oneRt;
199                 reserveTransfers.push_back(oneRt);
200             }
201             if (rtExport.HasSupplement() ||
202                 (!rtExport.IsSameChain() && rtExport.numInputs > 0))
203             {
204                 numOutput++;
205                 if (!(exportTx.vout.size() > numOutput &&
206                       exportTx.vout[numOutput].scriptPubKey.IsPayToCryptoCondition(p) &&
207                       p.IsValid() &&
208                       p.evalCode == EVAL_CROSSCHAIN_EXPORT &&
209                       p.vData.size() &&
210                       (rtExport = CCrossChainExport(p.vData[0])).IsValid() &&
211                       rtExport.IsSupplemental()))
212                 {
213                     numOutput--;
214                     rtExport = CCrossChainExport();
215                 }
216             }
217             else
218             {
219                 // no more supplements, done
220                 rtExport = CCrossChainExport();
221             }
222         }
223     }
224
225     // now, we should have accurate reserve transfers
226     uint256 rtHash = reserveTransfers.size() ? hw.GetHash() : uint256();
227     if (rtHash != hashReserveTransfers)
228     {
229         return state.Error(strprintf("%s: reserve transfers do not match reserve transfer hash in export",__func__));
230     }
231
232     exportNotarization = CPBaaSNotarization();
233
234     if (IsSameChain() && !IsChainDefinition())
235     {
236         if (IsClearLaunch() || !IsPrelaunch())
237         {
238             numOutput++;
239             COptCCParams p;
240             // we have an export finalization to verify/skip
241             if (!(exportTx.vout.size() > numOutput &&
242                     exportTx.vout[numOutput].scriptPubKey.IsPayToCryptoCondition(p) &&
243                     p.IsValid() &&
244                     p.evalCode == EVAL_FINALIZE_EXPORT &&
245                     p.vData.size() &&
246                     (CObjectFinalization(p.vData[0])).IsValid()))
247             {
248                 return state.Error(strprintf("%s: invalid export finalization",__func__));
249             }
250         }
251         if ((IsPrelaunch() || IsClearLaunch()))
252         {
253             // in same chain before launch, we expect a notarization to follow
254             numOutput++;
255             COptCCParams p;
256             if (!(exportTx.vout.size() > numOutput &&
257                 exportTx.vout[numOutput].scriptPubKey.IsPayToCryptoCondition(p) &&
258                 p.IsValid() &&
259                 (p.evalCode == EVAL_ACCEPTEDNOTARIZATION || p.evalCode == EVAL_EARNEDNOTARIZATION) &&
260                 p.vData.size() &&
261                 (exportNotarization = CPBaaSNotarization(p.vData[0])).IsValid()))
262             {
263                 return state.Error(strprintf("%s: invalid export notarization",__func__));
264             }
265         }
266     }
267     nextOutput = numOutput + 1;
268     return true;
269 }
270
271 bool CCrossChainExport::GetExportInfo(const CTransaction &exportTx, 
272                                     int numExportOut, 
273                                     int &primaryExportOutNumOut,
274                                     int32_t &nextOutput,
275                                     CPBaaSNotarization &exportNotarization, 
276                                     std::vector<CReserveTransfer> &reserveTransfers) const
277 {
278     CValidationState state;
279     return GetExportInfo(exportTx, numExportOut, primaryExportOutNumOut, nextOutput, exportNotarization, reserveTransfers, state);
280 }
281
282
283 bool CCrossChainImport::GetImportInfo(const CTransaction &importTx, 
284                                       uint32_t nHeight,
285                                       int numImportOut, 
286                                       CCrossChainExport &ccx,
287                                       CCrossChainImport &sysCCI,
288                                       int32_t &sysCCIOut,
289                                       CPBaaSNotarization &importNotarization,
290                                       int32_t &importNotarizationOut,
291                                       int32_t &evidenceOutStart,
292                                       int32_t &evidenceOutEnd,
293                                       std::vector<CReserveTransfer> &reserveTransfers,
294                                       CValidationState &state) const
295 {
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
298
299     // following that, we should find in order:
300     //
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
302     //
303     // 2. any necessary export proof for the import, present only if we are coming from an external system or PBaaS chain
304     //
305     // 3. if we are coming from an external system or PBaaS chain, following outputs will include the reserve transfers for the export proof
306     //
307     // 4. Notarization for import currency, only present if this is fractional currency or first launch of new PBaaS chain
308     // 
309
310     sysCCIOut = -1;
311     evidenceOutStart = -1;
312     evidenceOutEnd = -1;
313
314     CCrossChainImport sysCCITemp;
315
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.
318     LOCK(mempool.cs);
319
320     uint32_t solutionVersion = CConstVerusSolutionVector::GetVersionByHeight(nHeight);
321
322     CCrossChainImport altImport;
323     const CCrossChainImport *pBaseImport = this;
324
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()) 
328     {
329         if (!(numImportOut-- > 0 &&
330               (altImport = CCrossChainImport(importTx.vout[numImportOut].scriptPubKey)).IsValid() &&
331               !(pBaseImport = &altImport)->IsSourceSystemImport()))
332         {
333             return state.Error(strprintf("%s: invalid import",__func__));
334         }
335     }
336
337     bool isPBaaSDefinitionOrLaunch = (!IsVerusActive() && pBaseImport->IsInitialLaunchImport()) || 
338                                      (pBaseImport->IsDefinitionImport() &&
339                                       pBaseImport->sourceSystemID != ASSETCHAINS_CHAINID);
340
341     importNotarizationOut = numImportOut + 1;
342
343     if (pBaseImport->IsSameChain())
344     {
345         // reserve transfers are available via the inputs to the matching export
346         CTransaction exportTx = pBaseImport->exportTxId.IsNull() ? importTx : CTransaction();
347         uint256 hashBlk;
348         COptCCParams p;
349
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) &&
355               p.IsValid() &&
356               p.evalCode == EVAL_CROSSCHAIN_EXPORT &&
357               p.vData.size() &&
358               (ccx = CCrossChainExport(p.vData[0])).IsValid())))
359         {
360             return state.Error(strprintf("%s: cannot retrieve export transaction for import",__func__));
361         }
362
363         if (!pBaseImport->IsDefinitionImport())
364         {
365             int32_t nextOutput;
366             CPBaaSNotarization xNotarization;
367             int primaryOutNumOut;
368             if (!ccx.GetExportInfo(exportTx, pBaseImport->exportTxOutNum, primaryOutNumOut, nextOutput, xNotarization, reserveTransfers, state))
369             {
370                 return false;
371             }
372         }
373         // next output after import out is notarization
374     }
375     else
376     {
377         COptCCParams p;
378
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)
382         {
383             // next output should be the import for the system from which this export comes
384             uint256 hashBlk;
385             sysCCIOut = numImportOut + 1;
386             if (!(sysCCIOut >= 0 &&
387                 importTx.vout.size() > sysCCIOut &&
388                 importTx.vout[sysCCIOut].scriptPubKey.IsPayToCryptoCondition(p) &&
389                 p.IsValid() &&
390                 p.evalCode == EVAL_CROSSCHAIN_IMPORT &&
391                 p.vData.size() &&
392                 (sysCCITemp = CCrossChainImport(p.vData[0])).IsValid()))
393             {
394                 return state.Error(strprintf("%s: cannot retrieve export evidence for import",__func__));
395             }
396
397             importNotarizationOut++;
398         }
399
400         if (!isPBaaSDefinitionOrLaunch ||
401             pBaseImport->importCurrencyID == ASSETCHAINS_CHAINID ||
402             (!pBaseImport->importCurrencyID.IsNull() && pBaseImport->importCurrencyID == ConnectedChains.ThisChain().GatewayConverterID()))
403         {
404             // next output should be export in evidence output followed by supplemental reserve transfers for the export
405             evidenceOutStart = importNotarizationOut + 1;
406             CNotaryEvidence evidence;
407
408             if (!(evidenceOutStart >= 0 &&
409                 importTx.vout.size() > evidenceOutStart &&
410                 importTx.vout[evidenceOutStart].scriptPubKey.IsPayToCryptoCondition(p) &&
411                 p.IsValid() &&
412                 p.evalCode == EVAL_NOTARY_EVIDENCE &&
413                 p.vData.size() &&
414                 (evidence = CNotaryEvidence(p.vData[0])).IsValid() &&
415                 evidence.IsPartialTxProof() &&
416                 evidence.evidence.size()))
417             {
418                 return state.Error(strprintf("%s: cannot retrieve export evidence for import", __func__));
419             }
420
421             CTransaction exportTx;
422             p = COptCCParams();
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) &&
427                 p.IsValid() &&
428                 p.vData.size() &&
429                 (ccx = CCrossChainExport(p.vData[0])).IsValid()))
430             {
431                 return state.Error(strprintf("%s: invalid export evidence for import", __func__));
432             }
433             int32_t nextOutput;
434             CPBaaSNotarization xNotarization;
435             int primaryOutNumOut;
436             if (!ccx.GetExportInfo(importTx, evidenceOutStart, primaryOutNumOut, nextOutput, xNotarization, reserveTransfers))
437             {
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__));
442             }
443
444             // evidence out end points to the last evidence out, not beyond
445             evidenceOutEnd = nextOutput - 1;
446         }
447     }
448     COptCCParams p;
449     if (!(importTx.vout.size() > importNotarizationOut &&
450           importTx.vout[importNotarizationOut].scriptPubKey.IsPayToCryptoCondition(p) &&
451           p.IsValid() &&
452           (p.evalCode == EVAL_ACCEPTEDNOTARIZATION || p.evalCode == EVAL_EARNEDNOTARIZATION) &&
453           p.vData.size() &&
454           (importNotarization = CPBaaSNotarization(p.vData[0])).IsValid()))
455     {
456         return state.Error(strprintf("%s: invalid import notarization for import",__func__));
457     }
458     if (sysCCITemp.IsValid())
459     {
460         sysCCI = sysCCITemp;
461     }
462     return true;
463 }
464
465 bool CCrossChainImport::GetImportInfo(const CTransaction &importTx, 
466                                     uint32_t nHeight,
467                                     int numImportOut, 
468                                     CCrossChainExport &ccx,
469                                     CCrossChainImport &sysCCI,
470                                     int32_t &sysCCIOut,
471                                     CPBaaSNotarization &importNotarization, 
472                                     int32_t &importNotarizationOut,
473                                     int32_t &evidenceOutStart,
474                                     int32_t &evidenceOutEnd,
475                                     std::vector<CReserveTransfer> &reserveTransfers) const
476 {
477     CValidationState state;
478     return GetImportInfo(importTx, 
479                             nHeight,
480                             numImportOut, 
481                             ccx, 
482                             sysCCI, 
483                             sysCCIOut,
484                             importNotarization, 
485                             importNotarizationOut, 
486                             evidenceOutStart,
487                             evidenceOutEnd,
488                             reserveTransfers, 
489                             state);
490 }
491
492 bool CCrossChainImport::ValidateImport(const CTransaction &tx,
493                                        int numImportin,
494                                        int numImportOut,
495                                        CCrossChainExport &ccx,
496                                        CPBaaSNotarization &importNotarization,
497                                        std::vector<CReserveTransfer> &reserveTransfers,
498                                        CValidationState &state) const
499 {
500     return true;
501 }
502
503 bool CCrossChainImport::ValidateImport(const CTransaction &tx,
504                                         int numImportin,
505                                         int numImportOut,
506                                         CCrossChainExport &ccx,
507                                         CPBaaSNotarization &importNotarization,
508                                         std::vector<CReserveTransfer> &reserveTransfers) const
509 {
510     CValidationState state;
511     return ValidateImport(tx, numImportin, numImportOut, ccx, importNotarization, reserveTransfers, state);
512 }
513
514 CCurrencyState::CCurrencyState(const UniValue &obj)
515 {
516     try
517     {
518         flags = uni_get_int(find_value(obj, "flags"));
519         version = uni_get_int(find_value(obj, "version"), VERSION_CURRENT);
520
521         std::string cIDStr = uni_get_str(find_value(obj, "currencyid"));
522         if (cIDStr != "")
523         {
524             CTxDestination currencyDest = DecodeDestination(cIDStr);
525             currencyID = GetDestinationID(currencyDest);
526         }
527
528         auto CurrenciesArr = IsFractional() ? find_value(obj, "reservecurrencies") : find_value(obj, "launchcurrencies");
529         size_t numCurrencies = 0;
530
531         if (IsFractional() &&
532             (!CurrenciesArr.isArray() ||
533              !(numCurrencies = CurrenciesArr.size())))
534         {
535             version = VERSION_INVALID;
536             LogPrintf("%s: Failed to proplerly specify launch or reserve currencies in currency definition\n", __func__);
537         }
538         if (numCurrencies > MAX_RESERVE_CURRENCIES)
539         {
540             version = VERSION_INVALID;
541             LogPrintf("%s: More than %d launch or reserve currencies in currency definition\n", __func__, MAX_RESERVE_CURRENCIES);
542         }
543
544         // store currencies, weights, and reserves
545         if (CurrenciesArr.size())
546         {
547             try
548             {
549                 for (int i = 0; i < CurrenciesArr.size(); i++)
550                 {
551                     uint160 currencyID = GetDestinationID(DecodeDestination(uni_get_str(find_value(CurrenciesArr[i], "currencyid"))));
552                     if (currencyID.IsNull())
553                     {
554                         LogPrintf("Invalid currency ID\n");
555                         version = VERSION_INVALID;
556                         break;
557                     }
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")));
561                 }
562             }
563             catch (...)
564             {
565                 version = VERSION_INVALID;
566                 LogPrintf("Invalid specification of currencies, weights, and/or reserves in initial definition of reserve currency\n");
567             }
568         }
569
570         if (version == VERSION_INVALID)
571         {
572             printf("Invalid currency specification, see debug.log for reason other than invalid flags\n");
573             LogPrintf("Invalid currency specification\n");
574         }
575         else
576         {
577             initialSupply = AmountFromValue(find_value(obj, "initialsupply"));
578             emitted = AmountFromValue(find_value(obj, "emitted"));
579             supply = AmountFromValue(find_value(obj, "supply"));
580         }
581     }
582     catch (...)
583     {
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;
587     }
588 }
589
590 CCoinbaseCurrencyState::CCoinbaseCurrencyState(const CTransaction &tx, int *pOutIdx)
591 {
592     int localIdx;
593     int &i = pOutIdx ? *pOutIdx : localIdx;
594     for (i = 0; i < tx.vout.size(); i++)
595     {
596         COptCCParams p;
597         if (IsPayToCryptoCondition(tx.vout[i].scriptPubKey, p))
598         {
599             if (p.evalCode == EVAL_CURRENCYSTATE && p.vData.size())
600             {
601                 FromVector(p.vData[0], *this);
602                 break;
603             }
604         }
605     }
606 }
607
608 std::vector<std::vector<CAmount>> ValueColumnsFromUniValue(const UniValue &uni,
609                                                            const std::vector<std::string> &rowNames,
610                                                            const std::vector<std::string> &columnNames)
611 {
612     std::vector<std::vector<CAmount>> retVal;
613     for (int i = 0; i < rowNames.size(); i++)
614     {
615         UniValue row = find_value(uni, rowNames[i]);
616         if (row.isObject())
617         {
618             for (int j = 0; j < columnNames.size(); j++)
619             {
620                 if (retVal.size() == j)
621                 {
622                     retVal.emplace_back();
623                 }
624                 CAmount columnVal = 0;
625                 columnVal = AmountFromValueNoErr(find_value(row, columnNames[j]));
626                 retVal[j].push_back(columnVal);
627             }
628         }
629     }
630     return retVal;
631 }
632
633
634 CCoinbaseCurrencyState::CCoinbaseCurrencyState(const UniValue &obj) : CCurrencyState(obj)
635 {
636     try
637     {
638         std::vector<std::vector<CAmount>> columnAmounts;
639
640         std::vector<std::string> rowNames;
641         auto currenciesValue = find_value(obj, "currencies");
642         if (currenciesValue.isObject())
643         {
644             rowNames = currenciesValue.getKeys();
645         }
646         if (!currencies.size() && rowNames.size())
647         {
648             currencies.resize(rowNames.size());
649             weights.resize(rowNames.size());
650             reserves.resize(rowNames.size());
651             for (int i = 0; i < rowNames.size(); i++)
652             {
653                 currencies[i] = GetDestinationID(DecodeDestination(rowNames[i]));
654             }
655         }
656         else if (currencies.size())
657         {
658             rowNames.resize(currencies.size());
659             for (int i = 0; i < rowNames.size(); i++)
660             {
661                 rowNames[i] = EncodeDestination(CIdentityID(currencies[i]));
662             }
663         }
664         if (currencies.size() != rowNames.size())
665         {
666             LogPrintf("%s: mismatch currencies and reserve currencies\n", __func__);
667             version = VERSION_INVALID;
668             return;
669         }
670         std::vector<std::string> columnNames({"reservein", "primarycurrencyin", "reserveout", "lastconversionprice", "viaconversionprice", "fees", "conversionfees", "priorweights"});
671         if (currenciesValue.isObject())
672         {
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())
676             {
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])
686                 {
687                     priorWeights.push_back(oneColumnNum);
688                 }
689             }
690         }
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"));
695     }
696     catch(...)
697     {
698         version = VERSION_INVALID;
699         LogPrintf("%s: exception reading json CCoinbaseCurrencyState\n", __func__);
700     }
701 }
702
703 CAmount CalculateFractionalOut(CAmount NormalizedReserveIn, CAmount Supply, CAmount NormalizedReserve, int32_t reserveRatio)
704 {
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;
715
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());
717
718     int64_t fractionalOut = 0;
719
720     // first check if anything to buy
721     if (NormalizedReserveIn)
722     {
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());
725
726         if (!CCurrencyState::to_int64(supplyout, fractionalOut))
727         {
728             return -1;
729         }
730     }
731     return fractionalOut;
732 }
733
734 CAmount CalculateReserveOut(CAmount FractionalIn, CAmount Supply, CAmount NormalizedReserve, int32_t reserveRatio)
735 {
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;
746
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());
748
749     int64_t reserveOut = 0;
750
751     // first check if anything to buy
752     if (FractionalIn)
753     {
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());
756
757         if (!CCurrencyState::to_int64(reserveout, reserveOut))
758         {
759             assert(false);
760         }
761     }
762     return reserveOut;
763 }
764
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
774 {
775     static arith_uint256 bigSatoshi(SATOSHIDEN);
776
777     int32_t numCurrencies = currencies.size();
778     std::vector<CAmount> inputReserves = _inputReserves;
779     std::vector<CAmount> inputFractional = _inputFractional;
780
781     CCurrencyState newState = *this;
782     std::vector<CAmount> rates(numCurrencies);
783     std::vector<CAmount> initialRates = PricesInReserve();
784
785     bool haveConversion = false;
786
787     if (inputReserves.size() == inputFractional.size() && inputReserves.size() == numCurrencies && 
788         (!pCrossConversions || pCrossConversions->size() == numCurrencies))
789     {
790         int i;
791         for (i = 0; i < numCurrencies; i++)
792         {
793             if (!pCrossConversions || (*pCrossConversions)[i].size() != numCurrencies)
794             {
795                 break;
796             }
797         }
798         if (!pCrossConversions || i == numCurrencies)
799         {
800             for (auto oneIn : inputReserves)
801             {
802                 if (oneIn)
803                 {
804                     haveConversion = true;
805                     break;
806                 }
807             }
808             if (!haveConversion)
809             {
810                 for (auto oneIn : inputFractional)
811                 {
812                     if (oneIn)
813                     {
814                         haveConversion = true;
815                         break;
816                     }
817                 }
818             }
819         }
820     }
821     else
822     {
823         printf("%s: invalid parameters\n", __func__);
824         LogPrintf("%s: invalid parameters\n", __func__);
825         return initialRates;
826     }
827     
828     if (!haveConversion)
829     {
830         // not considered an error
831         _newState = newState;
832         return initialRates;
833     }
834
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
837     bool failed = false;
838
839     for (auto oneIn : inputReserves)
840     {
841         if (oneIn < 0)
842         {
843             failed = true;
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);
846             break;
847         }
848     }
849     for (auto oneIn : inputFractional)
850     {
851         if (oneIn < 0)
852         {
853             failed = true;
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);
856             break;
857         }
858     }
859
860     if (failed)
861     {
862         return initialRates;
863     }
864
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.
868     //
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:
874     //
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
893     //
894
895     std::multimap<CAmount, std::pair<CAmount, uint160>> fractionalIn, fractionalOut;
896
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;
899
900     arith_uint256 bigSupply(supply);
901
902     int32_t totalReserveWeight = 0;
903     int32_t maxReserveRatio = 0;
904
905     for (auto weight : weights)
906     {
907         maxReserveRatio = weight > maxReserveRatio ? weight : maxReserveRatio;
908         totalReserveWeight += weight;
909         if (!weight)
910         {
911             LogPrintf("%s: invalid, zero weight currency for conversion\n", __func__);
912             return initialRates;
913         }
914     }
915
916     if (!maxReserveRatio)
917     {
918         LogPrintf("%s: attempting to convert amounts on non-fractional currency\n", __func__);
919         return initialRates;
920     }
921
922     // it is currently an error to have > 100% reserve ratio currency
923     if (totalReserveWeight > bigSatoshi)
924     {
925         LogPrintf("%s: total currency backing weight exceeds 100%\n", __func__);
926         return initialRates;
927     }
928
929     arith_uint256 bigMaxReserveRatio = arith_uint256(maxReserveRatio);
930     arith_uint256 bigTotalReserveWeight = arith_uint256(totalReserveWeight);
931
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++)
936     {
937         arith_uint256 weight(weights[i]);
938         //printf("%s: %ld\n", __func__, ReserveToNative(inputReserves[i], i));
939         CAmount asNative = ReserveToNative(inputReserves[i], i);
940         // if overflow
941         if (asNative < 0)
942         {
943             failed = true;
944             break;
945         }
946         CAmount netFractional = inputFractional[i] - asNative;
947         int64_t deltaRatio;
948         arith_uint256 bigDeltaRatio;
949         if (netFractional > 0)
950         {
951             bigDeltaRatio = ((arith_uint256(netFractional) * bigMaxReserveRatio) / weight);
952             if (bigDeltaRatio > INT64_MAX)
953             {
954                 failed = true;
955                 break;
956             }
957             deltaRatio = bigDeltaRatio.GetLow64();
958             fractionalIn.insert(std::make_pair(deltaRatio, std::make_pair(netFractional, currencies[i])));
959         }
960         else if (netFractional < 0)
961         {
962             netFractional = -netFractional;
963             bigDeltaRatio = ((arith_uint256(netFractional) * bigMaxReserveRatio) / weight);
964             if (bigDeltaRatio > INT64_MAX)
965             {
966                 failed = true;
967                 break;
968             }
969             deltaRatio = bigDeltaRatio.GetLow64();
970             fractionalOut.insert(std::make_pair(deltaRatio, std::make_pair(netFractional, currencies[i])));
971         }
972     }
973
974     if (failed)
975     {
976         LogPrintf("%s: OVERFLOW in calculating changes in currency\n", __func__);
977         return initialRates;
978     }
979
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.
984     //
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();
988
989     CAmount layerAmount = 0;
990     CAmount layerStart;
991
992     for (auto inFIT = fractionalIn.upper_bound(layerAmount); inFIT != fractionalIn.end(); inFIT = fractionalIn.upper_bound(layerAmount))
993     {
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++)
1001         {
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;
1006
1007             if (it->second.first < 0)
1008             {
1009                 LogPrintf("%s: UNDERFLOW in calculating changes in currency\n", __func__);
1010                 return initialRates;
1011             }
1012
1013             fractionalLayersIn[frIdx].first += weight;
1014             fractionalLayersIn[frIdx].second.first += curAmt;
1015             fractionalLayersIn[frIdx].second.second.push_back(it->second.second);
1016         }
1017     }
1018
1019     layerAmount = 0;
1020     for (auto outFIT = fractionalOut.upper_bound(layerAmount); outFIT != fractionalOut.end(); outFIT = fractionalOut.upper_bound(layerAmount))
1021     {
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++)
1028         {
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)
1032             {
1033                 LogPrintf("%s: OVERFLOW in calculating changes in currency\n", __func__);
1034                 return initialRates;
1035             }
1036             CAmount curAmt = bigCurAmt.GetLow64();
1037             it->second.first -= curAmt;
1038             assert(it->second.first >= 0);
1039
1040             fractionalLayersOut[frIdx].first += weight;
1041             fractionalLayersOut[frIdx].second.first += curAmt;
1042             fractionalLayersOut[frIdx].second.second.push_back(it->second.second);
1043         }
1044     }    
1045
1046     int64_t supplyAfterBuy = 0, supplyAfterBuySell = 0, supplyAfterSell = 0, supplyAfterSellBuy = 0;
1047     int64_t reserveAfterBuy = 0, reserveAfterBuySell = 0, reserveAfterSell = 0, reserveAfterSellBuy = 0;
1048
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)
1054     {
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.
1057         //
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);
1064         if (newSupply < 0)
1065         {
1066             LogPrintf("%s: currency supply OVERFLOW\n", __func__);
1067             return initialRates;
1068         }
1069         arith_uint256 bigNewSupply(newSupply);
1070         addSupply += newSupply;
1071         for (auto &id : layer.second.second)
1072         {
1073             auto idIT = fractionalOutMap.find(id);
1074             CAmount newSupplyForCurrency = ((bigNewSupply * weights[reserveMap[id]]) / bigLayerWeight).GetLow64();
1075
1076             // initialize or add to the new supply for this currency
1077             if (idIT == fractionalOutMap.end())
1078             {
1079                 fractionalOutMap[id] = std::make_pair(newSupplyForCurrency, int64_t(0));
1080             }
1081             else
1082             {
1083                 idIT->second.first += newSupplyForCurrency;
1084             }
1085         }
1086     }
1087
1088     supplyAfterBuy = supply + addSupply;
1089     assert(supplyAfterBuy >= 0);
1090
1091     reserveAfterBuy = supply + addNormalizedReserves;
1092     assert(reserveAfterBuy >= 0);
1093
1094     addSupply = 0;
1095     addNormalizedReserves = 0;
1096     CAmount addNormalizedReservesBB = 0, addNormalizedReservesAB = 0;
1097
1098     // calculate sell both before and after buy through this loop
1099     for (auto &layer : fractionalLayersIn)
1100     {
1101         // first calculate sell before-buy, then after-buy
1102         arith_uint256 bigLayerWeight(layer.first);
1103
1104         // before-buy starting point
1105         CAmount totalLayerReservesBB = ((bigSupply * bigLayerWeight) / bigSatoshi).GetLow64() + addNormalizedReservesBB;
1106         CAmount totalLayerReservesAB = ((arith_uint256(supplyAfterBuy) * bigLayerWeight) / bigSatoshi).GetLow64() + addNormalizedReservesAB;
1107
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);
1110
1111         // input fractional is burned and output reserves are removed from reserves
1112         addSupply -= layer.second.first;
1113         addNormalizedReservesBB -= newNormalizedReserveBB;
1114         addNormalizedReservesAB -= newNormalizedReserveAB;
1115
1116         for (auto &id : layer.second.second)
1117         {
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();
1121
1122             // initialize or add to the new supply for this currency
1123             if (idIT == fractionalInMap.end())
1124             {
1125                 fractionalInMap[id] = std::make_pair(newReservesForCurrencyBB, newReservesForCurrencyAB);
1126             }
1127             else
1128             {
1129                 idIT->second.first += newReservesForCurrencyBB;
1130                 idIT->second.second += newReservesForCurrencyAB;
1131             }
1132         }
1133     }
1134
1135     supplyAfterSell = supply + addSupply;
1136     assert(supplyAfterSell >= 0);
1137
1138     supplyAfterBuySell = supplyAfterBuy + addSupply;
1139     assert(supplyAfterBuySell >= 0);
1140
1141     reserveAfterSell = supply + addNormalizedReservesBB;
1142     assert(reserveAfterSell >= 0);
1143
1144     reserveAfterBuySell = reserveAfterBuy + addNormalizedReservesAB;
1145     assert(reserveAfterBuySell >= 0);
1146
1147     addSupply = 0;
1148     addNormalizedReserves = 0;
1149
1150     // now calculate buy after sell
1151     for (auto &layer : fractionalLayersOut)
1152     {
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)
1160         {
1161             auto idIT = fractionalOutMap.find(id);
1162
1163             assert(idIT != fractionalOutMap.end());
1164
1165             idIT->second.second += ((bigNewSupply * weights[reserveMap[id]]) / bigLayerWeight).GetLow64();
1166         }
1167     }
1168
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);
1173
1174     for (int i = 0; i < numCurrencies; i++)
1175     {
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
1179         // currency.
1180         auto fractionalOutIT = fractionalOutMap.find(currencies[i]);
1181         auto fractionalInIT = fractionalInMap.find(currencies[i]);
1182
1183         auto inputReserve = inputReserves[i];
1184         auto inputFraction = inputFractional[i];
1185         reserveSizes[i] = inputReserve;
1186         fractionalSizes[i] = inputFraction;
1187
1188         CAmount fractionDelta = 0, reserveDelta = 0;
1189
1190         if (fractionalOutIT != fractionalOutMap.end())
1191         {
1192             arith_uint256 bigFractionDelta(fractionalOutIT->second.first);
1193             fractionDelta = ((bigFractionDelta + arith_uint256(fractionalOutIT->second.second)) >> 1).GetLow64();
1194             assert(inputFraction + fractionDelta > 0);
1195
1196             fractionalSizes[i] += fractionDelta;
1197             rates[i] = ((arith_uint256(inputReserve) * bigSatoshi) / arith_uint256(fractionalSizes[i])).GetLow64();
1198
1199             // add the new reserve and supply to the currency
1200             newState.supply += fractionDelta;
1201
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];
1204         }
1205         else if (fractionalInIT != fractionalInMap.end())
1206         {
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);
1211
1212             rates[i] = ((arith_uint256(reserveSizes[i]) * bigSatoshi) / arith_uint256(inputFraction)).GetLow64();
1213
1214             // subtract the fractional and reserve that has left the currency
1215             newState.supply -= inputFraction;
1216             newState.reserves[i] -= adjustedReserveDelta;
1217         }
1218     }
1219
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)
1223     {
1224         bool convertRToR = false;
1225         std::vector<CAmount> reservesRToR(numCurrencies, 0);    // keep track of reserve inputs to convert to the fractional currency
1226
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++)
1230         {
1231             // add up all conversion amounts for each fractional to each reserve-to-reserve conversion
1232             for (int j = 0; j < numCurrencies; j++)
1233             {
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])
1238                 {
1239                     convertRToR = true;
1240                     reservesRToR[i] += (*pCrossConversions)[i][j];
1241                 }
1242             }
1243         }
1244
1245         if (convertRToR)
1246         {
1247             std::vector<CAmount> scratchValues(numCurrencies, 0);
1248             std::vector<CAmount> fractionsToConvert(numCurrencies, 0);
1249
1250             // add fractional created to be converted to its destination
1251             for (int i = 0; i < reservesRToR.size(); i++)
1252             {
1253                 if (reservesRToR[i])
1254                 {
1255                     for (int j = 0; j < (*pCrossConversions)[i].size(); j++)
1256                     {
1257                         if ((*pCrossConversions)[i][j])
1258                         {
1259                             fractionsToConvert[j] += ReserveToNativeRaw((*pCrossConversions)[i][j], rates[i]);
1260                         }
1261                     }
1262                 }
1263             }
1264
1265             std::vector<CAmount> _viaPrices;
1266             std::vector<CAmount> &viaPrices(pViaPrices ? *pViaPrices : _viaPrices);
1267             CCurrencyState intermediateState = newState;
1268             viaPrices = intermediateState.ConvertAmounts(scratchValues, fractionsToConvert, newState);
1269         }
1270     }
1271
1272     if (!failed)
1273     {
1274         _newState = newState;
1275     }
1276
1277     for (int i = 0; i < rates.size(); i++)
1278     {
1279         if (!rates[i])
1280         {
1281             rates[i] = PriceInReserve(i);
1282         }
1283     }
1284     return rates;
1285 }
1286
1287 CAmount CCurrencyState::ConvertAmounts(CAmount inputReserve, CAmount inputFraction, CCurrencyState &newState, int32_t reserveIndex) const
1288 {
1289     int32_t numCurrencies = currencies.size();
1290     if (reserveIndex >= numCurrencies)
1291     {
1292         printf("%s: reserve index out of range\n", __func__);
1293         return 0;
1294     }
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,
1300                                                  inputFractional,
1301                                                  newState);
1302     return retVal[reserveIndex];
1303 }
1304
1305 UniValue CReserveInOuts::ToUniValue() const
1306 {
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));
1313     return retVal;
1314 }
1315
1316 UniValue CReserveTransactionDescriptor::ToUniValue() const
1317 {
1318     UniValue retVal(UniValue::VOBJ);
1319     UniValue inOuts(UniValue::VARR);
1320     for (auto &oneInOut : currencies)
1321     {
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);
1326     }
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));
1331     return retVal;
1332 }
1333
1334 void CReserveTransactionDescriptor::AddReserveInput(const uint160 &currency, CAmount value)
1335 {
1336     //printf("adding %ld:%s reserve input\n", value, EncodeDestination(CIdentityID(currency)).c_str());
1337     currencies[currency].reserveIn += value;
1338 }
1339
1340 void CReserveTransactionDescriptor::AddReserveOutput(const uint160 &currency, CAmount value)
1341 {
1342     //printf("adding %ld:%s reserve output\n", value, EncodeDestination(CIdentityID(currency)).c_str());
1343     currencies[currency].reserveOut += value;
1344 }
1345
1346 void CReserveTransactionDescriptor::AddReserveOutConverted(const uint160 &currency, CAmount value)
1347 {
1348     currencies[currency].reserveOutConverted += value;
1349 }
1350
1351 void CReserveTransactionDescriptor::AddNativeOutConverted(const uint160 &currency, CAmount value)
1352 {
1353     currencies[currency].nativeOutConverted += value;
1354 }
1355
1356 void CReserveTransactionDescriptor::AddReserveConversionFees(const uint160 &currency, CAmount value)
1357 {
1358     currencies[currency].reserveConversionFees += value;
1359 }
1360
1361 void CReserveTransactionDescriptor::AddReserveOutput(const CTokenOutput &ro)
1362 {
1363     flags |= IS_RESERVE;
1364     for (auto &oneCur : ro.reserveValues.valueMap)
1365     {
1366         if (oneCur.first != ASSETCHAINS_CHAINID && oneCur.second)
1367         {
1368             AddReserveOutput(oneCur.first, oneCur.second);
1369         }
1370     }
1371 }
1372
1373 void CReserveTransactionDescriptor::AddReserveTransfer(const CReserveTransfer &rt)
1374 {
1375     flags |= IS_RESERVE;
1376     for (auto &oneCur : rt.TotalCurrencyOut().valueMap)
1377     {
1378         if (oneCur.first != ASSETCHAINS_CHAINID && oneCur.second)
1379         {
1380             AddReserveOutput(oneCur.first, oneCur.second);
1381         }
1382     }
1383 }
1384
1385 CAmount CReserveTransactionDescriptor::AllFeesAsNative(const CCurrencyState &currencyState) const
1386 {
1387     CAmount nativeFees = NativeFees();
1388     CCurrencyValueMap reserveFees = ReserveFees();
1389     for (int i = 0; i < currencyState.currencies.size(); i++)
1390     {
1391         auto it = reserveFees.valueMap.find(currencyState.currencies[i]);
1392         if (it != reserveFees.valueMap.end())
1393         {
1394             nativeFees += currencyState.ReserveToNative(it->second, i);
1395         }
1396     }
1397     return nativeFees;
1398 }
1399
1400 CAmount CReserveTransactionDescriptor::AllFeesAsNative(const CCurrencyState &currencyState, const std::vector<CAmount> &exchangeRates) const
1401 {
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++)
1406     {
1407         auto it = reserveFees.valueMap.find(currencyState.currencies[i]);
1408         if (it != reserveFees.valueMap.end())
1409         {
1410             nativeFees += currencyState.ReserveToNativeRaw(it->second, exchangeRates[i]);
1411         }
1412     }
1413     return nativeFees;
1414 }
1415
1416 CCurrencyValueMap CReserveTransactionDescriptor::ReserveFees(const uint160 &nativeID) const
1417 {
1418     uint160 id = nativeID.IsNull() ? ASSETCHAINS_CHAINID : nativeID;
1419     CCurrencyValueMap retFees;
1420     for (auto &one : currencies)
1421     {
1422         // skip native
1423         if (one.first != id)
1424         {
1425             CAmount oneFee = one.second.reserveIn - (one.second.reserveOut - one.second.reserveOutConverted);
1426             if (oneFee)
1427             {
1428                 retFees.valueMap[one.first] = oneFee;
1429             }
1430         }
1431     }
1432     return retFees;
1433 }
1434
1435 CAmount CReserveTransactionDescriptor::NativeFees() const
1436 {
1437     return nativeIn - nativeOut;
1438 }
1439
1440 CCurrencyValueMap CReserveTransactionDescriptor::AllFeesAsReserve(const CCurrencyState &currencyState, int defaultReserve) const
1441 {
1442     CCurrencyValueMap reserveFees = ReserveFees();
1443
1444     auto it = reserveFees.valueMap.find(currencyState.currencies[defaultReserve]);
1445     if (it != reserveFees.valueMap.end())
1446     {
1447         it->second += currencyState.NativeToReserve(NativeFees(), defaultReserve);
1448     }
1449     else
1450     {
1451         reserveFees.valueMap[currencyState.currencies[defaultReserve]] = NativeFees();
1452     }
1453     return reserveFees;
1454 }
1455
1456 CCurrencyValueMap CReserveTransactionDescriptor::AllFeesAsReserve(const CCurrencyState &currencyState, const std::vector<CAmount> &exchangeRates, int defaultReserve) const
1457 {
1458     CCurrencyValueMap reserveFees = ReserveFees();
1459
1460     auto it = reserveFees.valueMap.find(currencyState.currencies[defaultReserve]);
1461     if (it != reserveFees.valueMap.end())
1462     {
1463         it->second += currencyState.NativeToReserveRaw(NativeFees(), exchangeRates[defaultReserve]);
1464     }
1465     else
1466     {
1467         reserveFees.valueMap[currencyState.currencies[defaultReserve]] = NativeFees();
1468     }
1469     return reserveFees;
1470 }
1471
1472 /*
1473  * Checks all structural aspects of the reserve part of a transaction that may have reserve inputs and/or outputs
1474  */
1475 CReserveTransactionDescriptor::CReserveTransactionDescriptor(const CTransaction &tx, const CCoinsViewCache &view, int32_t nHeight) :
1476         flags(0),
1477         ptx(NULL),
1478         numBuys(0),
1479         numSells(0),
1480         numTransfers(0),
1481         nativeIn(0),
1482         nativeOut(0),
1483         nativeConversionFees(0)
1484 {
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
1487
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
1492
1493     // no inputs are valid at height 0
1494     if (!nHeight)
1495     {
1496         flags |= IS_REJECT;
1497         return;
1498     }
1499
1500     int32_t solutionVersion = CConstVerusSolutionVector::activationHeight.ActiveVersion(nHeight);
1501
1502     // reserve descriptor transactions cannot run until identity activates
1503     if (!chainActive.LastTip() || solutionVersion < CConstVerusSolutionVector::activationHeight.ACTIVATE_IDENTITY)
1504     {
1505         return;
1506     }
1507
1508     bool isPBaaS = solutionVersion >= CActivationHeight::ACTIVATE_PBAAS;
1509     bool isPBaaSActivation = CConstVerusSolutionVector::activationHeight.IsActivationHeight(CActivationHeight::ACTIVATE_PBAAS, nHeight);
1510     bool loadedCurrencies = false;
1511
1512     CNameReservation nameReservation;
1513     CIdentity identity;
1514
1515     std::vector<CPBaaSNotarization> notarizations;
1516     CCurrencyValueMap importGeneratedCurrency;
1517
1518     flags |= IS_VALID;
1519
1520     for (int i = 0; i < tx.vout.size(); i++)
1521     {
1522         COptCCParams p;
1523
1524         if (tx.vout[i].scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid())
1525         {
1526             switch (p.evalCode)
1527             {
1528                 case EVAL_IDENTITY_RESERVATION:
1529                 {
1530                     // one name reservation per transaction
1531                     if (p.version < p.VERSION_V3 || !p.vData.size() || nameReservation.IsValid() || !(nameReservation = CNameReservation(p.vData[0])).IsValid())
1532                     {
1533                         flags &= ~IS_VALID;
1534                         flags |= IS_REJECT;
1535                         return;
1536                     }
1537                     if (identity.IsValid())
1538                     {
1539                         if (identity.name == nameReservation.name)
1540                         {
1541                             flags |= IS_IDENTITY_DEFINITION + IS_HIGH_FEE;
1542                         }
1543                         else
1544                         {
1545                             flags &= ~IS_VALID;
1546                             flags |= IS_REJECT;
1547                             return;
1548                         }
1549                     }
1550                 }
1551                 break;
1552
1553                 case EVAL_IDENTITY_PRIMARY:
1554                 {
1555                     // one identity per transaction, unless we are first block coinbase on a PBaaS chain
1556                     // or import
1557                     if (p.version < p.VERSION_V3 ||
1558                         !p.vData.size() ||
1559                         (solutionVersion < CActivationHeight::ACTIVATE_PBAAS && identity.IsValid()) ||
1560                         !(identity = CIdentity(p.vData[0])).IsValid())
1561                     {
1562                         flags &= ~IS_VALID;
1563                         flags |= IS_REJECT;
1564                         return;
1565                     }
1566                     flags |= IS_IDENTITY;
1567                     if (nameReservation.IsValid())
1568                     {
1569                         if (identity.name == nameReservation.name)
1570                         {
1571                             flags |= IS_IDENTITY_DEFINITION + IS_HIGH_FEE;
1572                         }
1573                         else
1574                         {
1575                             flags &= ~IS_VALID;
1576                             flags |= IS_REJECT;
1577                             return;
1578                         }
1579                     }
1580                 }
1581                 break;
1582
1583                 case EVAL_RESERVE_DEPOSIT:
1584                 {
1585                     CReserveDeposit rd;
1586                     if (!p.vData.size() || !(rd = CReserveDeposit(p.vData[0])).IsValid())
1587                     {
1588                         flags &= ~IS_VALID;
1589                         flags |= IS_REJECT;
1590                         return;
1591                     }
1592                     for (auto &oneCur : rd.reserveValues.valueMap)
1593                     {
1594                         if (oneCur.first != ASSETCHAINS_CHAINID)
1595                         {
1596                             AddReserveOutput(oneCur.first, oneCur.second);
1597                         }
1598                     }
1599                 }
1600                 break;
1601
1602                 case EVAL_RESERVE_OUTPUT:
1603                 {
1604                     CTokenOutput ro;
1605                     if (!p.vData.size() || !(ro = CTokenOutput(p.vData[0])).IsValid())
1606                     {
1607                         flags &= ~IS_VALID;
1608                         flags |= IS_REJECT;
1609                         return;
1610                     }
1611                     for (auto &oneCur : ro.reserveValues.valueMap)
1612                     {
1613                         if (oneCur.first != ASSETCHAINS_CHAINID && oneCur.second)
1614                         {
1615                             AddReserveOutput(oneCur.first, oneCur.second);
1616                         }
1617                     }
1618                 }
1619                 break;
1620
1621                 case EVAL_RESERVE_TRANSFER:
1622                 {
1623                     CReserveTransfer rt;
1624                     if (!p.vData.size() || !(rt = CReserveTransfer(p.vData[0])).IsValid())
1625                     {
1626                         flags &= ~IS_VALID;
1627                         flags |= IS_REJECT;
1628                         return;
1629                     }
1630                     AddReserveTransfer(rt);
1631                 }
1632                 break;
1633
1634                 case EVAL_RESERVE_EXCHANGE:
1635                 {
1636                     flags &= ~IS_VALID;
1637                     flags |= IS_REJECT;
1638                     return;
1639                 }
1640                 break;
1641
1642                 case EVAL_CROSSCHAIN_IMPORT:
1643                 {
1644                     if (isPBaaS &&
1645                         nHeight == 1 &&
1646                         tx.IsCoinBase() &&
1647                         !loadedCurrencies)
1648                     {
1649                         // load currencies
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;
1654                         COptCCParams tempP;
1655                         for (int j = 0; j < tx.vout.size(); j++)
1656                         {
1657                             if (tx.vout[j].scriptPubKey.IsPayToCryptoCondition(tempP) &&
1658                                 tempP.IsValid() &&
1659                                 tempP.evalCode == EVAL_CURRENCY_DEFINITION &&
1660                                 tempP.vData.size() &&
1661                                 (oneCurDef = CCurrencyDefinition(tempP.vData[0])).IsValid())
1662                             {
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));
1665                             }
1666                         }
1667                         loadedCurrencies = true;
1668                     }
1669
1670                     CCrossChainImport cci, sysCCI;
1671
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())
1675                     {
1676                         flags &= ~IS_VALID;
1677                         flags |= IS_REJECT;
1678                         return;
1679                     }
1680
1681                     flags |= (IS_IMPORT + IS_HIGH_FEE);
1682
1683                     CCurrencyDefinition importCurrencyDef, sourceSystemDef;
1684                     CCrossChainExport ccx;
1685                     int32_t sysCCIOut;
1686                     notarizations.push_back(CPBaaSNotarization());
1687                     CPBaaSNotarization &importNotarization = notarizations.back();
1688
1689                     int32_t importNotarizationOut;
1690                     int32_t eOutStart, eOutEnd;
1691                     std::vector<CReserveTransfer> importTransfers;
1692
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))
1695                     {
1696                         break;
1697                     }
1698
1699                     if (!cci.IsDefinitionImport())
1700                     {
1701                         if (!cci.GetImportInfo(tx, nHeight, i, ccx, sysCCI, sysCCIOut, importNotarization, importNotarizationOut, eOutStart, eOutEnd, importTransfers))
1702                         {
1703                             flags &= ~IS_VALID;
1704                             flags |= IS_REJECT;
1705                             return;
1706                         }
1707
1708                         importCurrencyDef = ConnectedChains.GetCachedCurrency(cci.importCurrencyID);
1709                         sourceSystemDef = ConnectedChains.GetCachedCurrency(cci.sourceSystemID);
1710
1711                         if (!sourceSystemDef.IsValid() || !importCurrencyDef.IsValid())
1712                         {
1713                             flags &= ~IS_VALID;
1714                             flags |= IS_REJECT;
1715                             return;
1716                         }
1717
1718                         // get the chain definition of the chain we are importing
1719                         std::vector<CTxOut> checkOutputs;
1720                         CCurrencyValueMap importedCurrency, gatewayDeposits, spentCurrencyOut;
1721
1722                         CCoinbaseCurrencyState checkState = importNotarization.currencyState;
1723                         CCoinbaseCurrencyState newState;
1724
1725                         /* if (tx.IsCoinBase())
1726                         {
1727                             printf("%s: currency state before revert: %s\n", __func__, checkState.ToUniValue().write(1,2).c_str());
1728                         }*/
1729
1730                         checkState.RevertReservesAndSupply();
1731                         if (!cci.IsPostLaunch() && cci.IsInitialLaunchImport())
1732                         {
1733                             checkState.SetLaunchClear();
1734                         }
1735
1736                         /*if (tx.IsCoinBase())
1737                         {
1738                             printf("%s: currency state after revert: %s\n", __func__, checkState.ToUniValue().write(1,2).c_str());
1739                         }*/
1740
1741                         CReserveTransactionDescriptor rtxd;
1742                         if (!rtxd.AddReserveTransferImportOutputs(sourceSystemDef,
1743                                                                   ConnectedChains.thisChain,
1744                                                                   importCurrencyDef,
1745                                                                   checkState,
1746                                                                   importTransfers,
1747                                                                   checkOutputs,
1748                                                                   importedCurrency,
1749                                                                   gatewayDeposits,
1750                                                                   spentCurrencyOut,
1751                                                                   &newState))
1752                         {
1753                             flags &= ~IS_VALID;
1754                             flags |= IS_REJECT;
1755                             return;
1756                         }
1757
1758                         /*if (tx.IsCoinBase())
1759                         {
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());
1762                         }*/
1763
1764                         importGeneratedCurrency += importedCurrency;
1765                         if (newState.primaryCurrencyOut)
1766                         {
1767                             importGeneratedCurrency.valueMap[cci.importCurrencyID] = newState.primaryCurrencyOut;
1768                         }
1769                         if (nHeight == 1 && cci.importCurrencyID == ASSETCHAINS_CHAINID)
1770                         {
1771                             importGeneratedCurrency.valueMap[ASSETCHAINS_CHAINID] += gatewayDeposits.valueMap[ASSETCHAINS_CHAINID];
1772                         }
1773
1774                         /*
1775                         printf("%s: importGeneratedCurrency:\n%s\nnewState:\n%s\n", 
1776                                 __func__, 
1777                                 importGeneratedCurrency.ToUniValue().write(1,2).c_str(), 
1778                                 newState.ToUniValue().write(1,2).c_str());
1779                         */
1780
1781                         for (auto &oneOutCur : cci.totalReserveOutMap.valueMap)
1782                         {
1783                             AddReserveOutput(oneOutCur.first, oneOutCur.second);
1784                         }
1785                     }
1786                 }
1787                 break;
1788
1789                 // this check will need to be made complete by preventing mixing both here and where the others
1790                 // are seen
1791                 case EVAL_CROSSCHAIN_EXPORT:
1792                 {
1793                     CCrossChainExport ccx;
1794                     if (!p.vData.size() ||
1795                         !(ccx = CCrossChainExport(p.vData[0])).IsValid())
1796                     {
1797                         flags &= ~IS_VALID;
1798                         flags |= IS_REJECT;
1799                         return;
1800                     }
1801                     //printf("%s: ccx: %s\n", __func__, ccx.ToUniValue().write(1,2).c_str());
1802                     importGeneratedCurrency -= ccx.totalBurned;
1803                     flags |= IS_EXPORT;
1804                 }
1805                 break;
1806
1807                 case EVAL_CURRENCY_DEFINITION:
1808                 {
1809                     CCurrencyDefinition cDef;
1810                     if (!p.vData.size() ||
1811                         !(cDef = CCurrencyDefinition(p.vData[0])).IsValid())
1812                     {
1813                         flags &= ~IS_VALID;
1814                         flags |= IS_REJECT;
1815                         return;
1816                     }
1817                 }
1818                 break;
1819
1820                 default:
1821                 {
1822                     CCurrencyValueMap output = tx.vout[i].scriptPubKey.ReserveOutValue();
1823                     output.valueMap.erase(ASSETCHAINS_CHAINID);
1824                     for (auto &oneOutCur : output.valueMap)
1825                     {
1826                         AddReserveOutput(oneOutCur.first, oneOutCur.second);
1827                     }
1828                 }
1829             }
1830         }
1831         /*if (flags & IS_IMPORT)
1832         {
1833             printf("currencies after proccessing code %d:\n", p.evalCode);
1834             for (auto &oneInOut : currencies)
1835             {
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);
1843             }
1844         }*/
1845     }
1846
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);
1852
1853     if (importGeneratedCurrency.valueMap.count(ASSETCHAINS_CHAINID))
1854     {
1855         nativeIn += importGeneratedCurrency.valueMap[ASSETCHAINS_CHAINID];
1856         importGeneratedCurrency.valueMap.erase(ASSETCHAINS_CHAINID);
1857     }
1858
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();
1862
1863     /* if (flags & IS_IMPORT || flags & IS_EXPORT)
1864     {
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());
1867     } */
1868
1869     for (auto &oneCur : currencies)
1870     {
1871         oneCur.second.reserveIn = 0;
1872     }
1873     if (reservesIn.valueMap.size())
1874     {
1875         flags |= IS_RESERVE;
1876         for (auto &oneCur : reservesIn.valueMap)
1877         {
1878             currencies[oneCur.first].reserveIn = oneCur.second;
1879         }
1880     }
1881     
1882     if (!IsReserve() && ReserveOutputMap().valueMap.size())
1883     {
1884         flags |= IS_RESERVE;
1885     }
1886
1887     ptx = &tx;
1888 }
1889
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
1892 {
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)
1898     {
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))
1902         {
1903             retVal.valueMap[one.first] = isImportCurrency ? one.second.nativeOutConverted : one.second.reserveIn;
1904         }
1905     }
1906     return retVal;
1907 }
1908
1909 CReserveTransfer CReserveTransfer::GetRefundTransfer() const
1910 {
1911     CReserveTransfer rt = *this;
1912     uint160 newDest;
1913
1914     if (rt.IsImportToSource())
1915     {
1916         newDest = rt.FirstCurrency();
1917     }
1918     else
1919     {
1920         newDest = rt.destCurrencyID;
1921     }
1922
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)
1925     {
1926         CIdentity(rt.destination.destination);
1927         rt.destination = CTransferDestination(CTransferDestination::DEST_ID, rt.destination.destination);
1928     }
1929
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);
1936
1937     if (rt.IsMint())
1938     {
1939         rt.flags &= ~CReserveTransfer::MINT_CURRENCY;
1940         rt.reserveValues.valueMap.begin()->second = 0;
1941     }
1942     rt.flags |= rt.REFUND;
1943     rt.destCurrencyID = newDest;
1944     return rt;
1945 }
1946
1947 bool CReserveTransfer::GetTxOut(const CCurrencyValueMap &reserves, int64_t nativeAmount, CTxOut &txOut) const
1948 {
1949     bool makeNormalOutput = true;
1950     if (HasNextLeg())
1951     {
1952         makeNormalOutput = false;
1953         CReserveTransfer nextLegTransfer = CReserveTransfer(CReserveTransfer::VERSION_INVALID);
1954
1955         // if we have a nested transfer, use it
1956         if (destination.TypeNoFlags() == destination.DEST_NESTEDTRANSFER)
1957         {
1958             printf("%s: Nested currency transfers not yet supported\n", __func__);
1959             return false;
1960             // get the reserve transfer from the raw data and
1961             CReserveTransfer rt(destination.destination);
1962             if (rt.IsValid())
1963             {
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;
1970             }
1971         }
1972         else
1973         {
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)
1981             {
1982                 newFlags |= CReserveTransfer::CROSS_SYSTEM;
1983                 CCurrencyValueMap newReserves = reserves;
1984                 if (nativeAmount)
1985                 {
1986                     newReserves.valueMap[ASSETCHAINS_CHAINID] = nativeAmount;
1987                 }
1988                 nextLegTransfer = CReserveTransfer(newFlags,
1989                                                     newReserves,
1990                                                     destination.gatewayID,
1991                                                     destination.fees,
1992                                                     destination.gatewayID,
1993                                                     lastLegDest,
1994                                                     uint160(),
1995                                                     destination.gatewayID);
1996             }
1997             else
1998             {
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;
2002             }
2003         }
2004         if (nextLegTransfer.IsValid())
2005         {
2006             // emit a reserve exchange output
2007             CCcontract_info CC;
2008             CCcontract_info *cp;
2009             cp = CCinit(&CC, EVAL_RESERVE_TRANSFER);
2010             CPubKey pk = CPubKey(ParseHex(CC.CChexstr));
2011
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)));
2015             return true;
2016         }
2017     }
2018     if (makeNormalOutput)
2019     {
2020         // make normal output to the destination, which must be valid
2021         if (!reserves.valueMap.size() && nativeAmount)
2022         {
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)
2028             {
2029                 txOut = CTxOut(nativeAmount, GetScriptForDestination(dest));
2030                 return true;
2031             }
2032         }
2033         else
2034         {
2035             CTxDestination dest = TransferDestinationToDestination(destination);
2036             if (dest.which() == COptCCParams::ADDRTYPE_ID || dest.which() == COptCCParams::ADDRTYPE_PK || dest.which() == COptCCParams::ADDRTYPE_PKH)
2037             {
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)));
2041                 return true;
2042             }
2043         }
2044     }
2045     return false;
2046 }
2047
2048 CReserveTransfer RefundExport(const CBaseChainObject *objPtr)
2049 {
2050     if (objPtr->objectType == CHAINOBJ_RESERVETRANSFER)
2051     {
2052         return ((CChainObject<CReserveTransfer> *)objPtr)->object.GetRefundTransfer();
2053     }
2054     return CReserveTransfer();
2055 }
2056
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)
2071 {
2072     // easy way to refer to return currency state or a dummy without conditionals
2073     CCoinbaseCurrencyState _newCurrencyState;
2074     if (!pNewCurrencyState)
2075     {
2076         pNewCurrencyState = &_newCurrencyState;
2077     }
2078     CCoinbaseCurrencyState &newCurrencyState = *pNewCurrencyState;
2079
2080     // prepare to update ins, outs, emissions, and last pricing
2081     newCurrencyState = importCurrencyState;
2082     newCurrencyState.ClearForNextBlock();
2083
2084     bool isFractional = importCurrencyDef.IsFractional();
2085
2086     // reserve currency amounts converted to fractional
2087     CCurrencyValueMap reserveConverted;
2088
2089     // fractional currency amount and the reserve it is converted to
2090     CCurrencyValueMap fractionalConverted;
2091
2092     CCurrencyValueMap newConvertedReservePool;
2093
2094     std::map<uint160,int32_t> currencyIndexMap = importCurrencyDef.GetCurrenciesMap();
2095
2096     uint160 systemSourceID = systemSource.GetID();
2097     uint160 systemDestID = systemDest.GetID();  // native on destination system
2098     uint160 importCurrencyID = importCurrencyDef.GetID();
2099
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;
2105
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;
2112
2113     // this is cached here, but only used for pre-conversions
2114     CCurrencyValueMap preConvertedOutput;
2115     CCurrencyValueMap preConvertedReserves;
2116     CAmount preAllocTotal = 0;
2117
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)
2123     {
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());
2126         return false;
2127     }
2128     bool isCrossSystemImport = nativeSourceCurrencyID != systemDestID;
2129
2130     nativeIn = 0;
2131     numTransfers = 0;
2132     for (auto &oneInOut : currencies)
2133     {
2134         oneInOut.second.reserveIn = 0;
2135         oneInOut.second.reserveOut = 0;
2136     }
2137
2138     CCcontract_info CC;
2139     CCcontract_info *cp;
2140
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
2144
2145     bool feeOutputStart = false;                        // fee outputs must come after all others, this indicates they have started
2146     int nFeeOutputs = 0;                                // number of fee outputs
2147
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;
2155
2156     for (int i = 0; i <= exportObjects.size(); i++)
2157     {
2158         CReserveTransfer curTransfer;
2159
2160         if (i == exportObjects.size())
2161         {
2162             // this will be the primary fee output
2163             curTransfer = CReserveTransfer(CReserveTransfer::VALID + CReserveTransfer::FEE_OUTPUT,
2164                                            systemDestID,
2165                                            0,
2166                                            systemDestID,
2167                                            0,
2168                                            importCurrencyID,
2169                                            CTransferDestination());
2170         }
2171         else if (importCurrencyState.IsRefunding() ||
2172                  (exportObjects[i].IsPreConversion() && importCurrencyState.IsLaunchCompleteMarker()) ||
2173                  (exportObjects[i].IsConversion() && !exportObjects[i].IsPreConversion() && !importCurrencyState.IsLaunchCompleteMarker()))
2174         {
2175             curTransfer = exportObjects[i].GetRefundTransfer();
2176         }
2177         else
2178         {
2179             curTransfer = exportObjects[i];
2180         }
2181
2182         if (((importCurrencyID != curTransfer.FirstCurrency()) && curTransfer.IsImportToSource()) ||
2183             ((importCurrencyID != curTransfer.destCurrencyID) && !curTransfer.IsImportToSource()))
2184         {
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__);
2187             return false;
2188         }
2189
2190         //printf("currency transfer #%d:\n%s\n", i, curTransfer.ToUniValue().write(1,2).c_str());
2191         CCurrencyDefinition _currencyDest;
2192         const CCurrencyDefinition &currencyDest = curTransfer.IsRefund() ?
2193                                                     (_currencyDest = ConnectedChains.GetCachedCurrency(curTransfer.FirstCurrency())) :
2194                                                     (importCurrencyID == curTransfer.destCurrencyID) ?
2195                                                     importCurrencyDef :
2196                                                     (_currencyDest = ConnectedChains.GetCachedCurrency(curTransfer.destCurrencyID));
2197
2198         if (!currencyDest.IsValid())
2199         {
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());
2202             return false;
2203         }
2204
2205         //printf("%s: transferFees: %s\n", __func__, transferFees.ToUniValue().write(1,2).c_str());
2206
2207         if (i == exportObjects.size() || curTransfer.IsValid())
2208         {
2209             CTxOut newOut;
2210
2211             // at the end, make our fee outputs
2212             if (i == exportObjects.size())
2213             {
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())
2219                 {
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)
2225                     {
2226                         // this fee input was injected into the currency at definition
2227                         importedCurrency.valueMap[importCurrencyDef.launchSystemID] += currencyRegistrationFee;
2228                         AddReserveInput(importCurrencyDef.launchSystemID, currencyRegistrationFee);
2229                     }
2230                     else
2231                     {
2232                         if (importCurrencyDef.systemID != systemDestID && importCurrencyState.IsRefunding())
2233                         {
2234                             gatewayDepositsIn.valueMap[systemDestID] += currencyRegistrationFee;
2235                         }
2236                         nativeIn += currencyRegistrationFee;
2237                     }
2238
2239                     if (importCurrencyState.IsLaunchConfirmed())
2240                     {
2241                         if (importCurrencyState.IsPrelaunch())
2242                         {
2243                             // first time with launch clear on prelaunch, start supply at initial supply
2244                             newCurrencyState.supply = newCurrencyState.initialSupply;
2245                         }
2246
2247                         // if we have finished importing all pre-launch exports, create all pre-allocation outputs
2248                         for (auto &onePreAlloc : importCurrencyDef.preAllocation)
2249                         {
2250                             // we need to make one output for each pre-allocation
2251                             AddNativeOutConverted(importCurrencyID, onePreAlloc.second);
2252                             if (importCurrencyID != systemDestID)
2253                             {
2254                                 AddReserveOutConverted(importCurrencyID, onePreAlloc.second);
2255                             }
2256                             
2257                             preAllocTotal += onePreAlloc.second;
2258
2259                             std::vector<CTxDestination> dests;
2260                             if (onePreAlloc.first.IsNull())
2261                             {
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)});
2264                             }
2265                             else
2266                             {
2267                                 dests = std::vector<CTxDestination>({CTxDestination(CIdentityID(onePreAlloc.first))});
2268                             }
2269
2270                             if (importCurrencyID == systemDestID)
2271                             {
2272                                 vOutputs.push_back(CTxOut(onePreAlloc.second, GetScriptForDestination(dests[0])));
2273                                 nativeOut += onePreAlloc.second;
2274                             }
2275                             else
2276                             {
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))));
2280                             }
2281                         }
2282                         if (importCurrencyDef.gatewayConverterIssuance)
2283                         {
2284                             if (importCurrencyDef.IsPBaaSChain())
2285                             {
2286                                 preAllocTotal += importCurrencyDef.gatewayConverterIssuance;
2287                                 AddNativeOutConverted(importCurrencyID, importCurrencyDef.gatewayConverterIssuance);
2288                                 nativeOut += importCurrencyDef.gatewayConverterIssuance;
2289                             }
2290                         }
2291                     }
2292                 }
2293
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();
2297
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;
2303
2304                 if (importCurrencyState.IsLaunchConfirmed() &&
2305                     isFractional &&
2306                     importCurrencyState.reserves[systemDestIdx])
2307                 {
2308                     // 1/2 of all conversion fees go directly into the fractional currency itself
2309                     liquidityFees = conversionFees / 2;
2310                     transferFees -= liquidityFees;
2311
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;
2315
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)
2318                     {
2319                         // only convert through "via" if we are going from one reserve to the system ID
2320                         if (oneFee.first != importCurrencyID && oneFee.first != systemDestID)
2321                         {
2322                             auto curIt = currencyIndexMap.find(oneFee.first);
2323                             if (curIt == currencyIndexMap.end())
2324                             {
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());
2327                                 return false;
2328                             }
2329                             int curIdx = curIt->second;
2330
2331                             // printf("%s: *this 1: %s\n", __func__, ToUniValue().write(1,2).c_str());
2332
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);
2340
2341                             if (systemDestID == importCurrencyID)
2342                             {
2343                                 AddNativeOutConverted(oneFee.first, oneFeeValue);
2344                                 totalNativeFee += oneFeeValue;
2345                             }
2346                             else
2347                             {
2348                                 // if fractional currency is not native, one more conversion to native
2349                                 oneFeeValue = 
2350                                     CCurrencyState::NativeToReserveRaw(oneFeeValue, importCurrencyState.viaConversionPrice[systemDestIdx]);
2351                                 totalNativeFee += oneFeeValue;
2352                                 newConvertedReservePool.valueMap[systemDestID] += oneFeeValue;
2353                                 AddReserveOutConverted(systemDestID, oneFeeValue);
2354                             }
2355
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());
2359                         }
2360                         else if (oneFee.first == systemDestID)
2361                         {
2362                             totalNativeFee += oneFee.second;
2363                         }
2364                         else if (oneFee.first == importCurrencyID)
2365                         {
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);
2370
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)));
2377                         }
2378                     }
2379                     // loop through, subtract "from" and add "to"
2380                     convertedFees = transferFees;
2381                     if (feeConversions.size())
2382                     {
2383                         for (auto &conversionPairs : feeConversions)
2384                         {
2385                             convertedFees.valueMap[conversionPairs.first.first] -= conversionPairs.first.second;
2386                             convertedFees.valueMap[conversionPairs.second.first] += conversionPairs.second.second;
2387                         }
2388                         convertedFees = convertedFees.CanonicalMap();
2389                     }
2390                 }
2391                 else
2392                 {
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"))
2398                     {
2399                         addr = DecodeDestination(mapArgs["-mineraddress"]);
2400                     }
2401                     else if (!VERUS_NOTARYID.IsNull())
2402                     {
2403                         addr = VERUS_NOTARYID;
2404                     }
2405                     else if (!VERUS_DEFAULTID.IsNull())
2406                     {
2407                         addr = VERUS_DEFAULTID;
2408                     }
2409                     else if (!VERUS_NODEID.IsNull())
2410                     {
2411                         addr = CIdentityID(VERUS_NODEID);
2412                     }
2413                     else if (!NOTARY_PUBKEY.empty())
2414                     {
2415                         CPubKey pkey;
2416                         std::vector<unsigned char> hexKey = ParseHex(NOTARY_PUBKEY);
2417                         pkey.Set(hexKey.begin(), hexKey.end());
2418                         addr = pkey.GetID();
2419                     }
2420
2421                     std::vector<CTxDestination> dests({addr});
2422                     for (auto &oneFee : transferFees.valueMap)
2423                     {
2424                         if (oneFee.first != systemDestID && oneFee.first != VERUS_CHAINID && oneFee.second)
2425                         {
2426                             CAmount resExportFee = CCrossChainExport::CalculateExportFeeRaw(oneFee.second, numTransfers);
2427                             CAmount exportSplit = CCrossChainExport::ExportReward(resExportFee);
2428                             AddReserveOutput(oneFee.first, oneFee.second);
2429
2430                             CTokenOutput ro = CTokenOutput(oneFee.first, oneFee.second);
2431                             //vOutputs.push_back(CTxOut(0, MakeMofNCCScript(CConditionObj<CTokenOutput>(EVAL_RESERVE_OUTPUT, dests, 1, &ro))));
2432                         }
2433                         else if (oneFee.second)
2434                         {
2435                             if (oneFee.first == systemDestID)
2436                             {
2437                                 totalNativeFee += oneFee.second;
2438                             }
2439                             else if (importCurrencyState.IsLaunchConfirmed() && oneFee.first == VERUS_CHAINID)
2440                             {
2441                                 totalVerusFee += oneFee.second;
2442                             }
2443                         }
2444                     }
2445                     convertedFees = transferFees;
2446                 }
2447
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);
2452
2453                 for (auto &oneFee : convertedFees.valueMap)
2454                 {
2455                     if (oneFee.first == systemDestID)
2456                     {
2457                         nativeOut += oneFee.second;
2458                     }
2459                     else
2460                     {
2461                         AddReserveOutput(oneFee.first, oneFee.second);
2462                     }
2463                 }
2464
2465                 if (!curTransfer.destination.IsValid() || !exporterReward)
2466                 {
2467                     break;
2468                 }
2469                 curTransfer = CReserveTransfer(CReserveTransfer::VALID + CReserveTransfer::FEE_OUTPUT,
2470                                                systemDestID, exporterReward, systemDestID, 0, systemDestID, curTransfer.destination);
2471             }
2472             else
2473             {
2474                 numTransfers++;
2475
2476                 CAmount explicitFees = curTransfer.nFees;
2477                 transferFees.valueMap[curTransfer.feeCurrencyID] += explicitFees;
2478
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)
2483                 {
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)
2488                     {
2489                         explicitFees += curTransfer.destination.fees;
2490                     }
2491
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;
2495
2496                     // if it's already in the correct currency, nothing to do, otherwise convert if we can
2497                     if (curTransfer.feeCurrencyID != nextDestSysID)
2498                     {
2499                         if (!isFractional ||
2500                             !currencyIndexMap.count(nextDestSysID) ||
2501                             !currencyIndexMap.count(curTransfer.feeCurrencyID))
2502                         {
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());
2505                             return false;
2506                         }
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
2511
2512                         CAmount oneFeeValue = 0;
2513                         int feeCurIdx = currencyIndexMap[curTransfer.feeCurrencyID];
2514                         int nextDestIdx = currencyIndexMap[nextDestSysID];
2515
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;
2519
2520                         AddReserveConversionFees(curTransfer.feeCurrencyID, passThroughFee);
2521
2522                         transferFees.valueMap[curTransfer.feeCurrencyID] += passThroughFee;
2523
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]);
2530
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;
2535
2536                         AddReserveOutput(nextDestSysID, reserveFromFrac);
2537                         AddReserveOutConverted(nextDestSysID, reserveFromFrac);
2538                     } else
2539                     {
2540                         if (curTransfer.feeCurrencyID == systemDestID)
2541                         {
2542                             nativeOut = curTransfer.destination.fees;
2543                         }
2544                         else
2545                         {
2546                             AddReserveOutput(nextDestSysID, curTransfer.destination.fees);
2547                         }
2548                     }
2549                 }
2550
2551                 if (curTransfer.feeCurrencyID == systemDestID)
2552                 {
2553                     nativeIn += explicitFees;
2554                 }
2555                 else
2556                 {
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);
2559                 }
2560
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)
2564                 {
2565                     uint160 inputID = curTransfer.FirstCurrency();
2566                     CAmount totalCurrencyInput = curTransfer.FirstValue();
2567
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
2570
2571                     // source system currency is imported, dest system must come from deposits
2572                     if (curTransfer.feeCurrencyID == systemSourceID)
2573                     {
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)))
2578                         {
2579                             printf("%s: currency transfer fees invalid for receiving system\n", __func__);
2580                             LogPrintf("%s: currency transfer fees invalid for receiving system\n", __func__);
2581                             return false;
2582                         }
2583                         if (importCurrencyState.IsRefunding())
2584                         {
2585                             gatewayDepositsIn.valueMap[systemSourceID] += explicitFees;
2586                         }
2587                         else
2588                         {
2589                             importedCurrency.valueMap[systemSourceID] += explicitFees;
2590                         }
2591                     }
2592                     else if (curTransfer.feeCurrencyID == systemDestID)
2593                     {
2594                         gatewayDepositsIn.valueMap[systemDestID] += explicitFees;
2595                     }
2596                     else if (!(curTransfer.feeCurrencyID == curTransfer.FirstCurrency() && 
2597                               isFractional && 
2598                               currencyIndexMap.count(curTransfer.feeCurrencyID) &&
2599                               importCurrencyState.IsLaunchConfirmed()))
2600                     {
2601                         printf("%s: pass-through fees invalid\n", __func__);
2602                         LogPrintf("%s: pass-through fees invalid\n", __func__);
2603                         return false;
2604                     }   
2605
2606                     CCurrencyDefinition inputDef = ConnectedChains.GetCachedCurrency(inputID);
2607                     if (!inputDef.IsValid())
2608                     {
2609                         printf("%s: Invalid or unregistered currency for import from %s\n", __func__, curTransfer.ToUniValue().write().c_str());
2610                         return false;
2611                     }
2612                     if (curTransfer.IsMint())
2613                     {
2614                         printf("%s: Invalid mint operation from %s\n", __func__, curTransfer.ToUniValue().write().c_str());
2615                         return false;
2616                     }
2617
2618                     if (totalCurrencyInput)
2619                     {
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)))
2624                         {
2625                             importedCurrency.valueMap[inputID] += totalCurrencyInput;
2626                         }
2627                         else
2628                         {
2629                             gatewayDepositsIn.valueMap[inputID] += totalCurrencyInput;
2630                         }
2631
2632                         if (inputID == systemDestID)
2633                         {
2634                             nativeIn += totalCurrencyInput;
2635                         }
2636                         else
2637                         {
2638                             AddReserveInput(inputID, totalCurrencyInput);
2639                         }
2640                     }
2641                 }
2642                 else 
2643                 {
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())
2649                     {
2650                         feeCurrency = curTransfer.nFees ? curTransfer.feeCurrencyID : curTransfer.FirstCurrency();
2651                         feeEquivalent = CReserveTransactionDescriptor::CalculateConversionFee(curTransfer.FirstValue()) + curTransfer.nFees;
2652                     }
2653                     else
2654                     {
2655                         feeCurrency = curTransfer.feeCurrencyID;
2656                         feeEquivalent = curTransfer.nFees;
2657                     }
2658
2659                     if (feeCurrency != systemDestID)
2660                     { 
2661                         if (!importCurrencyDef.IsFractional() || !(currencyIndexMap.count(feeCurrency) || feeCurrency == importCurrencyID))
2662                         {
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());
2665                             return false;
2666                         }
2667                         if (curTransfer.feeCurrencyID != importCurrencyID)
2668                         {
2669                             feeEquivalent = importCurrencyState.ReserveToNativeRaw(feeEquivalent, importCurrencyState.conversionPrice[currencyIndexMap[feeCurrency]]);
2670                         }
2671                         feeEquivalent = importCurrencyState.NativeToReserveRaw(feeEquivalent, importCurrencyState.viaConversionPrice[systemDestIdx]);
2672                     }
2673
2674                     if (feeEquivalent < curTransfer.CalculateTransferFee())
2675                     {
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());
2678                         return false;
2679                     }
2680
2681                     if (curTransfer.FirstCurrency() == systemDestID && !curTransfer.IsMint())
2682                     {
2683                         nativeIn += curTransfer.FirstValue();
2684                     }
2685                     else
2686                     {
2687                         if (curTransfer.IsMint())
2688                         {
2689                             AddReserveInput(curTransfer.destCurrencyID, curTransfer.FirstValue());
2690                         }
2691                         else
2692                         {
2693                             AddReserveInput(curTransfer.FirstCurrency(), curTransfer.FirstValue());
2694                         }
2695                     }
2696                 }
2697             }
2698
2699             if (curTransfer.IsPreConversion())
2700             {
2701                 // pre-conversions can only come from our launch system
2702                 if (importCurrencyDef.launchSystemID != systemSourceID)
2703                 {
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());
2706                     return false;
2707                 }
2708
2709                 if (importCurrencyState.IsLaunchCompleteMarker())
2710                 {
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());
2713                     return false;
2714                 }
2715
2716                 uint160 convertFromID = curTransfer.FirstCurrency();
2717
2718                 // either the destination currency must be fractional or the source currency
2719                 // must be native
2720                 if (!isFractional && convertFromID != importCurrencyDef.launchSystemID)
2721                 {
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());
2724                     return false;
2725                 }
2726
2727                 // get currency index
2728                 auto curIndexIt = currencyIndexMap.find(convertFromID);
2729                 if (curIndexIt == currencyIndexMap.end())
2730                 {
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());
2733                     return false;
2734                 }
2735                 int curIdx = curIndexIt->second;
2736
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();
2742
2743                 preConversionFee = CalculateConversionFee(curTransfer.FirstValue());
2744                 if (preConversionFee > curTransfer.FirstValue())
2745                 {
2746                     preConversionFee = curTransfer.FirstValue();
2747                 }
2748
2749                 valueOut -= preConversionFee;
2750
2751                 AddReserveConversionFees(curTransfer.FirstCurrency(), preConversionFee);
2752                 transferFees.valueMap[curTransfer.FirstCurrency()] +=  preConversionFee;
2753
2754                 newCurrencyConverted = importCurrencyState.ReserveToNativeRaw(valueOut, importCurrencyState.conversionPrice[curIdx]);
2755
2756                 if (newCurrencyConverted == -1)
2757                 {
2758                     // if we have an overflow, this isn't going to work
2759                     newCurrencyConverted = 0;
2760                 }
2761
2762                 if (newCurrencyConverted)
2763                 {
2764                     uint160 firstCurID = curTransfer.FirstCurrency();
2765                     reserveConverted.valueMap[firstCurID] += valueOut;
2766                     preConvertedReserves.valueMap[firstCurID] += valueOut;
2767                     if (isFractional && isCrossSystemImport && importedCurrency.valueMap.count(firstCurID))
2768                     {
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;
2774                     }
2775
2776                     if (totalCarveOut > 0 && totalCarveOut < SATOSHIDEN)
2777                     {
2778                         CAmount newReserveIn = CCurrencyState::NativeToReserveRaw(valueOut, SATOSHIDEN - totalCarveOut);
2779                         totalCarveOuts.valueMap[curTransfer.FirstCurrency()] += valueOut - newReserveIn;
2780                         valueOut = newReserveIn;
2781                     }
2782
2783                     if (curTransfer.FirstCurrency() != systemDestID)
2784                     {
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
2787                         if (!isFractional)
2788                         {
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))));
2793                         }
2794                     }
2795                     else
2796                     {
2797                         // if it is not fractional, send proceeds to currency ID, else leave it in reserve deposit
2798                         if (!isFractional)
2799                         {
2800                             nativeOut += valueOut;
2801                             vOutputs.push_back(CTxOut(valueOut, GetScriptForDestination(CIdentityID(importCurrencyID))));
2802                         }
2803                     }
2804
2805                     preConvertedOutput.valueMap[curTransfer.FirstCurrency()] += newCurrencyConverted;
2806                     AddNativeOutConverted(curTransfer.FirstCurrency(), newCurrencyConverted);
2807                     AddNativeOutConverted(curTransfer.destCurrencyID, newCurrencyConverted);
2808                     if (curTransfer.destCurrencyID == systemDestID)
2809                     {
2810                         nativeOut += newCurrencyConverted;
2811                         if (!importCurrencyState.IsLaunchConfirmed())
2812                         {
2813                             nativeIn += newCurrencyConverted;
2814                         }
2815                         curTransfer.GetTxOut(CCurrencyValueMap(), newCurrencyConverted, newOut);
2816                     }
2817                     else // all conversions are to primary currency
2818                     {
2819                         AddReserveOutConverted(curTransfer.destCurrencyID, newCurrencyConverted);
2820                         AddReserveOutput(curTransfer.destCurrencyID, newCurrencyConverted);
2821                         if (!importCurrencyState.IsLaunchConfirmed())
2822                         {
2823                             AddReserveInput(curTransfer.destCurrencyID, newCurrencyConverted);
2824                         }
2825                         curTransfer.GetTxOut(CCurrencyValueMap(std::vector<uint160>({curTransfer.destCurrencyID}), std::vector<int64_t>({newCurrencyConverted})), 
2826                                              0, newOut);
2827                     }
2828                 }
2829             }
2830             else if (curTransfer.IsConversion())
2831             {
2832                 if (curTransfer.FirstCurrency() == curTransfer.destCurrencyID)
2833                 {
2834                     printf("%s: Conversion does not specify two currencies\n", __func__);
2835                     LogPrintf("%s: Conversion does not specify two currencies\n", __func__);
2836                     return false;
2837                 }
2838
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());
2845
2846                 CCurrencyDefinition sourceCurrency = ConnectedChains.GetCachedCurrency(curTransfer.FirstCurrency());
2847
2848                 if (!sourceCurrency.IsValid())
2849                 {
2850                     printf("%s: Currency specified for conversion not found\n", __func__);
2851                     LogPrintf("%s: Currency specified for conversion not found\n", __func__);
2852                     return false;
2853                 }
2854
2855                 if (!(toFractional || 
2856                     (importCurrencyID == curTransfer.FirstCurrency() &&
2857                         sourceCurrency.IsFractional() &&
2858                         currencyIndexMap.count(curTransfer.destCurrencyID))))
2859                 {
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__);
2862                     return false;
2863                 }
2864
2865                 if (curTransfer.IsReserveToReserve() &&
2866                     (!toFractional ||
2867                         curTransfer.secondReserveID.IsNull() ||
2868                         curTransfer.secondReserveID == curTransfer.FirstCurrency() ||
2869                         !currencyIndexMap.count(curTransfer.secondReserveID)))
2870                 {
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());
2873                     return false;
2874                 }
2875
2876                 const CCurrencyDefinition &fractionalCurrency = toFractional ? currencyDest : sourceCurrency;
2877                 const CCurrencyDefinition &reserveCurrency = toFractional ? sourceCurrency : currencyDest;
2878                 int reserveIdx = currencyIndexMap[reserveCurrency.GetID()];
2879
2880                 assert(fractionalCurrency.IsValid() && 
2881                         reserveCurrency.IsValid() && 
2882                         fractionalCurrency.currencies[reserveIdx] == reserveCurrency.GetID());
2883
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;
2891
2892                 if (!(curTransfer.flags & curTransfer.FEE_OUTPUT))
2893                 {
2894                     oneConversionFee = CalculateConversionFee(curTransfer.FirstValue());
2895                     if (curTransfer.IsReserveToReserve())
2896                     {
2897                         oneConversionFee <<= 1;
2898                     }
2899                     if (oneConversionFee > curTransfer.FirstValue())
2900                     {
2901                         oneConversionFee = curTransfer.FirstValue();
2902                     }
2903                     valueOut -= oneConversionFee;
2904                     AddReserveConversionFees(curTransfer.FirstCurrency(), oneConversionFee);
2905                     transferFees.valueMap[curTransfer.FirstCurrency()] += oneConversionFee;
2906                 }
2907
2908                 if (toFractional)
2909                 {
2910                     reserveConverted.valueMap[curTransfer.FirstCurrency()] += valueOut;
2911                     newCurrencyConverted = importCurrencyState.ReserveToNativeRaw(valueOut, importCurrencyState.conversionPrice[reserveIdx]);
2912                 }
2913                 else
2914                 {
2915                     fractionalConverted.valueMap[curTransfer.destCurrencyID] += valueOut;
2916                     newCurrencyConverted = importCurrencyState.NativeToReserveRaw(valueOut, importCurrencyState.conversionPrice[reserveIdx]);
2917                 }
2918
2919                 if (newCurrencyConverted)
2920                 {
2921                     uint160 outputCurrencyID;
2922
2923                     if (curTransfer.IsReserveToReserve())
2924                     {
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;
2933                     }
2934                     else
2935                     {
2936                         outputCurrencyID = curTransfer.destCurrencyID;
2937                     }
2938
2939                     if (toFractional && !curTransfer.IsReserveToReserve())
2940                     {
2941                         AddNativeOutConverted(curTransfer.FirstCurrency(), newCurrencyConverted);
2942                         AddNativeOutConverted(curTransfer.destCurrencyID, newCurrencyConverted);
2943                         if (curTransfer.destCurrencyID == systemDestID)
2944                         {
2945                             nativeOut += newCurrencyConverted;
2946                         }
2947                         else
2948                         {
2949                             AddReserveOutConverted(curTransfer.destCurrencyID, newCurrencyConverted);
2950                             AddReserveOutput(curTransfer.destCurrencyID, newCurrencyConverted);
2951                         }
2952                     }
2953                     else
2954                     {
2955                         AddReserveOutConverted(outputCurrencyID, newCurrencyConverted);
2956                         newConvertedReservePool.valueMap[outputCurrencyID] += newCurrencyConverted;
2957                         if (outputCurrencyID == systemDestID)
2958                         {
2959                             nativeOut += newCurrencyConverted;
2960                         }
2961                         else
2962                         {
2963                             AddReserveOutput(outputCurrencyID, newCurrencyConverted);
2964                         }
2965
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())
2970                         {
2971                             AddNativeOutConverted(importCurrencyID, -valueOut);
2972                         }
2973                     }
2974
2975                     if (outputCurrencyID == systemDestID)
2976                     {
2977                         curTransfer.GetTxOut(CCurrencyValueMap(), newCurrencyConverted, newOut);
2978                     }
2979                     else
2980                     {
2981                         curTransfer.GetTxOut(CCurrencyValueMap(std::vector<uint160>({outputCurrencyID}), std::vector<int64_t>({newCurrencyConverted})), 
2982                                              0, newOut);
2983                     }
2984                 }
2985             }
2986             else
2987             {
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())
2991                 {
2992                     // if the source is fractional currency, it is burned
2993                     if (curTransfer.FirstCurrency() != importCurrencyID || !(isFractional || importCurrencyDef.IsToken()))
2994                     {
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());
2998                         return false;
2999                     }
3000                     if (curTransfer.flags & curTransfer.IsBurnChangeWeight())
3001                     {
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());
3004                         return false;
3005                     }
3006                     // burn the input fractional currency
3007                     AddNativeOutConverted(curTransfer.FirstCurrency(), -curTransfer.FirstValue());
3008                     burnedChangePrice += curTransfer.FirstValue();
3009                 }
3010                 else if (!curTransfer.IsMint() && systemDestID == curTransfer.FirstCurrency())
3011                 {
3012                     nativeOut += curTransfer.FirstValue();
3013                     if (!curTransfer.GetTxOut(CCurrencyValueMap(), curTransfer.FirstValue(), newOut) || newOut.nValue == -1)
3014                     {
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());
3017                         return false;
3018                     }
3019                 }
3020                 else
3021                 {
3022                     // if this is a minting of currency
3023                     // this is used for both pre-allocation and also centrally, algorithmically, or externally controlled currencies
3024                     uint160 destCurID;
3025                     if (curTransfer.IsMint() && curTransfer.destCurrencyID == importCurrencyID)
3026                     {
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)
3032                         {
3033                             AddReserveOutConverted(curTransfer.destCurrencyID, curTransfer.FirstValue());
3034                         }
3035                     }
3036                     else
3037                     {
3038                         destCurID = curTransfer.FirstCurrency();
3039                     }
3040                     AddReserveOutput(destCurID, curTransfer.FirstValue());
3041                     curTransfer.GetTxOut(CCurrencyValueMap(std::vector<uint160>({destCurID}), std::vector<int64_t>({curTransfer.FirstValue()})), 
3042                                             0, newOut);
3043                 }
3044             }
3045             if (newOut.nValue < 0)
3046             {
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());
3049             }
3050             else
3051             {
3052                 vOutputs.push_back(newOut);
3053             }
3054         }
3055         else
3056         {
3057             printf("%s: Invalid reserve transfer on export\n", __func__);
3058             LogPrintf("%s: Invalid reserve transfer on export\n", __func__);
3059             return false;
3060         }
3061     }
3062
3063     if (importCurrencyState.IsRefunding())
3064     {
3065         importedCurrency = CCurrencyValueMap();
3066     }
3067     else if ((totalCarveOuts = totalCarveOuts.CanonicalMap()).valueMap.size())
3068     {
3069         // add carveout outputs
3070         for (auto &oneCur : totalCarveOuts.valueMap)
3071         {
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)
3074             {
3075                 nativeOut += oneCur.second;
3076                 vOutputs.push_back(CTxOut(oneCur.second, GetScriptForDestination(CIdentityID(importCurrencyID))));
3077             }
3078             else
3079             {
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))));
3086             }
3087         }
3088     }
3089
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;
3095
3096     // remove burned currency from supply
3097     //
3098     // check to see if liquidity fees include currency to burn and burn if so
3099     if (liquidityFees.valueMap.count(importCurrencyID))
3100     {
3101         CAmount primaryLiquidityFees = liquidityFees.valueMap[importCurrencyID];
3102         newCurrencyState.primaryCurrencyOut -= primaryLiquidityFees;
3103         liquidityFees.valueMap.erase(importCurrencyID);
3104     }
3105     if (burnedChangePrice > 0)
3106     {
3107         if (!(burnedChangePrice <= newCurrencyState.supply))
3108         {
3109             printf("%s: Invalid burn amount %ld\n", __func__, burnedChangePrice);
3110             LogPrintf("%s: Invalid burn amount %ld\n", __func__, burnedChangePrice);
3111             return false;
3112         }
3113         newCurrencyState.supply -= burnedChangePrice;
3114     }
3115
3116     CCurrencyValueMap adjustedReserveConverted = reserveConverted - preConvertedReserves;
3117
3118     if (isFractional && newCurrencyState.IsLaunchConfirmed())
3119     {
3120         CCoinbaseCurrencyState scratchCurrencyState = importCurrencyState;
3121
3122         if (scratchCurrencyState.IsPrelaunch() && preConvertedReserves > CCurrencyValueMap())
3123         {
3124             // add all pre-converted reserves before calculating pricing for fee conversions
3125             for (auto &oneReserve : preConvertedReserves.valueMap)
3126             {
3127                 if (oneReserve.second)
3128                 {
3129                     scratchCurrencyState.reserves[currencyIndexMap[oneReserve.first]] += oneReserve.second;
3130                 }
3131             }
3132         }
3133
3134         if (importCurrencyState.IsLaunchClear() && importCurrencyState.IsLaunchConfirmed())
3135         {
3136             if (totalCarveOut)
3137             {
3138                 scratchCurrencyState.ApplyCarveouts(totalCarveOut);
3139             }
3140             if (importCurrencyDef.preLaunchDiscount)
3141             {
3142                 scratchCurrencyState.ApplyCarveouts(importCurrencyDef.preLaunchDiscount);
3143             }
3144         }
3145
3146         if (adjustedReserveConverted.CanonicalMap().valueMap.size() || fractionalConverted.CanonicalMap().valueMap.size())
3147         {
3148             CCurrencyState dummyCurState;
3149             std::vector<int64_t> newPrices =
3150                 scratchCurrencyState.ConvertAmounts(adjustedReserveConverted.AsCurrencyVector(importCurrencyState.currencies),
3151                                                     fractionalConverted.AsCurrencyVector(importCurrencyState.currencies),
3152                                                     dummyCurState,
3153                                                     &crossConversions,
3154                                                     &newCurrencyState.viaConversionPrice);
3155             if (!dummyCurState.IsValid())
3156             {
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());
3159                 return false;
3160             }
3161             if (!newCurrencyState.IsLaunchCompleteMarker())
3162             {
3163                 // make viaconversion prices the dynamic prices and conversion prices remain initial pricing
3164                 for (int i = 0; i < newPrices.size(); i++)
3165                 {
3166                     if (i != systemDestIdx)
3167                     {
3168                         newCurrencyState.viaConversionPrice[i] = newPrices[i];
3169                     }
3170                 }
3171             }
3172             else
3173             {
3174                 newCurrencyState.conversionPrice = newPrices;
3175             }
3176         }
3177     }
3178
3179     if (newCurrencyState.IsPrelaunch())
3180     {
3181         adjustedReserveConverted = reserveConverted;
3182     }
3183
3184     newCurrencyState.preConvertedOut = 0;
3185     if (!newCurrencyState.IsRefunding())
3186     {
3187         for (auto &oneVal : preConvertedOutput.valueMap)
3188         {
3189             newCurrencyState.preConvertedOut += oneVal.second;
3190             if (newCurrencyState.IsLaunchConfirmed() && !isFractional)
3191             {
3192                 newCurrencyState.supply += oneVal.second;
3193             }
3194         }
3195     }
3196
3197     std::vector<CAmount> vResConverted;
3198     std::vector<CAmount> vResOutConverted;
3199     std::vector<CAmount> vFracConverted;
3200     std::vector<CAmount> vFracOutConverted;
3201
3202     CCurrencyValueMap reserveBalanceInMap;
3203
3204     // liquidity fees that are in the import currency are burned above
3205     std::vector<CAmount> vLiquidityFees = liquidityFees.AsCurrencyVector(newCurrencyState.currencies);
3206
3207     if (newCurrencyState.IsLaunchConfirmed())
3208     {
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++)
3215         {
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;
3220             if (newReservesIn)
3221             {
3222                 reserveBalanceInMap.valueMap[newCurrencyState.currencies[i]] = newReservesIn;
3223             }
3224             netPrimaryIn += (newCurrencyState.primaryCurrencyIn[i] = vFracConverted[i]);
3225             netPrimaryOut += vFracOutConverted[i];
3226             totalNewFrac += vFracOutConverted[i];
3227
3228         }
3229         newCurrencyState.supply += (netPrimaryOut - netPrimaryIn);
3230         netPrimaryIn += totalNewFrac;
3231     }
3232     else
3233     {
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++)
3239         {
3240             newCurrencyState.reserveIn[i] = vResConverted[i] + vLiquidityFees[i];
3241             if (isFractional)
3242             {
3243                 newCurrencyState.reserves[i] += (vResConverted[i] - vResOutConverted[i]) + vLiquidityFees[i];
3244             }
3245             else
3246             {
3247                 CAmount newPrimaryOut = vFracOutConverted[i] - vFracConverted[i];
3248                 newCurrencyState.supply += newPrimaryOut;
3249                 netPrimaryIn += newPrimaryOut;
3250                 netPrimaryOut += newPrimaryOut;
3251             }
3252         }
3253     }
3254
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())
3258     {
3259         if (isFractional)
3260         {
3261             if (newCurrencyState.IsLaunchConfirmed())
3262             {
3263                 // calculate launch prices and ensure that conversion prices remain constant until
3264                 // launch is complete
3265                 if (newCurrencyState.IsLaunchClear() && newCurrencyState.IsPrelaunch())
3266                 {
3267                     CCoinbaseCurrencyState tempCurrencyState = importCurrencyState;
3268
3269                     if (preConvertedReserves > CCurrencyValueMap())
3270                     {
3271                         tempCurrencyState.reserves = 
3272                             (CCurrencyValueMap(
3273                                 tempCurrencyState.currencies, tempCurrencyState.reserves) + preConvertedReserves).AsCurrencyVector(tempCurrencyState.currencies);
3274                     }
3275
3276                     /* printf("%s: importCurrencyState:\n%s\nnewCurrencyState:\n%s\nrevertedState:\n%s\n", 
3277                         __func__, 
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()); */
3283
3284                     tempCurrencyState.supply = importCurrencyDef.initialFractionalSupply;
3285
3286                     if (importCurrencyDef.launchSystemID == importCurrencyDef.systemID)
3287                     {
3288                         newCurrencyState.conversionPrice = tempCurrencyState.PricesInReserve(true);
3289                     }
3290                     else
3291                     {
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)
3300                         {
3301                             oneWeight += oneExtraWeight;
3302                             if (weightRemainder)
3303                             {
3304                                 oneWeight++;
3305                                 weightRemainder--;
3306                             }
3307                         }
3308                         std::vector<CAmount> launchPrices = tempCurrencyState.PricesInReserve(true);
3309                         launchPrices.insert(launchPrices.begin() + systemDestIdx, systemDestPrice);
3310                         newCurrencyState.conversionPrice = launchPrices;
3311                     }
3312                 }
3313                 else
3314                 {
3315                     newCurrencyState.conversionPrice = importCurrencyState.conversionPrice;
3316                 }
3317             }
3318             else if (importCurrencyState.IsPrelaunch() && !importCurrencyState.IsRefunding())
3319             {
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)
3326                 {
3327                     newCurrencyState.conversionPrice = tempCurrencyState.PricesInReserve(true);
3328                 }
3329                 else
3330                 {
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)
3338                     {
3339                         oneWeight += oneExtraWeight;
3340                         if (weightRemainder)
3341                         {
3342                             oneWeight++;
3343                             weightRemainder--;
3344                         }
3345                     }
3346                     std::vector<CAmount> launchPrices = tempCurrencyState.PricesInReserve(true);
3347                     launchPrices.insert(launchPrices.begin() + systemDestIdx, newCurrencyState.viaConversionPrice[systemDestIdx]);
3348                     newCurrencyState.conversionPrice = launchPrices;
3349                 }
3350             }
3351         }
3352     }
3353
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())
3357     {
3358         if (importCurrencyState.IsPrelaunch())
3359         {
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])
3366             {
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]);
3371             }
3372             newCurrencyState.preConvertedOut = extraPreconverted;
3373         }
3374         else
3375         {
3376             extraPreconverted = importCurrencyState.preConvertedOut - newCurrencyState.preConvertedOut;
3377         }
3378     }
3379
3380     if (newCurrencyState.IsRefunding())
3381     {
3382         preAllocTotal = 0;
3383         totalMinted = 0;
3384         extraPreconverted = 0;
3385     }
3386
3387     if (isFractional && importCurrencyState.IsLaunchClear() && importCurrencyState.IsLaunchConfirmed())
3388     {
3389         //printf("currency state: %s\n", newCurrencyState.ToUniValue().write(1,2).c_str());
3390         if (totalCarveOut)
3391         {
3392             //printf("%s: applying carveout %d to weights\n", __func__, totalCarveOut);
3393             newCurrencyState.ApplyCarveouts(totalCarveOut);
3394         }
3395         if (importCurrencyDef.preLaunchDiscount)
3396         {
3397             //printf("%s: applying prelaunch discount %d to weights\n", __func__, importCurrencyDef.preLaunchDiscount);
3398             newCurrencyState.ApplyCarveouts(importCurrencyDef.preLaunchDiscount);
3399         }
3400         //printf("new currency state: %s\n", newCurrencyState.ToUniValue().write(1,2).c_str());
3401     }
3402
3403     if (totalMinted || preAllocTotal)
3404     {
3405         newCurrencyState.UpdateWithEmission(totalMinted + preAllocTotal);
3406         netPrimaryOut += (totalMinted + preAllocTotal);
3407         netPrimaryIn += (totalMinted + preAllocTotal);
3408     }
3409
3410     if (newCurrencyState.IsLaunchConfirmed())
3411     {
3412         netPrimaryOut += newCurrencyState.preConvertedOut;
3413         netPrimaryIn += newCurrencyState.preConvertedOut + extraPreconverted;
3414     }
3415
3416     if (extraPreconverted)
3417     {
3418         gatewayDepositsIn.valueMap[importCurrencyID] += extraPreconverted;
3419     }
3420
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;
3423
3424     //printf("%s currencies: %s\n", __func__, ToUniValue().write(1,2).c_str());
3425
3426     if (netPrimaryIn)
3427     {
3428         ReserveInputs.valueMap[importCurrencyID] += netPrimaryIn;
3429     }
3430
3431     if (netPrimaryOut)
3432     {
3433         spentCurrencyOut.valueMap[importCurrencyID] += netPrimaryOut;
3434     }
3435
3436     newCurrencyState.primaryCurrencyOut = netPrimaryOut;
3437     if (importCurrencyDef.IsPBaaSChain() && importCurrencyState.IsLaunchConfirmed() && !importCurrencyState.IsLaunchClear())
3438     {
3439         // pre-conversions should already be on this chain as gateway deposits on behalf of the
3440         // launching chain
3441         newCurrencyState.primaryCurrencyOut -= newCurrencyState.preConvertedOut;
3442         gatewayDepositsIn.valueMap[importCurrencyID] += newCurrencyState.preConvertedOut;
3443         importedCurrency = (importedCurrency - preConvertedReserves).CanonicalMap();
3444         gatewayDepositsIn += preConvertedReserves;
3445     }
3446
3447     for (auto &oneInOut : currencies)
3448     {
3449         if (oneInOut.first == importCurrencyID)
3450         {
3451             if (oneInOut.first == systemDestID)
3452             {
3453                 systemOutConverted += oneInOut.second.nativeOutConverted;
3454             }
3455         }
3456         else
3457         {
3458             if (oneInOut.first == systemDestID)
3459             {
3460                 systemOutConverted += oneInOut.second.reserveOutConverted;
3461             }
3462             if (oneInOut.second.reserveIn)
3463             {
3464                 ReserveInputs.valueMap[oneInOut.first] += oneInOut.second.reserveIn;
3465             }
3466             if (liquidityFees.valueMap.count(oneInOut.first))
3467             {
3468                 ReserveInputs.valueMap[oneInOut.first] += liquidityFees.valueMap[oneInOut.first];
3469             }
3470             if (oneInOut.first != systemDestID)
3471             {
3472                 if (oneInOut.second.reserveOut)
3473                 {
3474                     spentCurrencyOut.valueMap[oneInOut.first] += oneInOut.second.reserveOut;
3475                 }
3476             }
3477         }
3478     }
3479
3480     if (nativeIn)
3481     {
3482         if (importCurrencyID == systemDestID)
3483         {
3484             ReserveInputs.valueMap[systemDestID] += (nativeIn - netPrimaryIn);
3485         }
3486         else
3487         {
3488             ReserveInputs.valueMap[systemDestID] += nativeIn;
3489         }
3490     }
3491     if (nativeOut)
3492     {
3493         if (importCurrencyID == systemDestID)
3494         {
3495             spentCurrencyOut.valueMap[systemDestID] += (nativeOut - netPrimaryOut);
3496         }
3497         else
3498         {
3499             spentCurrencyOut.valueMap[systemDestID] += nativeOut;
3500         }
3501     }
3502
3503     if (systemOutConverted && importCurrencyID != systemDestID)
3504     {
3505         // this does not have meaning besides a store of the system currency output that was converted
3506         currencies[importCurrencyID].reserveOutConverted = systemOutConverted;
3507     }
3508
3509     CCurrencyValueMap checkAgainstInputs(spentCurrencyOut);
3510
3511     if (((ReserveInputs + newConvertedReservePool) - checkAgainstInputs).HasNegative())
3512     {
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(),
3521             totalNativeFee,
3522             totalVerusFee);
3523         //*/
3524
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());
3530         //*/
3531
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());
3538         return false;
3539     }
3540     return true;
3541 }
3542
3543 CCurrencyValueMap CReserveTransactionDescriptor::ReserveInputMap(const uint160 &nativeID) const
3544 {
3545     CCurrencyValueMap retVal;
3546     uint160 id = nativeID.IsNull() ? ASSETCHAINS_CHAINID : nativeID;
3547     for (auto &oneInOut : currencies)
3548     {
3549         // skip native
3550         if (oneInOut.first != id)
3551         {
3552             if (oneInOut.second.reserveIn)
3553             {
3554                 retVal.valueMap[oneInOut.first] = oneInOut.second.reserveIn;
3555             }
3556         }
3557         if (oneInOut.second.nativeOutConverted)
3558         {
3559             retVal.valueMap[oneInOut.first] = oneInOut.second.nativeOutConverted;
3560         }
3561     }
3562     return retVal;
3563 }
3564
3565 CCurrencyValueMap CReserveTransactionDescriptor::ReserveOutputMap(const uint160 &nativeID) const
3566 {
3567     CCurrencyValueMap retVal;
3568     uint160 id = nativeID.IsNull() ? ASSETCHAINS_CHAINID : nativeID;
3569     for (auto &oneInOut : currencies)
3570     {
3571         // skip native
3572         if (oneInOut.first != id)
3573         {
3574             if (oneInOut.second.reserveOut)
3575             {
3576                 retVal.valueMap[oneInOut.first] = oneInOut.second.reserveOut;
3577             }
3578         }
3579     }
3580     return retVal;
3581 }
3582
3583 CCurrencyValueMap CReserveTransactionDescriptor::ReserveOutConvertedMap(const uint160 &nativeID) const
3584 {
3585     CCurrencyValueMap retVal;
3586     uint160 id = nativeID.IsNull() ? ASSETCHAINS_CHAINID : nativeID;
3587     for (auto &oneInOut : currencies)
3588     {
3589         // skip native
3590         if (oneInOut.first != id)
3591         {
3592             if (oneInOut.second.reserveOutConverted)
3593             {
3594                 retVal.valueMap[oneInOut.first] = oneInOut.second.reserveOutConverted;
3595             }
3596         }
3597     }
3598     return retVal;
3599 }
3600
3601 CCurrencyValueMap CReserveTransactionDescriptor::NativeOutConvertedMap() const
3602 {
3603     CCurrencyValueMap retVal;
3604     for (auto &oneInOut : currencies)
3605     {
3606         if (oneInOut.second.nativeOutConverted)
3607         {
3608             retVal.valueMap[oneInOut.first] = oneInOut.second.nativeOutConverted;
3609         }
3610     }
3611     return retVal;
3612 }
3613
3614 CCurrencyValueMap CReserveTransactionDescriptor::ReserveConversionFeesMap() const
3615 {
3616     CCurrencyValueMap retVal;
3617     for (auto &oneInOut : currencies)
3618     {
3619         if (oneInOut.second.reserveConversionFees)
3620         {
3621             retVal.valueMap[oneInOut.first] = oneInOut.second.reserveConversionFees;
3622         }
3623     }
3624     return retVal;
3625 }
3626
3627 std::vector<CAmount> CReserveTransactionDescriptor::ReserveInputVec(const CCurrencyState &cState) const
3628 {
3629     std::vector<CAmount> retVal(cState.currencies.size());
3630     std::map<uint160, int> curMap = cState.GetReserveMap();
3631     for (auto &oneInOut : currencies)
3632     {
3633         retVal[curMap[oneInOut.first]] = oneInOut.second.reserveIn;
3634     }
3635     return retVal;
3636 }
3637
3638 std::vector<CAmount> CReserveTransactionDescriptor::ReserveOutputVec(const CCurrencyState &cState) const
3639 {
3640     std::vector<CAmount> retVal(cState.currencies.size());
3641     std::map<uint160, int> curMap = cState.GetReserveMap();
3642     for (auto &oneInOut : currencies)
3643     {
3644         retVal[curMap[oneInOut.first]] = oneInOut.second.reserveOut;
3645     }
3646     return retVal;
3647 }
3648
3649 std::vector<CAmount> CReserveTransactionDescriptor::ReserveOutConvertedVec(const CCurrencyState &cState) const
3650 {
3651     std::vector<CAmount> retVal(cState.currencies.size());
3652     std::map<uint160, int> curMap = cState.GetReserveMap();
3653     for (auto &oneInOut : currencies)
3654     {
3655         retVal[curMap[oneInOut.first]] = oneInOut.second.reserveOutConverted;
3656     }
3657     return retVal;
3658 }
3659
3660 std::vector<CAmount> CReserveTransactionDescriptor::NativeOutConvertedVec(const CCurrencyState &cState) const
3661 {
3662     std::vector<CAmount> retVal(cState.currencies.size());
3663     std::map<uint160, int> curMap = cState.GetReserveMap();
3664     for (auto &oneInOut : currencies)
3665     {
3666         retVal[curMap[oneInOut.first]] = oneInOut.second.nativeOutConverted;
3667     }
3668     return retVal;
3669 }
3670
3671 std::vector<CAmount> CReserveTransactionDescriptor::ReserveConversionFeesVec(const CCurrencyState &cState) const
3672 {
3673     std::vector<CAmount> retVal(cState.currencies.size());
3674     std::map<uint160, int> curMap = cState.GetReserveMap();
3675     for (auto &oneInOut : currencies)
3676     {
3677         retVal[curMap[oneInOut.first]] = oneInOut.second.reserveConversionFees;
3678     }
3679     return retVal;
3680 }
3681
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)
3685 {
3686     emitted = 0;
3687
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())
3690     {
3691         if (supply <= 0)
3692         {
3693             emitted = supply = toEmit;
3694         }
3695         else
3696         {
3697             emitted = toEmit;
3698             supply += toEmit;
3699         }
3700         return *this;
3701     }
3702
3703     if (toEmit)
3704     {
3705         // first determine current ratio by adding up all currency weights
3706         CAmount InitialRatio = 0;
3707         for (auto weight : weights)
3708         {
3709             InitialRatio += weight;
3710         }
3711
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);
3717
3718         arith_uint256 bigScratch = (bigInitial * bigSupply * bigSatoshi) / (bigSupply + bigEmission);
3719         arith_uint256 bigRatio = bigScratch / bigSatoshi;
3720         // cap ratio at 1
3721         if (bigRatio >= bigSatoshi)
3722         {
3723             bigScratch = arith_uint256(SATOSHIDEN) * arith_uint256(SATOSHIDEN);
3724             bigRatio = bigSatoshi;
3725         }
3726
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))
3731         {
3732             newRatio += 1;
3733         }
3734
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;
3741
3742         for (auto &weight : weights)
3743         {
3744             CAmount weightDelta = (bigRatioDelta * arith_uint256(weight) / bigSatoshi).GetLow64();
3745             weight -= weightDelta;
3746             totalUpdates += weightDelta;
3747         }
3748
3749         CAmount updateExtra = (InitialRatio - newRatio) - totalUpdates;
3750
3751         // if we have any extra, distribute it evenly and any mod, both deterministically and pseudorandomly
3752         if (updateExtra)
3753         {
3754             CAmount forAll = updateExtra / currencies.size();
3755             CAmount forSome = updateExtra % currencies.size();
3756
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);
3760
3761             for (int i = 0; i < extraWeight.size(); i++)
3762             {
3763                 extraWeight[i] = forAll;
3764                 if (forSome)
3765                 {
3766                     extraWeight[i]++;
3767                     forSome--;
3768                 }
3769             }
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++)
3773             {
3774                 weights[i] -= extraWeight[i];
3775             }
3776         }
3777
3778         // update initial supply from what we currently have
3779         emitted = toEmit;
3780         supply += emitted;
3781     }
3782     return *this; 
3783 }
3784
3785 CCoinbaseCurrencyState &CCoinbaseCurrencyState::ApplyCarveouts(int32_t carveOut)
3786 {
3787     if (carveOut && carveOut < SATOSHIDEN)
3788     {
3789         // first determine current ratio by adding up all currency weights
3790         CAmount InitialRatio = 0;
3791         for (auto weight : weights)
3792         {
3793             InitialRatio += weight;
3794         }
3795
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;
3801
3802         int64_t newRatio = bigNewRatio.GetLow64();
3803
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))
3807         {
3808             if (newRatio < SATOSHIDEN)
3809             {
3810                 newRatio += 1;
3811             }
3812         }
3813
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;
3820
3821         for (auto &weight : weights)
3822         {
3823             CAmount weightDelta = (bigRatioDelta * arith_uint256(weight) / bigSatoshi).GetLow64();
3824             weight -= weightDelta;
3825             totalUpdates += weightDelta;
3826         }
3827
3828         CAmount updateExtra = (InitialRatio - newRatio) - totalUpdates;
3829
3830         // if we have any extra, distribute it evenly and any mod, both deterministically and pseudorandomly
3831         if (updateExtra)
3832         {
3833             CAmount forAll = updateExtra / currencies.size();
3834             CAmount forSome = updateExtra % currencies.size();
3835
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);
3839
3840             for (int i = 0; i < extraWeight.size(); i++)
3841             {
3842                 extraWeight[i] = forAll;
3843                 if (forSome)
3844                 {
3845                     extraWeight[i]++;
3846                     forSome--;
3847                 }
3848             }
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++)
3852             {
3853                 weights[i] -= extraWeight[i];
3854             }
3855         }
3856     }
3857     return *this; 
3858 }
3859
3860
3861 void CCoinbaseCurrencyState::RevertFees(const std::vector<CAmount> &normalConversionPrice,
3862                                         const std::vector<CAmount> &outgoingConversionPrice,
3863                                         const uint160 &systemID)
3864 {
3865     auto reserveIndexMap = GetReserveMap();
3866     if (IsFractional() && reserveIndexMap.count(systemID) && reserveIndexMap.find(systemID)->second)
3867     {
3868         // undo fees
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)
3873         {
3874             allConvertedFees.valueMap[GetID()] = primaryCurrencyFees;
3875         }
3876
3877         CCurrencyValueMap liquidityFees(CCurrencyValueMap(currencies, conversionFees));
3878         if (primaryCurrencyConversionFees)
3879         {
3880             liquidityFees.valueMap[systemID] = primaryCurrencyConversionFees;
3881         }
3882
3883         liquidityFees = liquidityFees / 2;
3884
3885         //printf("%s: liquidityfees/2:\n%s\n", __func__, liquidityFees.ToUniValue().write(1,2).c_str());
3886
3887         for (auto &oneLiquidityFee : liquidityFees.valueMap)
3888         {
3889             // importCurrency as liquidity fee will have gotten burned, so add it back to supply
3890             if (oneLiquidityFee.first == GetID())
3891             {
3892                 supply += oneLiquidityFee.second;
3893             }
3894             else
3895             {
3896                 // otherwise, the currency went in to reserves, so remove it
3897                 reserves[reserveIndexMap[oneLiquidityFee.first]] -= oneLiquidityFee.second;
3898             }
3899         }
3900
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)
3909         {
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)
3914             {
3915                 if (reserveIndexMap.count(oneFee.first))
3916                 {
3917                     reserves[reserveIndexMap[oneFee.first]] -= oneFee.second;
3918                     totalConvertedNativeFee +=
3919                         NativeToReserveRaw(ReserveToNativeRaw(oneFee.second, normalConversionPrice[reserveIndexMap[oneFee.first]]),
3920                                         outgoingConversionPrice[systemDestIdx]);
3921                 }
3922                 else if (oneFee.first == GetID())
3923                 {
3924                     totalConvertedNativeFee +=
3925                         NativeToReserveRaw(oneFee.second, normalConversionPrice[systemDestIdx]);
3926                 }
3927             }
3928         }
3929         reserves[systemDestIdx] += totalConvertedNativeFee;
3930
3931         //printf("%s: currencyState:\n%s\n", __func__, ToUniValue().write(1,2).c_str());
3932     }
3933 }
3934
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
3941 {
3942     CCurrencyValueMap originalFees(currencies, fees);
3943     auto reserveIndexMap = GetReserveMap();
3944     feesConverted = false;
3945     if (IsFractional() && reserveIndexMap.count(systemID) && reserveIndexMap.find(systemID)->second)
3946     {
3947         feesConverted = true;
3948
3949         CCurrencyValueMap allConvertedFees(currencies, fees);
3950         if (primaryCurrencyFees)
3951         {
3952             allConvertedFees.valueMap[GetID()] = primaryCurrencyFees;
3953         }
3954
3955         liquidityFees = CCurrencyValueMap(CCurrencyValueMap(currencies, conversionFees));
3956         if (primaryCurrencyConversionFees)
3957         {
3958             liquidityFees.valueMap[systemID] = primaryCurrencyConversionFees;
3959         }
3960
3961         liquidityFees = liquidityFees / 2;
3962
3963         allConvertedFees -= liquidityFees;
3964         CAmount totalNativeFee = 0;
3965         if (allConvertedFees.valueMap.count(systemID))
3966         {
3967             totalNativeFee += allConvertedFees.valueMap[systemID];
3968         }
3969         int systemDestIdx = reserveIndexMap[systemID];
3970         for (auto &oneFee : allConvertedFees.valueMap)
3971         {
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)
3976             {
3977                 if (reserveIndexMap.count(oneFee.first))
3978                 {
3979                     totalNativeFee +=
3980                         NativeToReserveRaw(ReserveToNativeRaw(oneFee.second, normalConversionPrice[reserveIndexMap[oneFee.first]]),
3981                                         outgoingConversionPrice[systemDestIdx]);
3982                 }
3983                 else if (oneFee.first == GetID())
3984                 {
3985                     totalNativeFee +=
3986                         NativeToReserveRaw(oneFee.second, normalConversionPrice[systemDestIdx]);
3987                 }
3988             }
3989         }
3990         convertedFees.valueMap[systemID] += totalNativeFee;
3991     }
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;
3996 }
3997
3998 void CCoinbaseCurrencyState::RevertReservesAndSupply()
3999 {
4000     if (IsFractional())
4001     {
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]])
4005         {
4006             // leave all currencies in
4007             // revert only fees at launch pricing
4008             RevertFees(viaConversionPrice, viaConversionPrice, ASSETCHAINS_CHAINID);
4009         }
4010         else
4011         {
4012             // reverse last changes
4013             auto currencyMap = GetReserveMap();
4014
4015             // revert changes in reserves and supply to pre conversion state, add reserve outs and subtract reserve ins
4016             for (auto &oneCur : currencyMap)
4017             {
4018                 reserves[oneCur.second] += (reserveOut[oneCur.second] - reserveIn[oneCur.second]);
4019                 supply += primaryCurrencyIn[oneCur.second];
4020             }
4021         }
4022     }
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
4025     if (IsPrelaunch())
4026     {
4027         supply -= primaryCurrencyOut;
4028     }
4029     else
4030     {
4031         supply -= (primaryCurrencyOut - preConvertedOut);
4032     }
4033     weights = priorWeights;
4034 }
4035
4036 CAmount CCurrencyState::CalculateConversionFee(CAmount inputAmount, bool convertToNative, int currencyIndex) const
4037 {
4038     arith_uint256 bigAmount(inputAmount);
4039     arith_uint256 bigSatoshi(SATOSHIDEN);
4040
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)
4044     {
4045         int64_t price;
4046         cpp_dec_float_50 priceInReserve = PriceInReserveDecFloat50(currencyIndex);
4047         if (!to_int64(priceInReserve, price))
4048         {
4049             assert(false);
4050         }
4051         bigAmount = price ? (bigAmount * bigSatoshi) / arith_uint256(price) : 0;
4052     }
4053
4054     CAmount fee = 0;
4055     fee = ((bigAmount * arith_uint256(CReserveTransfer::SUCCESS_FEE)) / bigSatoshi).GetLow64();
4056     if (fee < CReserveTransfer::MIN_SUCCESS_FEE)
4057     {
4058         fee = CReserveTransfer::MIN_SUCCESS_FEE;
4059     }
4060     return fee;
4061 }
4062
4063 CAmount CReserveTransactionDescriptor::CalculateConversionFeeNoMin(CAmount inputAmount)
4064 {
4065     arith_uint256 bigAmount(inputAmount);
4066     arith_uint256 bigSatoshi(SATOSHIDEN);
4067     return ((bigAmount * arith_uint256(CReserveTransfer::SUCCESS_FEE)) / bigSatoshi).GetLow64();
4068 }
4069
4070 CAmount CReserveTransactionDescriptor::CalculateConversionFee(CAmount inputAmount)
4071 {
4072     CAmount fee = CalculateConversionFeeNoMin(inputAmount);
4073     if (fee < CReserveTransfer::MIN_SUCCESS_FEE)
4074     {
4075         fee = CReserveTransfer::MIN_SUCCESS_FEE;
4076     }
4077     return fee;
4078 }
4079
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
4082 // the same amount
4083 CAmount CReserveTransactionDescriptor::CalculateAdditionalConversionFee(CAmount inputAmount)
4084 {
4085     arith_uint256 bigAmount(inputAmount);
4086     arith_uint256 bigSatoshi(SATOSHIDEN);
4087     arith_uint256 conversionFee(CReserveTransfer::SUCCESS_FEE);
4088
4089     CAmount newAmount = ((bigAmount * bigSatoshi) / (bigSatoshi - conversionFee)).GetLow64();
4090     if (newAmount - inputAmount < CReserveTransfer::MIN_SUCCESS_FEE)
4091     {
4092         newAmount = inputAmount + CReserveTransfer::MIN_SUCCESS_FEE;
4093     }
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
4098     return fee;
4099 }
4100
4101 bool CFeePool::GetCoinbaseFeePool(CFeePool &feePool, uint32_t height)
4102 {
4103     CBlock block;
4104     CTransaction coinbaseTx;
4105     feePool.SetInvalid();
4106     if (!height || chainActive.Height() < height)
4107     {
4108         height = chainActive.Height();
4109     }
4110     if (!height)
4111     {
4112         return true;
4113     }
4114     if (ReadBlockFromDisk(block, chainActive[height], Params().GetConsensus()))
4115     {
4116         coinbaseTx = block.vtx[0];
4117     }
4118     else
4119     {
4120         return false;
4121     }
4122     
4123     for (auto &txOut : coinbaseTx.vout)
4124     {
4125         COptCCParams p;
4126         if (txOut.scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode == EVAL_FEE_POOL && p.vData.size())
4127         {
4128             feePool = CFeePool(p.vData[0]);
4129         }
4130     }
4131     return true;
4132 }
4133
4134 CFeePool::CFeePool(const CTransaction &coinbaseTx)
4135 {
4136     nVersion = VERSION_INVALID;
4137     if (coinbaseTx.IsCoinBase())
4138     {
4139         for (auto &txOut : coinbaseTx.vout)
4140         {
4141             COptCCParams p;
4142             if (txOut.scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode == EVAL_FEE_POOL && p.vData.size())
4143             {
4144                 ::FromVector(p.vData[0], *this);
4145             }
4146         }
4147     }
4148 }
4149
4150 bool ValidateFeePool(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
4151 {
4152     // fee pool output is unspendable
4153     return false;
4154 }
4155
4156 bool IsFeePoolInput(const CScript &scriptSig)
4157 {
4158     return false;
4159 }
4160
4161 bool PrecheckFeePool(const CTransaction &tx, int32_t outNum, CValidationState &state, uint32_t height)
4162 {
4163     return true;
4164 }
4165
4166 bool PrecheckReserveTransfer(const CTransaction &tx, int32_t outNum, CValidationState &state, uint32_t height)
4167 {
4168     // do a basic sanity check that this reserve transfer's values are consistent
4169     COptCCParams p;
4170     CReserveTransfer rt;
4171     return (tx.vout[outNum].scriptPubKey.IsPayToCryptoCondition(p) &&
4172             p.IsValid() &&
4173             p.evalCode == EVAL_RESERVE_TRANSFER &&
4174             p.vData.size() &&
4175             (rt = CReserveTransfer(p.vData[0])).IsValid() &&
4176             rt.TotalCurrencyOut().valueMap[ASSETCHAINS_CHAINID] == tx.vout[outNum].nValue);
4177 }
This page took 0.261124 seconds and 4 git commands to generate.