diff --git a/src/contracts/QIP.h b/src/contracts/QIP.h index fc65ce68..9757ad8c 100644 --- a/src/contracts/QIP.h +++ b/src/contracts/QIP.h @@ -1,6 +1,6 @@ using namespace QPI; -constexpr uint32 QIP_MAX_NUMBER_OF_ICO = 1024; +constexpr uint32 QIP_MAX_NUMBER_OF_ICO = 16384; constexpr sint64 QIP_ICO_SETUP_FEE = 1000000000; constexpr sint64 QIP_ICO_SETUP_FEE_DISTRIBUTION_PERCENT = 90; constexpr sint64 QIP_INVESTORS_DISTRIBUTION_PERCENT = 97; @@ -26,6 +26,9 @@ enum QIPLogInfo { QIP_maxBuyers = 12, QIP_fundsAlreadyReturned = 13, QIP_amountBelowMinimum = 14, + QIP_invalidIssuer = 15, + QIP_qxAskOrderFound = 16, + QIP_icoAlreadyExists = 17, }; struct QIPLogger @@ -96,7 +99,8 @@ struct QIP : public ContractBase struct StateData { Array icos; - uint32 numberOfICO; + HashSet activeIcoIndexes; + uint32 currentIcoIndex; uint32 transferRightsFee; id developmentFundAddress; HashMap buyersInfo; @@ -180,6 +184,21 @@ struct QIP : public ContractBase uint32 vestingPeriod; }; + struct getBuyerInfo_input + { + id buyer; + uint32 indexOfICO; + }; + struct getBuyerInfo_output + { + sint64 tokensToReceive; + sint64 receivedTokens; + sint64 totalQuPayed; + bit isReturned; + bit isVested; + uint32 remainingVestingPeriod; + }; + struct getICOInfo_locals { ICOInfo ico; @@ -226,17 +245,58 @@ struct QIP : public ContractBase output.vestingPeriod = locals.ico.vestingPeriod; } + struct getBuyerInfo_locals + { + ICOInfo ico; + BuyerInfo buyerInfo; + IcoBuyerKey key; + }; + + PUBLIC_FUNCTION_WITH_LOCALS(getBuyerInfo) + { + locals.ico = state.get().icos.get(input.indexOfICO); + locals.key.assetName = locals.ico.assetName; + locals.key.issuer = locals.ico.issuer; + locals.key.creator = locals.ico.creatorOfICO; + locals.key.buyer = input.buyer; + + if (state.get().buyersInfo.get(locals.key, locals.buyerInfo)) + { + output.tokensToReceive = locals.buyerInfo.toReceive; + output.receivedTokens = locals.buyerInfo.received; + output.totalQuPayed = locals.buyerInfo.totalQuPayed; + output.isVested = locals.ico.isVested; + output.isReturned = locals.buyerInfo.isReturned; + if (locals.ico.isVested && !locals.buyerInfo.isReturned && qpi.epoch() - locals.ico.startEpoch - 2 < locals.ico.vestingPeriod) + { + output.remainingVestingPeriod = locals.ico.vestingPeriod - (qpi.epoch() - locals.ico.startEpoch - 2); + } + } + } + struct createICO_locals { ICOInfo newICO; QIPLogger log; sint64 amountPerShare; + sint64 elementIndex; BuyerInfo emptyInfo; + QX::AssetAskOrders_input qxAskOrdersInput; + QX::AssetAskOrders_output qxAskOrdersOutput; }; PUBLIC_PROCEDURE_WITH_LOCALS(createICO) { - if (input.startEpoch <= (uint32)qpi.epoch() + 1) + if (qpi.invocator() != input.issuer) + { + locals.log._contractIndex = SELF_INDEX; + locals.log._type = QIPLogInfo::QIP_invalidIssuer; + locals.log.dst = qpi.invocator(); + locals.log.amt = 0; + LOG_INFO(locals.log); + output.returnCode = QIPLogInfo::QIP_invalidIssuer; + } + else if (input.startEpoch <= (uint32)qpi.epoch() + 1) { locals.log._contractIndex = SELF_INDEX; locals.log._type = QIPLogInfo::QIP_invalidStartEpoch; @@ -273,7 +333,7 @@ struct QIP : public ContractBase LOG_INFO(locals.log); output.returnCode = QIPLogInfo::QIP_invalidPercent; } - else if (state.get().numberOfICO >= QIP_MAX_NUMBER_OF_ICO) + else if (state.get().activeIcoIndexes.population() >= QIP_MAX_NUMBER_OF_ICO) { locals.log._contractIndex = SELF_INDEX; locals.log._type = QIPLogInfo::QIP_overflowICO; @@ -300,7 +360,40 @@ struct QIP : public ContractBase LOG_INFO(locals.log); output.returnCode = QIPLogInfo::QIP_invalidVestingPeriod; } - else if (qpi.transferShareOwnershipAndPossession(input.assetName, input.issuer, qpi.invocator(), qpi.invocator(), input.saleAmountForPhase1 + input.saleAmountForPhase2 + input.saleAmountForPhase3, SELF) < 0) + else + { + locals.qxAskOrdersInput.assetName = input.assetName; + locals.qxAskOrdersInput.issuer = input.issuer; + locals.qxAskOrdersInput.offset = 0; + CALL_OTHER_CONTRACT_FUNCTION(QX, AssetAskOrders, locals.qxAskOrdersInput, locals.qxAskOrdersOutput); + if (locals.qxAskOrdersOutput.orders.get(0).numberOfShares != 0) + { + locals.log._contractIndex = SELF_INDEX; + locals.log._type = QIPLogInfo::QIP_qxAskOrderFound; + locals.log.dst = qpi.invocator(); + locals.log.amt = 0; + LOG_INFO(locals.log); + output.returnCode = QIPLogInfo::QIP_qxAskOrderFound; + } + else + { + for (locals.elementIndex = 0; locals.elementIndex < state.get().currentIcoIndex; locals.elementIndex++) + { + if (state.get().icos.get(locals.elementIndex).assetName == input.assetName && state.get().icos.get(locals.elementIndex).issuer == input.issuer) + { + locals.log._contractIndex = SELF_INDEX; + locals.log._type = QIPLogInfo::QIP_icoAlreadyExists; + locals.log.dst = qpi.invocator(); + locals.log.amt = 0; + LOG_INFO(locals.log); + output.returnCode = QIPLogInfo::QIP_icoAlreadyExists; + break; + } + } + } + } + + if (output.returnCode == 0 && qpi.transferShareOwnershipAndPossession(input.assetName, input.issuer, qpi.invocator(), qpi.invocator(), input.saleAmountForPhase1 + input.saleAmountForPhase2 + input.saleAmountForPhase3, SELF) < 0) { locals.log._contractIndex = SELF_INDEX; locals.log._type = QIPLogInfo::QIP_invalidTransfer; @@ -322,6 +415,7 @@ struct QIP : public ContractBase { qpi.transfer(qpi.invocator(), qpi.invocationReward() - QIP_ICO_SETUP_FEE); } + locals.newICO.creatorOfICO = qpi.invocator(); locals.newICO.issuer = input.issuer; locals.newICO.address1 = input.address1; @@ -358,8 +452,9 @@ struct QIP : public ContractBase locals.newICO.burnRemainingTokens = input.burnRemainingTokens; locals.newICO.isVested = input.isVested; locals.newICO.vestingPeriod = input.vestingPeriod; - state.mut().icos.set(state.get().numberOfICO, locals.newICO); - state.mut().numberOfICO++; + state.mut().icos.set(state.get().currentIcoIndex, locals.newICO); + state.mut().activeIcoIndexes.add(state.get().currentIcoIndex); + state.mut().currentIcoIndex++; locals.amountPerShare = QPI::div(smul(QPI::div(QIP_ICO_SETUP_FEE, 10LL), 6LL), 676LL); qpi.distributeDividends(locals.amountPerShare); @@ -390,7 +485,7 @@ struct QIP : public ContractBase PUBLIC_PROCEDURE_WITH_LOCALS(buyToken) { - if (input.indexOfICO >= state.get().numberOfICO) + if (!state.get().activeIcoIndexes.contains(input.indexOfICO)) { locals.errorCode = QIPLogInfo::QIP_ICONotFound; } @@ -599,7 +694,7 @@ struct QIP : public ContractBase PUBLIC_PROCEDURE_WITH_LOCALS(returnFunds) { - if (input.indexOfICO >= state.get().numberOfICO) + if (!state.get().activeIcoIndexes.contains(input.indexOfICO)) { locals.log._type = QIPLogInfo::QIP_ICONotFound; } @@ -645,7 +740,7 @@ struct QIP : public ContractBase } locals.fullReturnAmount = QPI::div(locals.buyerInfo.totalQuPayed * (QIP_INVESTORS_DISTRIBUTION_PERCENT - locals.ico.percent1), 100ll); - locals.returnAmount = locals.fullReturnAmount - QPI::div(locals.fullReturnAmount, sint64(locals.ico.vestingPeriod)) * (qpi.epoch() - locals.ico.startEpoch - 3); + locals.returnAmount = locals.fullReturnAmount - QPI::div(locals.fullReturnAmount, sint64(locals.ico.vestingPeriod)) * (qpi.epoch() - locals.ico.startEpoch - 2); qpi.transfer(qpi.invocator(), locals.returnAmount); locals.buyerInfo.isReturned = true; locals.ico.quToDistribute -= locals.buyerInfo.totalQuPayed; @@ -657,22 +752,27 @@ struct QIP : public ContractBase { ICOInfo ico; uint32 i; + uint64 elementIndex; }; PUBLIC_PROCEDURE_WITH_LOCALS(TransferShareManagementRights) { if (qpi.invocationReward() < state.get().transferRightsFee) { - return ; + return; } - for (locals.i = 0 ; locals.i < state.get().numberOfICO; locals.i++) + locals.elementIndex = state.get().activeIcoIndexes.nextElementIndex(NULL_INDEX); + while (locals.elementIndex != NULL_INDEX) { - locals.ico = state.get().icos.get(locals.i); - if (locals.ico.issuer == input.asset.issuer && locals.ico.assetName == input.asset.assetName) + locals.ico = state.get().icos.get(state.get().activeIcoIndexes.key(locals.elementIndex)); + if (locals.ico.issuer == input.asset.issuer + && locals.ico.assetName == input.asset.assetName + && !locals.ico.isVested) { - return ; + return; } + locals.elementIndex = state.get().activeIcoIndexes.nextElementIndex(locals.elementIndex); } if (qpi.numberOfPossessedShares(input.asset.assetName, input.asset.issuer,qpi.invocator(), qpi.invocator(), SELF_INDEX, SELF_INDEX) < input.numberOfShares) @@ -711,6 +811,7 @@ struct QIP : public ContractBase REGISTER_USER_FUNCTIONS_AND_PROCEDURES() { REGISTER_USER_FUNCTION(getICOInfo, 1); + REGISTER_USER_FUNCTION(getBuyerInfo, 2); REGISTER_USER_PROCEDURE(createICO, 1); REGISTER_USER_PROCEDURE(buyToken, 2); @@ -745,31 +846,34 @@ struct QIP : public ContractBase struct END_EPOCH_locals { ICOInfo ico; - sint32 idx; id buyer; sint64 elementIndex; sint64 buyerIndex; sint64 tempAmount; IcoBuyerKey icoBuyerKey; BuyerInfo buyerInfo; + bit isVestingCancelled; + sint64 tokenAmountToReturn; + uint64 icoElementIndex; }; END_EPOCH_WITH_LOCALS() { - for(locals.idx = 0; locals.idx < (sint32)state.get().numberOfICO; locals.idx++) + locals.icoElementIndex = state.get().activeIcoIndexes.nextElementIndex(NULL_INDEX); + while (locals.icoElementIndex != NULL_INDEX) { - locals.ico = state.get().icos.get(locals.idx); + locals.ico = state.get().icos.get(state.get().activeIcoIndexes.key(locals.icoElementIndex)); if (locals.ico.startEpoch == qpi.epoch() && locals.ico.remainingAmountForPhase1 > 0) { locals.ico.remainingAmountForPhase2 += locals.ico.remainingAmountForPhase1; locals.ico.remainingAmountForPhase1 = 0; - state.mut().icos.set(locals.idx, locals.ico); + state.mut().icos.set(state.get().activeIcoIndexes.key(locals.icoElementIndex), locals.ico); } else if (locals.ico.startEpoch + 1 == qpi.epoch() && locals.ico.remainingAmountForPhase2 > 0) { locals.ico.remainingAmountForPhase3 += locals.ico.remainingAmountForPhase2; locals.ico.remainingAmountForPhase2 = 0; - state.mut().icos.set(locals.idx, locals.ico); + state.mut().icos.set(state.get().activeIcoIndexes.key(locals.icoElementIndex), locals.ico); } else if (locals.ico.startEpoch + 2 == qpi.epoch()) { @@ -787,59 +891,80 @@ struct QIP : public ContractBase if (!locals.ico.isVested) { - state.mut().icos.set(locals.idx, state.get().icos.get(state.get().numberOfICO - 1)); - state.mut().numberOfICO--; - locals.idx--; + state.mut().activeIcoIndexes.removeByIndex(locals.icoElementIndex); } + + locals.ico.remainingAmountForPhase3 = 0; } - else if (locals.ico.isVested) + + if (locals.ico.isVested && qpi.epoch() >= locals.ico.startEpoch + 2) // locals.ico.startEpoch + 2 - first distribution epoch { + locals.isVestingCancelled = true; + locals.tokenAmountToReturn = 0; locals.elementIndex = state.get().buyersInfo.nextElementIndex(NULL_INDEX); while (locals.elementIndex != NULL_INDEX) { if (state.get().buyersInfo.key(locals.elementIndex).assetName == locals.ico.assetName && state.get().buyersInfo.key(locals.elementIndex).issuer == locals.ico.issuer - && state.get().buyersInfo.key(locals.elementIndex).creator == locals.ico.creatorOfICO - && !state.get().buyersInfo.value(locals.elementIndex).isReturned) + && state.get().buyersInfo.key(locals.elementIndex).creator == locals.ico.creatorOfICO) { + if (state.get().buyersInfo.value(locals.elementIndex).isReturned) + { + locals.tokenAmountToReturn += state.get().buyersInfo.value(locals.elementIndex).toReceive - state.get().buyersInfo.value(locals.elementIndex).received; + locals.elementIndex = state.get().buyersInfo.nextElementIndex(locals.elementIndex); + continue; + } + + if (locals.isVestingCancelled) + { + locals.isVestingCancelled = false; + } + locals.buyerInfo = state.get().buyersInfo.value(locals.elementIndex); locals.icoBuyerKey = state.get().buyersInfo.key(locals.elementIndex); - if (qpi.epoch() == locals.ico.startEpoch + locals.ico.vestingPeriod + 2) // last vesting epoch + if (qpi.epoch() == locals.ico.startEpoch + locals.ico.vestingPeriod + 1) // last vesting epoch { locals.tempAmount = locals.buyerInfo.toReceive - locals.buyerInfo.received; - state.mut().buyersInfo.removeByIndex(locals.elementIndex); + locals.buyerInfo.received = locals.buyerInfo.toReceive; } else { locals.tempAmount = QPI::div(locals.buyerInfo.toReceive, sint64(locals.ico.vestingPeriod)); locals.buyerInfo.received += locals.tempAmount; - state.mut().buyersInfo.replace(locals.icoBuyerKey, locals.buyerInfo); } + state.mut().buyersInfo.replace(locals.icoBuyerKey, locals.buyerInfo); qpi.transferShareOwnershipAndPossession(locals.ico.assetName, locals.ico.issuer, SELF, SELF, locals.tempAmount, locals.icoBuyerKey.buyer); } locals.elementIndex = state.get().buyersInfo.nextElementIndex(locals.elementIndex); } - qpi.transfer(locals.ico.address2, div(div(locals.ico.quToDistribute * locals.ico.percent2, 100ULL), uint64(locals.ico.vestingPeriod))); - qpi.transfer(locals.ico.address3, div(div(locals.ico.quToDistribute * locals.ico.percent3, 100ULL), uint64(locals.ico.vestingPeriod))); - qpi.transfer(locals.ico.address4, div(div(locals.ico.quToDistribute * locals.ico.percent4, 100ULL), uint64(locals.ico.vestingPeriod))); - qpi.transfer(locals.ico.address5, div(div(locals.ico.quToDistribute * locals.ico.percent5, 100ULL), uint64(locals.ico.vestingPeriod))); - qpi.transfer(locals.ico.address6, div(div(locals.ico.quToDistribute * locals.ico.percent6, 100ULL), uint64(locals.ico.vestingPeriod))); - qpi.transfer(locals.ico.address7, div(div(locals.ico.quToDistribute * locals.ico.percent7, 100ULL), uint64(locals.ico.vestingPeriod))); - qpi.transfer(locals.ico.address8, div(div(locals.ico.quToDistribute * locals.ico.percent8, 100ULL), uint64(locals.ico.vestingPeriod))); - qpi.transfer(locals.ico.address9, div(div(locals.ico.quToDistribute * locals.ico.percent9, 100ULL), uint64(locals.ico.vestingPeriod))); - qpi.transfer(locals.ico.address10, div(div(locals.ico.quToDistribute * locals.ico.percent10, 100ULL), uint64(locals.ico.vestingPeriod))); + if (locals.isVestingCancelled) + { + qpi.transferShareOwnershipAndPossession(locals.ico.assetName, locals.ico.issuer, SELF, SELF, locals.tokenAmountToReturn, locals.ico.creatorOfICO); + } + else + { + qpi.transfer(locals.ico.address2, div(div(locals.ico.quToDistribute * locals.ico.percent2, 100ULL), uint64(locals.ico.vestingPeriod))); + qpi.transfer(locals.ico.address3, div(div(locals.ico.quToDistribute * locals.ico.percent3, 100ULL), uint64(locals.ico.vestingPeriod))); + qpi.transfer(locals.ico.address4, div(div(locals.ico.quToDistribute * locals.ico.percent4, 100ULL), uint64(locals.ico.vestingPeriod))); + qpi.transfer(locals.ico.address5, div(div(locals.ico.quToDistribute * locals.ico.percent5, 100ULL), uint64(locals.ico.vestingPeriod))); + qpi.transfer(locals.ico.address6, div(div(locals.ico.quToDistribute * locals.ico.percent6, 100ULL), uint64(locals.ico.vestingPeriod))); + qpi.transfer(locals.ico.address7, div(div(locals.ico.quToDistribute * locals.ico.percent7, 100ULL), uint64(locals.ico.vestingPeriod))); + qpi.transfer(locals.ico.address8, div(div(locals.ico.quToDistribute * locals.ico.percent8, 100ULL), uint64(locals.ico.vestingPeriod))); + qpi.transfer(locals.ico.address9, div(div(locals.ico.quToDistribute * locals.ico.percent9, 100ULL), uint64(locals.ico.vestingPeriod))); + qpi.transfer(locals.ico.address10, div(div(locals.ico.quToDistribute * locals.ico.percent10, 100ULL), uint64(locals.ico.vestingPeriod))); + } if (qpi.epoch() == locals.ico.startEpoch + locals.ico.vestingPeriod + 2) // last vesting epoch { - state.mut().icos.set(locals.idx, state.get().icos.get(state.get().numberOfICO - 1)); - state.mut().numberOfICO--; - locals.idx--; + state.mut().activeIcoIndexes.removeByIndex(locals.icoElementIndex); } } + locals.icoElementIndex = state.get().activeIcoIndexes.nextElementIndex(locals.icoElementIndex); } state.mut().buyersInfo.cleanupIfNeeded(); + state.mut().activeIcoIndexes.cleanupIfNeeded(); } PRE_ACQUIRE_SHARES() diff --git a/test/contract_qip.cpp b/test/contract_qip.cpp index 86736610..f4800c9b 100644 --- a/test/contract_qip.cpp +++ b/test/contract_qip.cpp @@ -18,7 +18,7 @@ const id QIP_devAddress = ID(_Q, _O, _W, _F, _W, _Q, _C, _P, _C, _M, _V, _S, _W, class QIPChecker : public QIP, public QIP::StateData { public: - uint32 getNumberOfICO() const { return numberOfICO; } + uint64 getNumberOfICO() const { return activeIcoIndexes.population(); } void checkICOInfo(const QIP::getICOInfo_output& output, const QIP::createICO_input& input, const id& creator) { @@ -146,6 +146,16 @@ class ContractTestingQIP : protected ContractTesting return output; } + QIP::getBuyerInfo_output getBuyerInfo(id buyer, uint32 indexOfICO) + { + QIP::getBuyerInfo_input input; + input.indexOfICO = indexOfICO; + input.buyer = buyer; + QIP::getBuyerInfo_output output; + callFunction(QIP_CONTRACT_INDEX, 2, input, output); + return output; + } + sint64 transferShareManagementRightsQX(const id& invocator, const Asset& asset, sint64 numberOfShares, uint32 newManagingContractIndex, sint64 fee) { QX::TransferShareManagementRights_input input; @@ -181,7 +191,7 @@ TEST(ContractQIP, createICO_Success) increaseEnergy(issuer, QIP_ISSUE_ASSET_FEE); EXPECT_EQ(QIP.issueAsset(issuer, assetName, totalShares), totalShares); - id creator = QIP_testBuyer; + id creator = QIP_testIssuer; increaseEnergy(creator, QIP_TRANSFER_ASSET_FEE); increaseEnergy(issuer, QIP_TRANSFER_ASSET_FEE); EXPECT_EQ(QIP.transferAsset(issuer, creator, assetName, issuer, totalShares), totalShares); @@ -250,7 +260,7 @@ TEST(ContractQIP, createICO_InvalidStartEpoch) increaseEnergy(issuer, QIP_ISSUE_ASSET_FEE); EXPECT_EQ(QIP.issueAsset(issuer, assetName, totalShares), totalShares); - id creator = QIP_testBuyer; + id creator = QIP_testIssuer; increaseEnergy(creator, QIP_TRANSFER_ASSET_FEE); increaseEnergy(issuer, QIP_TRANSFER_ASSET_FEE); EXPECT_EQ(QIP.transferAsset(issuer, creator, assetName, issuer, totalShares), totalShares); @@ -317,7 +327,7 @@ TEST(ContractQIP, createICO_InvalidSaleAmount) increaseEnergy(issuer, QIP_ISSUE_ASSET_FEE); EXPECT_EQ(QIP.issueAsset(issuer, assetName, totalShares), totalShares); - id creator = QIP_testBuyer; + id creator = QIP_testIssuer; increaseEnergy(creator, QIP_TRANSFER_ASSET_FEE); increaseEnergy(issuer, QIP_TRANSFER_ASSET_FEE); EXPECT_EQ(QIP.transferAsset(issuer, creator, assetName, issuer, totalShares), totalShares); @@ -376,7 +386,7 @@ TEST(ContractQIP, createICO_InvalidPrice) increaseEnergy(issuer, QIP_ISSUE_ASSET_FEE); EXPECT_EQ(QIP.issueAsset(issuer, assetName, totalShares), totalShares); - id creator = QIP_testBuyer; + id creator = QIP_testIssuer; increaseEnergy(creator, QIP_TRANSFER_ASSET_FEE); increaseEnergy(issuer, QIP_TRANSFER_ASSET_FEE); EXPECT_EQ(QIP.transferAsset(issuer, creator, assetName, issuer, totalShares), totalShares); @@ -435,7 +445,7 @@ TEST(ContractQIP, createICO_InvalidPercent) increaseEnergy(issuer, QIP_ISSUE_ASSET_FEE); EXPECT_EQ(QIP.issueAsset(issuer, assetName, totalShares), totalShares); - id creator = QIP_testBuyer; + id creator = QIP_testIssuer; increaseEnergy(creator, QIP_TRANSFER_ASSET_FEE); increaseEnergy(issuer, QIP_TRANSFER_ASSET_FEE); EXPECT_EQ(QIP.transferAsset(issuer, creator, assetName, issuer, totalShares), totalShares); @@ -509,7 +519,7 @@ TEST(ContractQIP, buyToken_Phase1) increaseEnergy(issuer, QIP_ISSUE_ASSET_FEE); EXPECT_EQ(QIP.issueAsset(issuer, assetName, totalShares), totalShares); - id creator = QIP_testBuyer; + id creator = QIP_testIssuer; increaseEnergy(creator, QIP_TRANSFER_ASSET_FEE); increaseEnergy(issuer, QIP_TRANSFER_ASSET_FEE); EXPECT_EQ(QIP.transferAsset(issuer, creator, assetName, issuer, totalShares), totalShares); @@ -645,7 +655,7 @@ TEST(ContractQIP, buyToken_Phase2) increaseEnergy(issuer, QIP_ISSUE_ASSET_FEE); EXPECT_EQ(QIP.issueAsset(issuer, assetName, totalShares), totalShares); - id creator = QIP_testBuyer; + id creator = QIP_testIssuer; increaseEnergy(creator, QIP_TRANSFER_ASSET_FEE); increaseEnergy(issuer, QIP_TRANSFER_ASSET_FEE); EXPECT_EQ(QIP.transferAsset(issuer, creator, assetName, issuer, totalShares), totalShares); @@ -770,7 +780,7 @@ TEST(ContractQIP, buyToken_Phase3) increaseEnergy(issuer, QIP_ISSUE_ASSET_FEE); EXPECT_EQ(QIP.issueAsset(issuer, assetName, totalShares), totalShares); - id creator = QIP_testBuyer; + id creator = QIP_testIssuer; increaseEnergy(creator, QIP_TRANSFER_ASSET_FEE); increaseEnergy(issuer, QIP_TRANSFER_ASSET_FEE); EXPECT_EQ(QIP.transferAsset(issuer, creator, assetName, issuer, totalShares), totalShares); @@ -896,7 +906,7 @@ TEST(ContractQIP, buyVestedToken_Phase3) increaseEnergy(issuer, QIP_ISSUE_ASSET_FEE); EXPECT_EQ(QIP.issueAsset(issuer, assetName, totalShares), totalShares); - id creator = QIP_testBuyer; + id creator = QIP_testIssuer; increaseEnergy(creator, QIP_TRANSFER_ASSET_FEE); increaseEnergy(issuer, QIP_TRANSFER_ASSET_FEE); EXPECT_EQ(QIP.transferAsset(issuer, creator, assetName, issuer, totalShares), totalShares); @@ -1009,24 +1019,26 @@ TEST(ContractQIP, buyVestedToken_Phase3) EXPECT_EQ(getBalance(QIP_testAddress3), balanceBefore3); EXPECT_EQ(getBalance(QIP_devAddress), 300000); - ++system.epoch; QIP.endEpoch(); + EXPECT_EQ(getBalance(QIP_testAddress1), balanceBefore1 + expectedForAddress1); EXPECT_EQ(getBalance(QIP_testAddress2), 7900001); EXPECT_EQ(getBalance(QIP_testAddress3), 1000001); EXPECT_EQ(getBalance(QIP_devAddress), 300000); EXPECT_EQ(numberOfPossessedShares(assetName, issuer, buyer, buyer, QIP_CONTRACT_INDEX, QIP_CONTRACT_INDEX), 3333); - + ++system.epoch; QIP.endEpoch(); + EXPECT_EQ(getBalance(QIP_testAddress1), balanceBefore1 + expectedForAddress1); EXPECT_EQ(getBalance(QIP_testAddress2), 15800001); EXPECT_EQ(getBalance(QIP_testAddress3), 2000001); EXPECT_EQ(getBalance(QIP_devAddress), 300000); EXPECT_EQ(numberOfPossessedShares(assetName, issuer, buyer, buyer, QIP_CONTRACT_INDEX, QIP_CONTRACT_INDEX), 6666); - + ++system.epoch; QIP.endEpoch(); + EXPECT_EQ(getBalance(QIP_testAddress1), balanceBefore1 + expectedForAddress1); EXPECT_EQ(getBalance(QIP_testAddress2), balanceBefore2 + expectedForAddress2); EXPECT_EQ(getBalance(QIP_testAddress3), balanceBefore3 + expectedForAddress3); @@ -1127,7 +1139,7 @@ TEST(ContractQIP, returnFunds) QIP.beginEpoch(); QIP::buyToken_output buyOutput2 = QIP.buyToken(buyer2, 0, buyAmount2, requiredReward2); EXPECT_EQ(buyOutput2.returnCode, QIPLogInfo::QIP_success); - QIP.endEpoch(); + //QIP.endEpoch(); EXPECT_EQ(numberOfPossessedShares(assetName, issuer, buyer, buyer, QIP_CONTRACT_INDEX, QIP_CONTRACT_INDEX), 0); EXPECT_EQ(numberOfPossessedShares(assetName, issuer, buyer2, buyer2, QIP_CONTRACT_INDEX, QIP_CONTRACT_INDEX), 0); @@ -1135,17 +1147,19 @@ TEST(ContractQIP, returnFunds) EXPECT_EQ(getBalance(QIP_CONTRACT_ID), 24171804); EXPECT_EQ(getBalance(QIP_testAddress2), 0); - ++system.epoch; // 1 - QIP.beginEpoch(); - QIP.endEpoch(); + QIP.endEpoch(); // 1 + EXPECT_EQ(numberOfPossessedShares(assetName, issuer, buyer, buyer, QIP_CONTRACT_INDEX, QIP_CONTRACT_INDEX), 792); // buyAmount / vestingPeriod EXPECT_EQ(numberOfPossessedShares(assetName, issuer, buyer2, buyer2, QIP_CONTRACT_INDEX, QIP_CONTRACT_INDEX), 1269); EXPECT_EQ(getBalance(QIP_testAddress1), 12463110); EXPECT_EQ(getBalance(QIP_testAddress2), 3452981); - - ++system.epoch; // 2 + + ++system.epoch; QIP.beginEpoch(); - QIP.endEpoch(); + QIP::getBuyerInfo_output buyerInfo = QIP.getBuyerInfo(buyer2, 0); + EXPECT_EQ(buyerInfo.remainingVestingPeriod, 6); + QIP.endEpoch(); // 2 + EXPECT_EQ(numberOfPossessedShares(assetName, issuer, buyer, buyer, QIP_CONTRACT_INDEX, QIP_CONTRACT_INDEX), 792 * 2); EXPECT_EQ(numberOfPossessedShares(assetName, issuer, buyer2, buyer2, QIP_CONTRACT_INDEX, QIP_CONTRACT_INDEX), 1269 * 2); EXPECT_EQ(getBalance(QIP_testAddress2), 3452981 * 2); @@ -1153,6 +1167,7 @@ TEST(ContractQIP, returnFunds) ++system.epoch; // 3 QIP.beginEpoch(); QIP.endEpoch(); + EXPECT_EQ(numberOfPossessedShares(assetName, issuer, buyer, buyer, QIP_CONTRACT_INDEX, QIP_CONTRACT_INDEX), 792 * 3); EXPECT_EQ(numberOfPossessedShares(assetName, issuer, buyer2, buyer2, QIP_CONTRACT_INDEX, QIP_CONTRACT_INDEX), 1269 * 3); EXPECT_EQ(getBalance(QIP_testAddress2), 3452981 * 3); @@ -1182,6 +1197,7 @@ TEST(ContractQIP, returnFunds) ++system.epoch; // 6 QIP.beginEpoch(); QIP.endEpoch(); + EXPECT_EQ(numberOfPossessedShares(assetName, issuer, buyer, buyer, QIP_CONTRACT_INDEX, QIP_CONTRACT_INDEX), 792 * 3); EXPECT_EQ(numberOfPossessedShares(assetName, issuer, buyer2, buyer2, QIP_CONTRACT_INDEX, QIP_CONTRACT_INDEX), 1269 * 6); EXPECT_EQ(getBalance(QIP_testAddress2), 3452981 * 3 + 2438124 * 3); @@ -1189,6 +1205,7 @@ TEST(ContractQIP, returnFunds) ++system.epoch; // 7 QIP.beginEpoch(); QIP.endEpoch(); + EXPECT_EQ(numberOfPossessedShares(assetName, issuer, buyer, buyer, QIP_CONTRACT_INDEX, QIP_CONTRACT_INDEX), 792 * 3); EXPECT_EQ(numberOfPossessedShares(assetName, issuer, buyer2, buyer2, QIP_CONTRACT_INDEX, QIP_CONTRACT_INDEX), buyAmount2); EXPECT_EQ(getBalance(QIP_testAddress2), 3452981 * 3 + 2438124 * 4); @@ -1205,7 +1222,7 @@ TEST(ContractQIP, buyToken_InvalidEpoch) increaseEnergy(issuer, QIP_ISSUE_ASSET_FEE); EXPECT_EQ(QIP.issueAsset(issuer, assetName, totalShares), totalShares); - id creator = QIP_testBuyer; + id creator = QIP_testIssuer; increaseEnergy(creator, QIP_TRANSFER_ASSET_FEE); increaseEnergy(issuer, QIP_TRANSFER_ASSET_FEE); EXPECT_EQ(QIP.transferAsset(issuer, creator, assetName, issuer, totalShares), totalShares); @@ -1276,7 +1293,7 @@ TEST(ContractQIP, buyToken_InvalidAmount) increaseEnergy(issuer, QIP_ISSUE_ASSET_FEE); EXPECT_EQ(QIP.issueAsset(issuer, assetName, totalShares), totalShares); - id creator = QIP_testBuyer; + id creator = QIP_testIssuer; increaseEnergy(creator, QIP_TRANSFER_ASSET_FEE); increaseEnergy(issuer, QIP_TRANSFER_ASSET_FEE); EXPECT_EQ(QIP.transferAsset(issuer, creator, assetName, issuer, totalShares), totalShares); @@ -1363,7 +1380,7 @@ TEST(ContractQIP, buyToken_InsufficientInvocationReward) increaseEnergy(issuer, QIP_ISSUE_ASSET_FEE); EXPECT_EQ(QIP.issueAsset(issuer, assetName, totalShares), totalShares); - id creator = QIP_testBuyer; + id creator = QIP_testIssuer; increaseEnergy(creator, QIP_TRANSFER_ASSET_FEE); increaseEnergy(issuer, QIP_TRANSFER_ASSET_FEE); EXPECT_EQ(QIP.transferAsset(issuer, creator, assetName, issuer, totalShares), totalShares); @@ -1438,7 +1455,7 @@ TEST(ContractQIP, END_EPOCH_Phase1Rollover) increaseEnergy(issuer, QIP_ISSUE_ASSET_FEE); EXPECT_EQ(QIP.issueAsset(issuer, assetName, totalShares), totalShares); - id creator = QIP_testBuyer; + id creator = QIP_testIssuer; increaseEnergy(creator, QIP_TRANSFER_ASSET_FEE); increaseEnergy(issuer, QIP_TRANSFER_ASSET_FEE); EXPECT_EQ(QIP.transferAsset(issuer, creator, assetName, issuer, totalShares), totalShares); @@ -1516,7 +1533,7 @@ TEST(ContractQIP, END_EPOCH_Phase2Rollover) increaseEnergy(issuer, QIP_ISSUE_ASSET_FEE); EXPECT_EQ(QIP.issueAsset(issuer, assetName, totalShares), totalShares); - id creator = QIP_testBuyer; + id creator = QIP_testIssuer; increaseEnergy(creator, QIP_TRANSFER_ASSET_FEE); increaseEnergy(issuer, QIP_TRANSFER_ASSET_FEE); EXPECT_EQ(QIP.transferAsset(issuer, creator, assetName, issuer, totalShares), totalShares); @@ -1595,7 +1612,7 @@ TEST(ContractQIP, END_EPOCH_Phase3ReturnToCreator) increaseEnergy(issuer, QIP_ISSUE_ASSET_FEE); EXPECT_EQ(QIP.issueAsset(issuer, assetName, totalShares), totalShares); - id creator = QIP_testBuyer; + id creator = QIP_testIssuer; increaseEnergy(creator, QIP_TRANSFER_ASSET_FEE); increaseEnergy(issuer, QIP_TRANSFER_ASSET_FEE); EXPECT_EQ(QIP.transferAsset(issuer, creator, assetName, issuer, totalShares), totalShares); @@ -1682,7 +1699,7 @@ TEST(ContractQIP, END_EPOCH_BurnRemainingTokens) increaseEnergy(issuer, QIP_ISSUE_ASSET_FEE); EXPECT_EQ(QIP.issueAsset(issuer, assetName, totalShares), totalShares); - id creator = QIP_testBuyer; + id creator = QIP_testIssuer; increaseEnergy(creator, QIP_TRANSFER_ASSET_FEE); increaseEnergy(issuer, QIP_TRANSFER_ASSET_FEE); EXPECT_EQ(QIP.transferAsset(issuer, creator, assetName, issuer, totalShares), totalShares); @@ -1768,7 +1785,7 @@ TEST(ContractQIP, TransferShareManagementRights) increaseEnergy(issuer, QIP_ISSUE_ASSET_FEE); EXPECT_EQ(QIP.issueAsset(issuer, assetName, totalShares), totalShares); - id creator = QIP_testBuyer; + id creator = QIP_testIssuer; increaseEnergy(creator, QIP_TRANSFER_ASSET_FEE); increaseEnergy(issuer, QIP_TRANSFER_ASSET_FEE); EXPECT_EQ(QIP.transferAsset(issuer, creator, assetName, issuer, totalShares), totalShares);