@@ -55,6 +55,10 @@ constexpr uint64 QUTILLogTypeNotAuthorized = 20; // Not autho
5555constexpr uint64 QUTILLogTypeInsufficientFundsForCancel = 21 ; // Not have enough funds for poll calcellation
5656constexpr uint64 QUTILLogTypeMaxPollsReached = 22 ; // Max epoch per epoch reached
5757
58+ // Fee per shareholder for DistributeQuToShareholders()
59+ constexpr sint64 QUTIL_DISTRIBUTE_QU_TO_SHAREHOLDER_FEE_PER_SHAREHOLDER = 5 ;
60+
61+
5862struct QUTILLogger
5963{
6064 uint32 contractId; // to distinguish bw SCs
@@ -66,6 +70,9 @@ struct QUTILLogger
6670 // Other data go here
6771 sint8 _terminator; // Only data before "_terminator" are logged
6872};
73+
74+ // Deactivate logger for delay function
75+ #if 0
6976struct QUTILDFLogger
7077{
7178 uint32 contractId; // to distinguish bw SCs
@@ -76,6 +83,7 @@ struct QUTILDFLogger
7683 id result;
7784 sint8 _terminator; // Only data before "_terminator" are logged
7885};
86+ #endif
7987
8088// poll and voter structs
8189struct QUTILPoll {
@@ -223,6 +231,7 @@ struct QUTIL : public ContractBase
223231 id currentId;
224232 sint64 t;
225233 uint64 useNext;
234+ uint64 totalNumTransfers;
226235 QUTILLogger logger;
227236 };
228237
@@ -492,9 +501,33 @@ struct QUTIL : public ContractBase
492501 {
493502 locals.logger = QUTILLogger{ 0 , 0 , qpi.invocator (), SELF, qpi.invocationReward (), QUTIL_STM1_TRIGGERED };
494503 LOG_INFO (locals.logger );
495- state.total = input.amt0 + input.amt1 + input.amt2 + input.amt3 + input.amt4 + input.amt5 + input.amt6 + input.amt7 + input.amt8 + input.amt9 + input.amt10 + input.amt11 + input.amt12 + input.amt13 + input.amt14 + input.amt15 + input.amt16 + input.amt17 + input.amt18 + input.amt19 + input.amt20 + input.amt21 + input.amt22 + input.amt23 + input.amt24 + QUTIL_STM1_INVOCATION_FEE;
496- // invalid amount (<0), return fund and exit
497- if ((input.amt0 < 0 ) || (input.amt1 < 0 ) || (input.amt2 < 0 ) || (input.amt3 < 0 ) || (input.amt4 < 0 ) || (input.amt5 < 0 ) || (input.amt6 < 0 ) || (input.amt7 < 0 ) || (input.amt8 < 0 ) || (input.amt9 < 0 ) || (input.amt10 < 0 ) || (input.amt11 < 0 ) || (input.amt12 < 0 ) || (input.amt13 < 0 ) || (input.amt14 < 0 ) || (input.amt15 < 0 ) || (input.amt16 < 0 ) || (input.amt17 < 0 ) || (input.amt18 < 0 ) || (input.amt19 < 0 ) || (input.amt20 < 0 ) || (input.amt21 < 0 ) || (input.amt22 < 0 ) || (input.amt23 < 0 ) || (input.amt24 < 0 ))
504+
505+ // invalid amount (<0 or >= MAX_AMOUNT), return fund and exit
506+ if ((input.amt0 < 0 ) || (input.amt0 >= MAX_AMOUNT)
507+ || (input.amt1 < 0 ) || (input.amt1 >= MAX_AMOUNT)
508+ || (input.amt2 < 0 ) || (input.amt2 >= MAX_AMOUNT)
509+ || (input.amt3 < 0 ) || (input.amt3 >= MAX_AMOUNT)
510+ || (input.amt4 < 0 ) || (input.amt4 >= MAX_AMOUNT)
511+ || (input.amt5 < 0 ) || (input.amt5 >= MAX_AMOUNT)
512+ || (input.amt6 < 0 ) || (input.amt6 >= MAX_AMOUNT)
513+ || (input.amt7 < 0 ) || (input.amt7 >= MAX_AMOUNT)
514+ || (input.amt8 < 0 ) || (input.amt8 >= MAX_AMOUNT)
515+ || (input.amt9 < 0 ) || (input.amt9 >= MAX_AMOUNT)
516+ || (input.amt10 < 0 ) || (input.amt10 >= MAX_AMOUNT)
517+ || (input.amt11 < 0 ) || (input.amt11 >= MAX_AMOUNT)
518+ || (input.amt12 < 0 ) || (input.amt12 >= MAX_AMOUNT)
519+ || (input.amt13 < 0 ) || (input.amt13 >= MAX_AMOUNT)
520+ || (input.amt14 < 0 ) || (input.amt14 >= MAX_AMOUNT)
521+ || (input.amt15 < 0 ) || (input.amt15 >= MAX_AMOUNT)
522+ || (input.amt16 < 0 ) || (input.amt16 >= MAX_AMOUNT)
523+ || (input.amt17 < 0 ) || (input.amt17 >= MAX_AMOUNT)
524+ || (input.amt18 < 0 ) || (input.amt18 >= MAX_AMOUNT)
525+ || (input.amt19 < 0 ) || (input.amt19 >= MAX_AMOUNT)
526+ || (input.amt20 < 0 ) || (input.amt20 >= MAX_AMOUNT)
527+ || (input.amt21 < 0 ) || (input.amt21 >= MAX_AMOUNT)
528+ || (input.amt22 < 0 ) || (input.amt22 >= MAX_AMOUNT)
529+ || (input.amt23 < 0 ) || (input.amt23 >= MAX_AMOUNT)
530+ || (input.amt24 < 0 ) || (input.amt24 >= MAX_AMOUNT))
498531 {
499532 locals.logger = QUTILLogger{ 0 , 0 , qpi.invocator (), SELF, qpi.invocationReward (), QUTIL_STM1_INVALID_AMOUNT_NUMBER };
500533 output.returnCode = QUTIL_STM1_INVALID_AMOUNT_NUMBER;
@@ -504,9 +537,40 @@ struct QUTIL : public ContractBase
504537 qpi.transfer (qpi.invocator (), qpi.invocationReward ());
505538 }
506539 }
540+
541+ // Make sure that the sum of all amounts does not overflow and is equal to qpi.invocationReward()
542+ state.total = qpi.invocationReward ();
543+ state.total -= input.amt0 ; if (state.total < 0 ) goto exit;
544+ state.total -= input.amt1 ; if (state.total < 0 ) goto exit;
545+ state.total -= input.amt2 ; if (state.total < 0 ) goto exit;
546+ state.total -= input.amt3 ; if (state.total < 0 ) goto exit;
547+ state.total -= input.amt4 ; if (state.total < 0 ) goto exit;
548+ state.total -= input.amt5 ; if (state.total < 0 ) goto exit;
549+ state.total -= input.amt6 ; if (state.total < 0 ) goto exit;
550+ state.total -= input.amt7 ; if (state.total < 0 ) goto exit;
551+ state.total -= input.amt8 ; if (state.total < 0 ) goto exit;
552+ state.total -= input.amt9 ; if (state.total < 0 ) goto exit;
553+ state.total -= input.amt10 ; if (state.total < 0 ) goto exit;
554+ state.total -= input.amt11 ; if (state.total < 0 ) goto exit;
555+ state.total -= input.amt12 ; if (state.total < 0 ) goto exit;
556+ state.total -= input.amt13 ; if (state.total < 0 ) goto exit;
557+ state.total -= input.amt14 ; if (state.total < 0 ) goto exit;
558+ state.total -= input.amt15 ; if (state.total < 0 ) goto exit;
559+ state.total -= input.amt16 ; if (state.total < 0 ) goto exit;
560+ state.total -= input.amt17 ; if (state.total < 0 ) goto exit;
561+ state.total -= input.amt18 ; if (state.total < 0 ) goto exit;
562+ state.total -= input.amt19 ; if (state.total < 0 ) goto exit;
563+ state.total -= input.amt20 ; if (state.total < 0 ) goto exit;
564+ state.total -= input.amt21 ; if (state.total < 0 ) goto exit;
565+ state.total -= input.amt22 ; if (state.total < 0 ) goto exit;
566+ state.total -= input.amt23 ; if (state.total < 0 ) goto exit;
567+ state.total -= input.amt24 ; if (state.total < 0 ) goto exit;
568+ state.total -= QUTIL_STM1_INVOCATION_FEE; if (state.total < 0 ) goto exit;
569+
507570 // insufficient or too many qubic transferred, return fund and exit (we don't want to return change)
508- if (qpi. invocationReward () != state. total )
571+ if (state. total != 0 )
509572 {
573+ exit:
510574 locals.logger = QUTILLogger{ 0 , 0 , qpi.invocator (), SELF, qpi.invocationReward (), QUTIL_STM1_WRONG_FUND };
511575 LOG_INFO (locals.logger );
512576 output.returnCode = QUTIL_STM1_WRONG_FUND;
@@ -667,7 +731,7 @@ struct QUTIL : public ContractBase
667731 LOG_INFO (locals.logger );
668732 qpi.transfer (input.dst24 , input.amt24 );
669733 }
670- locals.logger = QUTILLogger{ 0 , 0 , qpi.invocator (), SELF, state. total , QUTIL_STM1_SUCCESS };
734+ locals.logger = QUTILLogger{ 0 , 0 , qpi.invocator (), SELF, qpi. invocationReward () , QUTIL_STM1_SUCCESS};
671735 LOG_INFO (locals.logger );
672736 output.returnCode = QUTIL_STM1_SUCCESS;
673737 qpi.burn (QUTIL_STM1_INVOCATION_FEE);
@@ -687,7 +751,8 @@ struct QUTIL : public ContractBase
687751 output.total = 0 ;
688752
689753 // Number of addresses and transfers is > 0 and total transfers do not exceed limit (including 2 transfers from invocator to contract and contract to invocator)
690- if (input.dstCount <= 0 || input.numTransfersEach <= 0 || input.dstCount * input.numTransfersEach + 2 > CONTRACT_ACTION_TRACKER_SIZE)
754+ locals.totalNumTransfers = smul ((uint64)input.dstCount , (uint64)input.numTransfersEach );
755+ if (input.dstCount <= 0 || input.numTransfersEach <= 0 || locals.totalNumTransfers > CONTRACT_ACTION_TRACKER_SIZE - 2 )
691756 {
692757 if (qpi.invocationReward () > 0 )
693758 {
@@ -700,7 +765,7 @@ struct QUTIL : public ContractBase
700765 }
701766
702767 // Check the fund is enough
703- if (qpi.invocationReward () < input. dstCount * input. numTransfersEach )
768+ if ((uint64) qpi.invocationReward () < locals. totalNumTransfers )
704769 {
705770 if (qpi.invocationReward () > 0 )
706771 {
@@ -1181,6 +1246,8 @@ struct QUTIL : public ContractBase
11811246 state.dfMiningSeed = qpi.getPrevSpectrumDigest ();
11821247 }
11831248
1249+ // Deactivate delay function
1250+ #if 0
11841251 struct BEGIN_TICK_locals
11851252 {
11861253 m256i dfPubkey, dfNonce;
@@ -1194,10 +1261,11 @@ struct QUTIL : public ContractBase
11941261 locals.dfPubkey = qpi.getPrevSpectrumDigest();
11951262 locals.dfNonce = qpi.getPrevComputerDigest();
11961263 state.dfCurrentState = qpi.computeMiningFunction(state.dfMiningSeed, locals.dfPubkey, locals.dfNonce);
1197-
1264+
11981265 locals.logger = QUTILDFLogger{ 0, 0, locals.dfNonce, locals.dfPubkey, state.dfMiningSeed, state.dfCurrentState};
11991266 LOG_INFO(locals.logger);
12001267 }
1268+ #endif
12011269
12021270 /*
12031271 * @return Return total number of shares that currently exist of the asset given as input
@@ -1207,6 +1275,79 @@ struct QUTIL : public ContractBase
12071275 output = qpi.numberOfShares (input);
12081276 }
12091277
1278+ struct DistributeQuToShareholders_input
1279+ {
1280+ Asset asset;
1281+ };
1282+ struct DistributeQuToShareholders_output
1283+ {
1284+ sint64 shareholders;
1285+ sint64 totalShares;
1286+ sint64 amountPerShare;
1287+ sint64 fees;
1288+ };
1289+ struct DistributeQuToShareholders_locals
1290+ {
1291+ AssetPossessionIterator iter;
1292+ sint64 payBack;
1293+ };
1294+
1295+ PUBLIC_PROCEDURE_WITH_LOCALS (DistributeQuToShareholders)
1296+ {
1297+ // 1. Compute fee (increases linear with number of shareholders)
1298+ // 1.1. Count shareholders and shares
1299+ for (locals.iter .begin (input.asset ); !locals.iter .reachedEnd (); locals.iter .next ())
1300+ {
1301+ if (locals.iter .numberOfPossessedShares () > 0 )
1302+ {
1303+ ++output.shareholders ;
1304+ output.totalShares += locals.iter .numberOfPossessedShares ();
1305+ }
1306+ }
1307+
1308+ // 1.2. Cancel if there are no shareholders
1309+ if (output.shareholders == 0 )
1310+ {
1311+ qpi.transfer (qpi.invocator (), qpi.invocationReward ());
1312+ return ;
1313+ }
1314+
1315+ // 1.3. Compute fee (proportional to number of shareholders)
1316+ output.fees = output.shareholders * QUTIL_DISTRIBUTE_QU_TO_SHAREHOLDER_FEE_PER_SHAREHOLDER;
1317+
1318+ // 1.4. Compute QU per share
1319+ output.amountPerShare = div<sint64>(qpi.invocationReward () - output.fees , output.totalShares );
1320+
1321+ // 1.5. Cancel if amount is not sufficient to pay fees and at least one QU per share
1322+ if (output.amountPerShare <= 0 )
1323+ {
1324+ qpi.transfer (qpi.invocator (), qpi.invocationReward ());
1325+ return ;
1326+ }
1327+
1328+ // 1.6. compute payback QU (remainder of distribution)
1329+ locals.payBack = qpi.invocationReward () - output.totalShares * output.amountPerShare - output.fees ;
1330+ ASSERT (locals.payBack >= 0 );
1331+
1332+ // 2. Distribute to shareholders
1333+ for (locals.iter .begin (input.asset ); !locals.iter .reachedEnd (); locals.iter .next ())
1334+ {
1335+ if (locals.iter .numberOfPossessedShares () > 0 )
1336+ {
1337+ qpi.transfer (locals.iter .possessor (), locals.iter .numberOfPossessedShares () * output.amountPerShare );
1338+ }
1339+ }
1340+
1341+ // 3. Burn fee
1342+ qpi.burn (output.fees );
1343+
1344+ // 4. pay back QU that cannot be evenly distributed
1345+ if (locals.payBack > 0 )
1346+ {
1347+ qpi.transfer (qpi.invocator (), locals.payBack );
1348+ }
1349+ }
1350+
12101351 REGISTER_USER_FUNCTIONS_AND_PROCEDURES ()
12111352 {
12121353 REGISTER_USER_FUNCTION (GetSendToManyV1Fee, 1 );
@@ -1222,5 +1363,6 @@ struct QUTIL : public ContractBase
12221363 REGISTER_USER_PROCEDURE (CreatePoll, 4 );
12231364 REGISTER_USER_PROCEDURE (Vote, 5 );
12241365 REGISTER_USER_PROCEDURE (CancelPoll, 6 );
1366+ REGISTER_USER_PROCEDURE (DistributeQuToShareholders, 7 );
12251367 }
12261368};
0 commit comments