diff --git a/src/contract_core/contract_def.h b/src/contract_core/contract_def.h index 10d7da5f5..1029c4a07 100644 --- a/src/contract_core/contract_def.h +++ b/src/contract_core/contract_def.h @@ -276,7 +276,7 @@ constexpr struct ContractDescription {"SWATCH", 123, 10000, sizeof(IPO)}, {"CCF", 127, 10000, sizeof(CCF)}, // proposal in epoch 125, IPO in 126, construction and first use in 127 {"QEARN", 137, 10000, sizeof(QEARN)}, // proposal in epoch 135, IPO in 136, construction in 137 / first donation after END_EPOCH, first round in epoch 138 - {"QVAULT", 138, 10000, sizeof(IPO)}, // proposal in epoch 136, IPO in 137, construction and first use in 138 + {"QVAULT", 138, 10000, sizeof(QVAULT)}, // proposal in epoch 136, IPO in 137, construction and first use in 138 {"MSVAULT", 149, 10000, sizeof(MSVAULT)}, // proposal in epoch 147, IPO in 148, construction and first use in 149 {"QBAY", 154, 10000, sizeof(QBAY)}, // proposal in epoch 152, IPO in 153, construction and first use in 154 // new contracts should be added above this line diff --git a/src/contracts/QVAULT.h b/src/contracts/QVAULT.h index 08a930153..054260f16 100644 --- a/src/contracts/QVAULT.h +++ b/src/contracts/QVAULT.h @@ -1,675 +1,3571 @@ using namespace QPI; -constexpr uint64 QVAULT_MAX_REINVEST_AMOUNT = 100000000000ULL; -constexpr uint64 QVAULT_QCAP_ASSETNAME = 1346454353; -constexpr uint64 QVAULT_QCAP_MAX_SUPPLY = 21000000; -constexpr uint32 QVAULT_MAX_NUMBER_OF_BANNED_ADDRESSES = 16; - +// QVAULT Contract Constants +// Asset identifiers for QCAP and QVAULT tokens +constexpr uint64 QVAULT_QCAP_ASSETNAME = 1346454353; // QCAP token asset name +constexpr uint64 QVAULT_QVAULT_ASSETNAME = 92686824592977; // QVAULT token asset name + +// Financial and operational constants +constexpr uint64 QVAULT_PROPOSAL_FEE = 10000000; // Fee required to submit proposals (in qubic) +constexpr uint64 QVAULT_IPO_PARTICIPATION_MIN_FUND = 1000000000; // Minimum fund required for IPO participation +constexpr uint32 QVAULT_QCAP_MAX_SUPPLY = 21000000; // Maximum total supply of QCAP tokens +constexpr uint32 QVAULT_MAX_NUMBER_OF_PROPOSAL = 65536; // Maximum number of proposals allowed +constexpr uint32 QVAULT_X_MULTIPLIER = 1048576; // X multiplier for QVAULT +constexpr uint32 QVAULT_MIN_QUORUM_REQ = 330; // Minimum quorum requirement (330) +constexpr uint32 QVAULT_MAX_QUORUM_REQ = 670; // Maximum quorum requirement (670) +constexpr uint32 QVAULT_MAX_URLS_COUNT = 256; // Maximum number of URLs allowed +constexpr uint32 QVAULT_MIN_VOTING_POWER = 10000; // Minimum voting power required +constexpr uint32 QVAULT_SUM_OF_ALLOCATION_PERCENTAGES = 970; // Sum of allocation percentages +constexpr uint32 QVAULT_MAX_USER_VOTES = 16; // Maximum number of votes per user + +// Yearly QCAP sale limits +constexpr uint32 QVAULT_2025MAX_QCAP_SALE_AMOUNT = 10714286; // Maximum QCAP sales for 2025 +constexpr uint32 QVAULT_2026MAX_QCAP_SALE_AMOUNT = 15571429; // Maximum QCAP sales for 2026 +constexpr uint32 QVAULT_2027MAX_QCAP_SALE_AMOUNT = 18000000; // Maximum QCAP sales for 2027 + +// Return code constants +constexpr sint32 QVAULT_SUCCESS = 0; // Operation completed successfully +constexpr sint32 QVAULT_INSUFFICIENT_QCAP = 1; // User doesn't have enough QCAP tokens +constexpr sint32 QVAULT_NOT_ENOUGH_STAKE = 2; // User doesn't have enough staked tokens +constexpr sint32 QVAULT_NOT_STAKER = 3; // User is not a staker +constexpr sint32 QVAULT_INSUFFICIENT_FUND = 4; // Insufficient funds for operation +constexpr sint32 QVAULT_NOT_TRANSFERRED_SHARE = 5; // Share transfer failed +constexpr sint32 QVAULT_NOT_IN_TIME = 6; // Operation attempted outside allowed timeframe +constexpr sint32 QVAULT_NOT_FAIR = 7; // Allocation proposal percentages don't sum to 1000 +constexpr sint32 QVAULT_OVERFLOW_SALE_AMOUNT = 8; // QCAP sale amount exceeds yearly limit +constexpr sint32 QVAULT_OVERFLOW_PROPOSAL = 9; // Proposal ID exceeds maximum allowed +constexpr sint32 QVAULT_OVERFLOW_VOTES = 10; // Maximum number of votes per user exceeded +constexpr sint32 QVAULT_SAME_DECISION = 11; // User is voting with same decision as before +constexpr sint32 QVAULT_ENDED_PROPOSAL = 12; // Proposal voting period has ended +constexpr sint32 QVAULT_NO_VOTING_POWER = 13; // User has no voting power +constexpr sint32 QVAULT_FAILED = 14; // Operation failed +constexpr sint32 QVAULT_INSUFFICIENT_SHARE = 15; // Insufficient shares for operation +constexpr sint32 QVAULT_ERROR_TRANSFER_ASSET = 16; // Asset transfer error occurred +constexpr sint32 QVAULT_INSUFFICIENT_VOTING_POWER = 17; // User doesn't have minimum voting power (10000) +constexpr sint32 QVAULT_INPUT_ERROR = 18; // Invalid input parameters +constexpr sint32 QVAULT_OVERFLOW_STAKER = 19; // Maximum number of stakers exceeded +constexpr sint32 QVAULT_INSUFFICIENT_SHARE_OR_VOTING_POWER = 20; // User doesn't have minimum voting power (10000) or shares +constexpr sint32 QVAULT_NOT_FOUND_STAKER_ADDRESS = 21; // User not found in staker list + +// Return result of proposal constants +constexpr uint8 QVAULT_PROPOSAL_PASSED = 0; +constexpr uint8 QVAULT_PROPOSAL_REJECTED = 1; +constexpr uint8 QVAULT_PROPOSAL_INSUFFICIENT_QUORUM = 2; +constexpr uint8 QVAULT_PROPOSAL_INSUFFICIENT_VOTING_POWER = 3; +constexpr uint8 QVAULT_PROPOSAL_INSUFFICIENT_QCAP = 4; +constexpr uint8 QVAULT_PROPOSAL_NOT_STARTED = 5; + +/** + * QVAULT2 - Placeholder struct for future use + */ struct QVAULT2 { }; +/** + * QVAULT Contract + * Main contract for managing QCAP token staking, voting, and governance + * Inherits from ContractBase to provide basic contract functionality + */ struct QVAULT : public ContractBase { public: - /* - submitAuthAddress PROCEDURE - multisig addresses can submit the new multisig address in this procedure. - */ + /** + * Input structure for getData function + * No input parameters required - returns all contract state data + */ + struct getData_input + { + }; - struct submitAuthAddress_input + /** + * Output structure for getData function + * Contains comprehensive contract state information including: + * - Administrative data (fees) + * - Financial data (funds, revenue, market cap) + * - Staking data (staked amounts, voting power) + * - Proposal counts for each type + * - Configuration parameters (percentages, limits) + */ + struct getData_output { - id newAddress; + sint32 returnCode; // Status code indicating success or failure + uint64 totalVotingPower; // Total voting power across all stakers + uint64 proposalCreateFund; // Fund accumulated from proposal fees + uint64 reinvestingFund; // Fund available for reinvestment + uint64 totalEpochRevenue; // Total revenue for current epoch + uint64 fundForBurn; // Fund allocated for token burning + uint64 totalStakedQcapAmount; // Total amount of QCAP tokens staked + uint64 qcapMarketCap; // Current QCAP market capitalization + uint64 raisedFundByQcap; // Total funds raised from QCAP sales + uint64 lastRoundPriceOfQcap; // QCAP price from last fundraising round + uint64 revenueByQearn; // Revenue generated from QEarn operations + uint32 qcapSoldAmount; // Total QCAP tokens sold to date + uint32 shareholderDividend; // Dividend percentage for shareholders (per mille) + uint32 QCAPHolderPermille; // Revenue allocation for QCAP holders (per mille) + uint32 reinvestingPermille; // Revenue allocation for reinvestment (per mille) + uint32 burnPermille; // Revenue allocation for burning (per mille) + uint32 qcapBurnPermille; // Revenue allocation for QCAP burning (per mille) + uint32 numberOfStaker; // Number of active stakers + uint32 numberOfVotingPower; // Number of users with voting power + uint32 numberOfGP; // Number of General Proposals + uint32 numberOfQCP; // Number of Quorum Change Proposals + uint32 numberOfIPOP; // Number of IPO Participation Proposals + uint32 numberOfQEarnP; // Number of QEarn Participation Proposals + uint32 numberOfFundP; // Number of Fundraising Proposals + uint32 numberOfMKTP; // Number of Marketplace Proposals + uint32 numberOfAlloP; // Number of Allocation Proposals + uint32 transferRightsFee; // Fee for transferring share management rights + uint32 minQuorumRq; // Minimum quorum requirement (330) + uint32 maxQuorumRq; // Maximum quorum requirement (670) + uint32 totalQcapBurntAmount; // Total QCAP tokens burned to date + uint32 circulatingSupply; // Current circulating supply of QCAP + uint32 quorumPercent; // Current quorum percentage for proposals }; - struct submitAuthAddress_output + /** + * Input structure for stake function + * @param amount Number of QCAP tokens to stake + */ + struct stake_input { + uint32 amount; }; - /* - changeAuthAddress PROCEDURE - the new multisig address can be changed by multisig address in this procedure. - the new multisig address should be submitted by the 2 multisig addresses in submitAuthAddress. if else, it will not be changed with new address - */ + /** + * Output structure for stake function + * @param returnCode Status code indicating success or failure + */ + struct stake_output + { + sint32 returnCode; + }; - struct changeAuthAddress_input + /** + * Input structure for unStake function + * @param amount Number of QCAP tokens to unstake + */ + struct unStake_input { - uint32 numberOfChangedAddress; + uint32 amount; }; - struct changeAuthAddress_output + /** + * Output structure for unStake function + * @param returnCode Status code indicating success or failure + */ + struct unStake_output { + sint32 returnCode; }; - /* - submitDistributionPermille PROCEDURE - the new distribution Permilles can be submitted by the multisig addresses in this procedure. - */ + /** + * Input structure for submitGP (General Proposal) function + * @param url URL containing proposal details (max 256 bytes) + */ + struct submitGP_input + { + Array url; + }; - struct submitDistributionPermille_input + /** + * Output structure for submitGP function + * @param returnCode Status code indicating success or failure + */ + struct submitGP_output { - uint32 newQCAPHolderPermille; - uint32 newReinvestingPermille; - uint32 newDevPermille; + sint32 returnCode; }; - struct submitDistributionPermille_output + /** + * Input structure for submitQCP (Quorum Change Proposal) function + * @param newQuorumPercent New quorum percentage to set (330-670) + * @param url URL containing proposal details (max 256 bytes) + */ + struct submitQCP_input { + uint32 newQuorumPercent; + Array url; }; - /* - changeDistributionPermille PROCEDURE - the new distribution Permilles can be changed by multisig address in this procedure. - the new distribution Permilles should be submitted by multsig addresses in submitDistributionPermille PROCEDURE. if else, it will not be changed with new Permilles. - */ + /** + * Output structure for submitQCP function + * @param returnCode Status code indicating success or failure + */ + struct submitQCP_output + { + sint32 returnCode; + }; - struct changeDistributionPermille_input + /** + * Input structure for submitIPOP (IPO Participation Proposal) function + * @param ipoContractIndex Index of the IPO contract to participate in + * @param url URL containing proposal details (max 256 bytes) + */ + struct submitIPOP_input { - uint32 newQCAPHolderPermille; - uint32 newReinvestingPermille; - uint32 newDevPermille; + uint32 ipoContractIndex; + Array url; }; - struct changeDistributionPermille_output + /** + * Output structure for submitIPOP function + * @param returnCode Status code indicating success or failure + */ + struct submitIPOP_output { + sint32 returnCode; }; - /* - submitReinvestingAddress PROCEDURE - the new reinvestingAddress can be submitted by the multisig addresses in this procedure. - */ + /** + * Input structure for submitQEarnP (QEarn Participation Proposal) function + * @param amountPerEpoch Amount to invest per epoch + * @param numberOfEpoch Number of epochs to participate (max 52) + * @param url URL containing proposal details (max 256 bytes) + */ + struct submitQEarnP_input + { + uint64 amountPerEpoch; + uint32 numberOfEpoch; + Array url; + }; - struct submitReinvestingAddress_input + /** + * Output structure for submitQEarnP function + * @param returnCode Status code indicating success or failure + */ + struct submitQEarnP_output { - id newAddress; + sint32 returnCode; }; - struct submitReinvestingAddress_output + /** + * Input structure for submitFundP (Fundraising Proposal) function + * @param priceOfOneQcap Price per QCAP token in qubic + * @param amountOfQcap Amount of QCAP tokens to sell + * @param url URL containing proposal details (max 256 bytes) + */ + struct submitFundP_input { + uint64 priceOfOneQcap; + uint32 amountOfQcap; + Array url; }; - /* - changeReinvestingAddress PROCEDURE - the new reinvesting address can be changed by multisig address in this procedure. - the new reinvesting address should be submitted by multsig addresses in submitReinvestingAddress. if else, it will not be changed with new address. - */ + /** + * Output structure for submitFundP function + * @param returnCode Status code indicating success or failure + */ + struct submitFundP_output + { + sint32 returnCode; + }; - struct changeReinvestingAddress_input + /** + * Input structure for submitMKTP (Marketplace Proposal) function + * @param amountOfQubic Amount of qubic to spend + * @param shareName Name/identifier of the share to purchase + * @param amountOfQcap Amount of QCAP tokens to offer + * @param indexOfShare Index of the share in the marketplace + * @param amountOfShare Amount of shares to purchase + * @param url URL containing proposal details (max 256 bytes) + */ + struct submitMKTP_input { - id newAddress; + uint64 amountOfQubic; + uint64 shareName; + uint32 amountOfQcap; + uint32 indexOfShare; + uint32 amountOfShare; + Array url; }; - struct changeReinvestingAddress_output + /** + * Output structure for submitMKTP function + * @param returnCode Status code indicating success or failure + */ + struct submitMKTP_output { + sint32 returnCode; + }; + /** + * Input structure for submitAlloP (Allocation Proposal) function + * @param reinvested Percentage for reinvestment (per mille) + * @param burn Percentage for burning (per mille) + * @param distribute Percentage for distribution (per mille) + * @param url URL containing proposal details (max 256 bytes) + * Note: All percentages must sum to 970 (per mille) + */ + struct submitAlloP_input + { + uint32 reinvested; + uint32 burn; + uint32 distribute; + Array url; }; - struct getData_input + /** + * Output structure for submitAlloP function + * @param returnCode Status code indicating success or failure + */ + struct submitAlloP_output { + sint32 returnCode; }; - struct getData_output + /** + * Input structure for voteInProposal function + * @param priceOfIPO IPO price for IPO participation proposals + * @param proposalType Type of proposal (1=GP, 2=QCP, 3=IPOP, 4=QEarnP, 5=FundP, 6=MKTP, 7=AlloP) + * @param proposalId ID of the proposal to vote on + * @param yes Voting decision (1=yes, 0=no) + */ + struct voteInProposal_input { - uint64 numberOfBannedAddress; - uint32 shareholderDividend, QCAPHolderPermille, reinvestingPermille, devPermille; - id authAddress1, authAddress2, authAddress3, reinvestingAddress, adminAddress; - id newAuthAddress1, newAuthAddress2, newAuthAddress3; - id newReinvestingAddress1, newReinvestingAddress2, newReinvestingAddress3; - id newAdminAddress1, newAdminAddress2, newAdminAddress3; - id bannedAddress1, bannedAddress2, bannedAddress3; - id unbannedAddress1, unbannedAddress2, unbannedAddress3; + uint64 priceOfIPO; + uint32 proposalType; + uint32 proposalId; + bit yes; }; - /* - submitAdminAddress PROCEDURE - the new adminAddress can be submitted by the multisig addresses in this procedure. - */ + /** + * Output structure for voteInProposal function + * @param returnCode Status code indicating success or failure + */ + struct voteInProposal_output + { + sint32 returnCode; + }; - struct submitAdminAddress_input + /** + * Input structure for buyQcap function + * @param amount Number of QCAP tokens to purchase + */ + struct buyQcap_input { - id newAddress; + uint32 amount; }; - struct submitAdminAddress_output + /** + * Output structure for buyQcap function + * @param returnCode Status code indicating success or failure + */ + struct buyQcap_output { + sint32 returnCode; }; - /* - changeAdminAddress PROCEDURE - the new admin address can be changed by multisig address in this procedure. - the new admin address should be submitted by multsig addresses in submitAdminAddress PROCEDURE. if else, it will not be changed with new address. - */ + /** + * Input structure for TransferShareManagementRights function + * @param asset Asset information (name and issuer) + * @param numberOfShares Number of shares to transfer management rights for + * @param newManagingContractIndex Index of the new managing contract + */ + struct TransferShareManagementRights_input + { + Asset asset; + sint64 numberOfShares; + uint32 newManagingContractIndex; + }; + + /** + * Output structure for TransferShareManagementRights function + * @param transferredNumberOfShares Number of shares successfully transferred + * @param returnCode Status code indicating success or failure + */ + struct TransferShareManagementRights_output + { + sint64 transferredNumberOfShares; + sint32 returnCode; + }; + +protected: - struct changeAdminAddress_input + /** + * Staking information structure + * Tracks individual staker addresses and their staked amounts + */ + struct stakingInfo { - id newAddress; + id stakerAddress; // Address of the staker + uint32 amount; // Amount of QCAP tokens staked }; - struct changeAdminAddress_output + // Storage arrays for staking and voting power data + Array staker; // Array of all stakers (max 1M stakers) + Array votingPower; // Array of users with voting power + + /** + * General Proposal (GP) information structure + * Stores details about general governance proposals + */ + struct GPInfo { + id proposer; // Address of the proposal creator + uint32 currentTotalVotingPower; // Total voting power when proposal was created + uint32 numberOfYes; // Number of yes votes received + uint32 numberOfNo; // Number of no votes received + uint32 proposedEpoch; // Epoch when proposal was created + uint32 currentQuorumPercent; // Quorum percentage when proposal was created + Array url; // URL containing proposal details + uint8 result; // Proposal result: 0=passed, 1=rejected, 2=insufficient quorum }; - /* - submitBannedAddress PROCEDURE - the banned addresses can be submitted by multisig address in this procedure. - */ - struct submitBannedAddress_input + // Storage array for general proposals + Array GP; + + /** + * Quorum Change Proposal (QCP) information structure + * Stores details about proposals to change the voting quorum percentage + */ + struct QCPInfo { - id bannedAddress; + id proposer; // Address of the proposal creator + uint32 currentTotalVotingPower; // Total voting power when proposal was created + uint32 numberOfYes; // Number of yes votes received + uint32 numberOfNo; // Number of no votes received + uint32 proposedEpoch; // Epoch when proposal was created + uint32 currentQuorumPercent; // Current quorum percentage + uint32 newQuorumPercent; // Proposed new quorum percentage + Array url; // URL containing proposal details + uint8 result; // Proposal result: 0=passed, 1=rejected, 2=insufficient quorum }; - struct submitBannedAddress_output + // Storage array for quorum change proposals + Array QCP; + + /** + * IPO Participation Proposal (IPOP) information structure + * Stores details about proposals to participate in IPO contracts + */ + struct IPOPInfo { + id proposer; // Address of the proposal creator + uint64 totalWeight; // Total weighted voting power for IPO participation + uint64 assignedFund; // Amount of funds assigned for IPO participation + uint32 currentTotalVotingPower; // Total voting power when proposal was created + uint32 numberOfYes; // Number of yes votes received + uint32 numberOfNo; // Number of no votes received + uint32 proposedEpoch; // Epoch when proposal was created + uint32 ipoContractIndex; // Index of the IPO contract to participate in + uint32 currentQuorumPercent; // Current quorum percentage + Array url; // URL containing proposal details + uint8 result; // Proposal result: 0=passed, 1=rejected, 2=insufficient quorum, 3=insufficient funds }; - /* - saveBannedAddress PROCEDURE - the banned address can be changed by multisig address in this procedure. - the banned address should be submitted by multisig addresses in submitBannedAddress PROCEDURE. if else, it will not be saved. - */ + // Storage array for IPO participation proposals + Array IPOP; - struct saveBannedAddress_input + /** + * QEarn Participation Proposal (QEarnP) information structure + * Stores details about proposals to participate in QEarn contracts + */ + struct QEarnPInfo { - id bannedAddress; + id proposer; // Address of the proposal creator + uint64 amountOfInvestPerEpoch; // Amount to invest per epoch + uint64 assignedFundPerEpoch; // Amount of funds assigned per epoch + uint32 currentTotalVotingPower; // Total voting power when proposal was created + uint32 numberOfYes; // Number of yes votes received + uint32 numberOfNo; // Number of no votes received + uint32 proposedEpoch; // Epoch when proposal was created + uint32 currentQuorumPercent; // Current quorum percentage + Array url; // URL containing proposal details + uint8 numberOfEpoch; // Number of epochs to participate + uint8 result; // Proposal result: 0=passed, 1=rejected, 2=insufficient quorum, 3=insufficient funds }; - struct saveBannedAddress_output + // Storage array for QEarn participation proposals + Array QEarnP; + + /** + * Fundraising Proposal (FundP) information structure + * Stores details about proposals to sell QCAP tokens for fundraising + */ + struct FundPInfo { + id proposer; // Address of the proposal creator + uint64 pricePerOneQcap; // Price per QCAP token in qubic + uint32 currentTotalVotingPower; // Total voting power when proposal was created + uint32 numberOfYes; // Number of yes votes received + uint32 numberOfNo; // Number of no votes received + uint32 amountOfQcap; // Total amount of QCAP tokens to sell + uint32 restSaleAmount; // Remaining amount of QCAP tokens available for sale + uint32 proposedEpoch; // Epoch when proposal was created + uint32 currentQuorumPercent; // Current quorum percentage + Array url; // URL containing proposal details + uint8 result; // Proposal result: 0=passed, 1=rejected, 2=insufficient quorum }; - /* - submitUnbannedAddress PROCEDURE - the unbanned addresses can be submitted by multisig address in this procedure. - */ + // Storage array for fundraising proposals + Array FundP; - struct submitUnbannedAddress_input + /** + * Marketplace Proposal (MKTP) information structure + * Stores details about proposals to purchase shares from the marketplace + */ + struct MKTPInfo { - id unbannedAddress; + id proposer; // Address of the proposal creator + uint64 amountOfQubic; // Amount of qubic to spend on shares + uint64 shareName; // Name/identifier of the share to purchase + uint32 currentTotalVotingPower; // Total voting power when proposal was created + uint32 numberOfYes; // Number of yes votes received + uint32 numberOfNo; // Number of no votes received + uint32 amountOfQcap; // Amount of QCAP tokens to offer + uint32 currentQuorumPercent; // Current quorum percentage + uint32 proposedEpoch; // Epoch when proposal was created + uint32 shareIndex; // Index of the share in the marketplace + uint32 amountOfShare; // Amount of shares to purchase + Array url; // URL containing proposal details + uint8 result; // Proposal result: 0=passed, 1=rejected, 2=insufficient quorum, 3=insufficient funds, 4=insufficient QCAP }; - struct submitUnbannedAddress_output + // Storage array for marketplace proposals + Array MKTP; + + /** + * Allocation Proposal (AlloP) information structure + * Stores details about proposals to change revenue allocation percentages + * All percentages are in per mille (parts per thousand) + */ + struct AlloPInfo { + id proposer; // Address of the proposal creator + uint32 currentTotalVotingPower; // Total voting power when proposal was created + uint32 numberOfYes; // Number of yes votes received + uint32 numberOfNo; // Number of no votes received + uint32 proposedEpoch; // Epoch when proposal was created + uint32 currentQuorumPercent; // Current quorum percentage + uint32 reinvested; // Percentage for reinvestment (per mille) + uint32 distributed; // Percentage for distribution (per mille) + uint32 burnQcap; // Percentage for QCAP burning (per mille) + Array url; // URL containing proposal details + uint8 result; // Proposal result: 0=passed, 1=rejected, 2=insufficient quorum }; - /* - unblockBannedAddress PROCEDURE - the banned address can be unblocked by multisig address in this procedure. - the unbanned address should be submitted by multisig addresses in submitUnbannedAddress PROCEDURE. if else, it will not be unblocked. - */ + // Storage array for allocation proposals + Array AlloP; - struct unblockBannedAddress_input + // Contract configuration and administration + id QCAP_ISSUER; // Address that can issue QCAP tokens + + /** + * Vote status information structure + * Tracks individual user votes on proposals + */ + struct voteStatusInfo { - id unbannedAddress; + uint64 priceOfIPO; // IPO price for IPO participation proposals + uint32 proposalId; // ID of the proposal voted on + uint8 proposalType; // Type of proposal (1-7) + bit decision; // Voting decision (1=yes, 0=no) }; - struct unblockBannedAddress_output + // Storage for user voting history and vote counts + HashMap, QVAULT_X_MULTIPLIER> vote; // User voting history (max 16 votes per user) + HashMap countOfVote; // Count of votes per user + + // Financial state variables + uint64 proposalCreateFund; // Fund accumulated from proposal fees + uint64 reinvestingFund; // Fund available for reinvestment + uint64 totalEpochRevenue; // Total revenue for current epoch + uint64 fundForBurn; // Fund allocated for token burning + uint64 totalHistoryRevenue; // Total historical revenue + uint64 rasiedFundByQcap; // Total funds raised from QCAP sales + uint64 lastRoundPriceOfQcap; // QCAP price from last fundraising round + uint64 revenueByQearn; // Revenue generated from QEarn operations + + // Per-epoch revenue tracking arrays + Array revenueInQcapPerEpoch; // Revenue in QCAP per epoch + Array revenueForOneQcapPerEpoch; // Revenue per QCAP token per epoch + Array revenueForOneQvaultPerEpoch; // Revenue per QVAULT share per epoch + Array revenueForReinvestPerEpoch; // Revenue for reinvestment per epoch + Array revenuePerShare; // Revenue per share per epoch + Array burntQcapAmPerEpoch; // QCAP amount burned per epoch + + // Staking and voting state + uint32 totalVotingPower; // Total voting power across all stakers + uint32 totalStakedQcapAmount; // Total amount of QCAP tokens staked + uint32 qcapSoldAmount; // Total QCAP tokens sold to date + + // Revenue allocation percentages (per mille) + uint32 shareholderDividend; // Dividend percentage for shareholders + uint32 QCAPHolderPermille; // Revenue allocation for QCAP holders + uint32 reinvestingPermille; // Revenue allocation for reinvestment + uint32 burnPermille; // Revenue allocation for burning + uint32 qcapBurnPermille; // Revenue allocation for QCAP burning + uint32 totalQcapBurntAmount; // Total QCAP tokens burned to date + + // Counters for stakers and voting power + uint32 numberOfStaker; // Number of active stakers + uint32 numberOfVotingPower; // Number of users with voting power + + // Proposal counters for each type + uint32 numberOfGP; // Number of General Proposals + uint32 numberOfQCP; // Number of Quorum Change Proposals + uint32 numberOfIPOP; // Number of IPO Participation Proposals + uint32 numberOfQEarnP; // Number of QEarn Participation Proposals + uint32 numberOfFundP; // Number of Fundraising Proposals + uint32 numberOfMKTP; // Number of Marketplace Proposals + uint32 numberOfAlloP; // Number of Allocation Proposals + + // Configuration parameters + uint32 transferRightsFee; // Fee for transferring share management rights + uint32 quorumPercent; // Current quorum percentage for proposals + + /** + * @return pack qvault datetime data from year, month, day, hour, minute, second to a uint32 + * year is counted from 24 (2024) + */ + inline static void packQvaultDate(uint32 _year, uint32 _month, uint32 _day, uint32 _hour, uint32 _minute, uint32 _second, uint32& res) + { + res = ((_year - 24) << 26) | (_month << 22) | (_day << 17) | (_hour << 12) | (_minute << 6) | (_second); + } + + inline static uint32 qvaultGetYear(uint32 data) + { + return ((data >> 26) + 24); + } + inline static uint32 qvaultGetMonth(uint32 data) + { + return ((data >> 22) & 0b1111); + } + inline static uint32 qvaultGetDay(uint32 data) + { + return ((data >> 17) & 0b11111); + } + inline static uint32 qvaultGetHour(uint32 data) + { + return ((data >> 12) & 0b11111); + } + inline static uint32 qvaultGetMinute(uint32 data) + { + return ((data >> 6) & 0b111111); + } + inline static uint32 qvaultGetSecond(uint32 data) + { + return (data & 0b111111); + } + /* + * @return unpack qvault datetime from uin32 to year, month, day, hour, minute, secon + */ + inline static void unpackQvaultDate(uint8& _year, uint8& _month, uint8& _day, uint8& _hour, uint8& _minute, uint8& _second, uint32 data) + { + _year = qvaultGetYear(data); // 6 bits + _month = qvaultGetMonth(data); //4bits + _day = qvaultGetDay(data); //5bits + _hour = qvaultGetHour(data); //5bits + _minute = qvaultGetMinute(data); //6bits + _second = qvaultGetSecond(data); //6bits + } + + /** + * Local variables for getData function + */ + struct getData_locals { + Asset qcapAsset; // QCAP asset information + sint32 _t; // Loop counter variable }; -protected: - - id QCAP_ISSUER; - id authAddress1, authAddress2, authAddress3, newAuthAddress1, newAuthAddress2, newAuthAddress3; - id reinvestingAddress, newReinvestingAddress1, newReinvestingAddress2, newReinvestingAddress3; - id adminAddress, newAdminAddress1, newAdminAddress2, newAdminAddress3; - id bannedAddress1, bannedAddress2, bannedAddress3; - id unbannedAddress1, unbannedAddress2, unbannedAddress3; - Array bannedAddress; - uint32 numberOfBannedAddress; - uint32 shareholderDividend, QCAPHolderPermille, reinvestingPermille, devPermille, burnPermille; - uint32 newQCAPHolderPermille1, newReinvestingPermille1, newDevPermille1; - uint32 newQCAPHolderPermille2, newReinvestingPermille2, newDevPermille2; - uint32 newQCAPHolderPermille3, newReinvestingPermille3, newDevPermille3; - - PUBLIC_PROCEDURE(submitAuthAddress) + /** + * Retrieves comprehensive contract state data + * Returns all important contract information including financial data, + * staking statistics, proposal counts, and configuration parameters + * + * @param input No input parameters required + * @param output Comprehensive contract state data + */ + PUBLIC_FUNCTION_WITH_LOCALS(getData) { - if(qpi.invocator() == state.authAddress1) - { - state.newAuthAddress1 = input.newAddress; - } - if(qpi.invocator() == state.authAddress2) - { - state.newAuthAddress2 = input.newAddress; - } - if(qpi.invocator() == state.authAddress3) + output.quorumPercent = state.quorumPercent; + output.totalVotingPower = state.totalVotingPower; + output.proposalCreateFund = state.proposalCreateFund; + output.reinvestingFund = state.reinvestingFund; + output.totalEpochRevenue = state.totalEpochRevenue; + output.shareholderDividend = state.shareholderDividend; + output.QCAPHolderPermille = state.QCAPHolderPermille; + output.reinvestingPermille = state.reinvestingPermille; + output.burnPermille = state.burnPermille; + output.qcapBurnPermille = state.qcapBurnPermille; + output.numberOfStaker = state.numberOfStaker; + output.numberOfVotingPower = state.numberOfVotingPower; + output.qcapSoldAmount = state.qcapSoldAmount; + output.numberOfGP = state.numberOfGP; + output.numberOfQCP = state.numberOfQCP; + output.numberOfIPOP = state.numberOfIPOP; + output.numberOfQEarnP = state.numberOfQEarnP; + output.numberOfFundP = state.numberOfFundP; + output.numberOfMKTP = state.numberOfMKTP; + output.numberOfAlloP = state.numberOfAlloP; + output.transferRightsFee = state.transferRightsFee; + output.fundForBurn = state.fundForBurn; + output.totalStakedQcapAmount = state.totalStakedQcapAmount; + output.minQuorumRq = QVAULT_MIN_QUORUM_REQ; + output.maxQuorumRq = QVAULT_MAX_QUORUM_REQ; + output.totalQcapBurntAmount = state.totalQcapBurntAmount; + output.raisedFundByQcap = state.rasiedFundByQcap; + + locals.qcapAsset.assetName = QVAULT_QCAP_ASSETNAME; + locals.qcapAsset.issuer = state.QCAP_ISSUER; + + output.circulatingSupply = (uint32)(qpi.numberOfShares(locals.qcapAsset) - qpi.numberOfShares(locals.qcapAsset, AssetOwnershipSelect::byOwner(SELF), AssetPossessionSelect::byPossessor(SELF)) + state.totalStakedQcapAmount); + for (locals._t = state.numberOfFundP - 1; locals._t >= 0; locals._t--) { - state.newAuthAddress3 = input.newAddress; + if (state.FundP.get(locals._t).result == 0 && state.FundP.get(locals._t).proposedEpoch + 1 < qpi.epoch()) + { + output.qcapMarketCap = output.circulatingSupply * state.FundP.get(locals._t).pricePerOneQcap; + break; + } } - + output.lastRoundPriceOfQcap = state.lastRoundPriceOfQcap; + output.revenueByQearn = state.revenueByQearn; + output.returnCode = QVAULT_SUCCESS; } - struct changeAuthAddress_locals { - bit succeed; + /** + * Local variables for stake function + */ + struct stake_locals + { + stakingInfo user; // User staking information + sint32 _t; // Loop counter variable + bool isNewStaker; // Flag to check if the user is a new staker }; - PUBLIC_PROCEDURE_WITH_LOCALS(changeAuthAddress) + /** + * Stakes QCAP tokens to earn voting power and revenue + * Transfers QCAP tokens from user to contract and updates staking records + * + * @param input Amount of QCAP tokens to stake + * @param output Status code indicating success or failure + */ + PUBLIC_PROCEDURE_WITH_LOCALS(stake) { - if(qpi.invocator() != state.authAddress1 && qpi.invocator() != state.authAddress2 && qpi.invocator() != state.authAddress3) + if (input.amount > (uint32)qpi.numberOfPossessedShares(QVAULT_QCAP_ASSETNAME, state.QCAP_ISSUER, qpi.invocator(), qpi.invocator(), SELF_INDEX, SELF_INDEX)) { + output.returnCode = QVAULT_INSUFFICIENT_QCAP; return ; } - locals.succeed = 0; - - if(qpi.invocator() != state.authAddress1 && input.numberOfChangedAddress == 1 && state.newAuthAddress2 != NULL_ID && state.newAuthAddress2 == state.newAuthAddress3) + for (locals._t = 0 ; locals._t < (sint32)state.numberOfStaker; locals._t++) { - state.authAddress1 = state.newAuthAddress2; - locals.succeed = 1; + if (state.staker.get(locals._t).stakerAddress == qpi.invocator()) + { + break; + } } - if(qpi.invocator() != state.authAddress2 && input.numberOfChangedAddress == 2 && state.newAuthAddress1 != NULL_ID && state.newAuthAddress1 == state.newAuthAddress3) + if (locals._t == state.numberOfStaker) { - state.authAddress2 = state.newAuthAddress1; - locals.succeed = 1; + if (state.numberOfStaker >= QVAULT_X_MULTIPLIER) + { + output.returnCode = QVAULT_OVERFLOW_STAKER; + return ; + } + locals.user.amount = input.amount; + locals.user.stakerAddress = qpi.invocator(); + state.numberOfStaker++; + locals.isNewStaker = 1; } - - if(qpi.invocator() != state.authAddress3 && input.numberOfChangedAddress == 3 && state.newAuthAddress1 != NULL_ID && state.newAuthAddress1 == state.newAuthAddress2) + else { - state.authAddress3 = state.newAuthAddress1; - locals.succeed = 1; + locals.user.amount = state.staker.get(locals._t).amount + input.amount; + locals.user.stakerAddress = state.staker.get(locals._t).stakerAddress; } - if(locals.succeed == 1) + if (qpi.transferShareOwnershipAndPossession(QVAULT_QCAP_ASSETNAME, state.QCAP_ISSUER, qpi.invocator(), qpi.invocator(), input.amount, SELF) < 0) { - state.newAuthAddress1 = NULL_ID; - state.newAuthAddress2 = NULL_ID; - state.newAuthAddress3 = NULL_ID; + if (locals.isNewStaker == 1) + { + state.numberOfStaker--; + } + output.returnCode = QVAULT_ERROR_TRANSFER_ASSET; + return ; } + + state.totalStakedQcapAmount += input.amount; + state.staker.set(locals._t, locals.user); + output.returnCode = QVAULT_SUCCESS; } - PUBLIC_PROCEDURE(submitDistributionPermille) + /** + * Local variables for unStake function + */ + struct unStake_locals + { + stakingInfo user; // User staking information + sint32 _t; // Loop counter variable + }; + + /** + * Unstakes QCAP tokens, reducing voting power + * Transfers QCAP tokens back to user and updates staking records + * + * @param input Amount of QCAP tokens to unstake + * @param output Status code indicating success or failure + */ + PUBLIC_PROCEDURE_WITH_LOCALS(unStake) { - if(input.newDevPermille + input.newQCAPHolderPermille + input.newReinvestingPermille + state.shareholderDividend + state.burnPermille != 1000) + if (input.amount == 0) { + output.returnCode = QVAULT_INPUT_ERROR; return ; } - if(qpi.invocator() == state.authAddress1) + for (locals._t = 0 ; locals._t < (sint32)state.numberOfStaker; locals._t++) { - state.newDevPermille1 = input.newDevPermille; - state.newQCAPHolderPermille1 = input.newQCAPHolderPermille; - state.newReinvestingPermille1 = input.newReinvestingPermille; + if (state.staker.get(locals._t).stakerAddress == qpi.invocator()) + { + if (state.staker.get(locals._t).amount < input.amount) + { + output.returnCode = QVAULT_NOT_ENOUGH_STAKE; + } + else if (state.staker.get(locals._t).amount >= input.amount) + { + if (qpi.transferShareOwnershipAndPossession(QVAULT_QCAP_ASSETNAME, state.QCAP_ISSUER, SELF, SELF, input.amount, qpi.invocator()) < 0) + { + output.returnCode = QVAULT_INSUFFICIENT_QCAP; + return ; + } + + locals.user.stakerAddress = state.staker.get(locals._t).stakerAddress; + locals.user.amount = state.staker.get(locals._t).amount - input.amount; + state.staker.set(locals._t, locals.user); + + state.totalStakedQcapAmount -= input.amount; + output.returnCode = QVAULT_SUCCESS; + if (locals.user.amount == 0) + { + state.numberOfStaker--; + state.staker.set(locals._t, state.staker.get(state.numberOfStaker)); + } + } + return ; + } } - if(qpi.invocator() == state.authAddress2) + output.returnCode = QVAULT_NOT_STAKER; + } + + /** + * Local variables for submitGP function + */ + struct submitGP_locals + { + Asset qvaultShare; // QVAULT share asset information + GPInfo newProposal; // New general proposal to create + sint32 _t; // Loop counter variable + }; + + /** + * Submits a General Proposal (GP) for governance voting + * Requires minimum voting power (10000) or QVAULT shares + * Charges proposal fee and creates new proposal record + * + * @param input URL containing proposal details + * @param output Status code indicating success or failure + */ + PUBLIC_PROCEDURE_WITH_LOCALS(submitGP) + { + locals.qvaultShare.assetName = QVAULT_QVAULT_ASSETNAME; + locals.qvaultShare.issuer = NULL_ID; + + for (locals._t = 0 ; locals._t < (sint64)state.numberOfVotingPower; locals._t++) { - state.newDevPermille2 = input.newDevPermille; - state.newQCAPHolderPermille2 = input.newQCAPHolderPermille; - state.newReinvestingPermille2 = input.newReinvestingPermille; + if (state.votingPower.get(locals._t).stakerAddress == qpi.invocator()) + { + if (state.votingPower.get(locals._t).amount >= QVAULT_MIN_VOTING_POWER) + { + break; + } + else + { + if (qpi.numberOfShares(locals.qvaultShare, AssetOwnershipSelect::byOwner(qpi.invocator()), AssetPossessionSelect::byPossessor(qpi.invocator())) <= 0) + { + output.returnCode = QVAULT_INSUFFICIENT_VOTING_POWER; + if (qpi.invocationReward() > 0) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + } + return ; + } + break; + } + } } - if(qpi.invocator() == state.authAddress3) + if(locals._t == state.numberOfVotingPower && qpi.numberOfShares(locals.qvaultShare, AssetOwnershipSelect::byOwner(qpi.invocator()), AssetPossessionSelect::byPossessor(qpi.invocator())) <= 0) + { + if (qpi.invocationReward() > 0) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + } + output.returnCode = QVAULT_NO_VOTING_POWER; + return ; + } + + if (qpi.invocationReward() < QVAULT_PROPOSAL_FEE) + { + if (qpi.invocationReward() > 0) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + } + output.returnCode = QVAULT_INSUFFICIENT_FUND; + return ; + } + if (state.numberOfGP >= QVAULT_MAX_NUMBER_OF_PROPOSAL) + { + if (qpi.invocationReward() > 0) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + } + output.returnCode = QVAULT_OVERFLOW_PROPOSAL; + return ; + } + if (qpi.invocationReward() > QVAULT_PROPOSAL_FEE) { - state.newDevPermille3 = input.newDevPermille; - state.newQCAPHolderPermille3 = input.newQCAPHolderPermille; - state.newReinvestingPermille3 = input.newReinvestingPermille; + qpi.transfer(qpi.invocator(), qpi.invocationReward() - QVAULT_PROPOSAL_FEE); } + state.proposalCreateFund += QVAULT_PROPOSAL_FEE; + locals.newProposal.currentQuorumPercent = state.quorumPercent; + locals.newProposal.currentTotalVotingPower = state.totalVotingPower; + for (locals._t = 0; locals._t < QVAULT_MAX_URLS_COUNT; locals._t++) + { + locals.newProposal.url.set(locals._t, input.url.get(locals._t)); + } + locals.newProposal.proposedEpoch = qpi.epoch(); + locals.newProposal.numberOfYes = 0; + locals.newProposal.numberOfNo = 0; + locals.newProposal.proposer = qpi.invocator(); + locals.newProposal.result = QVAULT_PROPOSAL_NOT_STARTED; + + state.GP.set(state.numberOfGP++, locals.newProposal); + output.returnCode = QVAULT_SUCCESS; } - PUBLIC_PROCEDURE(changeDistributionPermille) + /** + * Local variables for submitQCP function + */ + struct submitQCP_locals + { + Asset qvaultShare; // QVAULT share asset information + QCPInfo newProposal; // New quorum change proposal to create + sint32 _t; // Loop counter variable + }; + + /** + * Submits a Quorum Change Proposal (QCP) to modify voting quorum percentage + * Requires minimum voting power (10000) or QVAULT shares + * Charges proposal fee and creates new proposal record + * + * @param input New quorum percentage and URL containing proposal details + * @param output Status code indicating success or failure + */ + PUBLIC_PROCEDURE_WITH_LOCALS(submitQCP) { - if(qpi.invocator() != state.authAddress1 && qpi.invocator() != state.authAddress2 && qpi.invocator() != state.authAddress3) + locals.qvaultShare.assetName = QVAULT_QVAULT_ASSETNAME; + locals.qvaultShare.issuer = NULL_ID; + + for (locals._t = 0 ; locals._t < (sint64)state.numberOfVotingPower; locals._t++) { - return ; + if (state.votingPower.get(locals._t).stakerAddress == qpi.invocator()) + { + if (state.votingPower.get(locals._t).amount >= QVAULT_MIN_VOTING_POWER) + { + break; + } + else + { + if (qpi.numberOfShares(locals.qvaultShare, AssetOwnershipSelect::byOwner(qpi.invocator()), AssetPossessionSelect::byPossessor(qpi.invocator())) <= 0) + { + output.returnCode = QVAULT_INSUFFICIENT_VOTING_POWER; + if (qpi.invocationReward() > 0) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + } + return ; + } + break; + } + } } - if(input.newDevPermille + input.newQCAPHolderPermille + input.newReinvestingPermille + state.shareholderDividend + state.burnPermille != 1000) + if(locals._t == state.numberOfVotingPower && qpi.numberOfShares(locals.qvaultShare, AssetOwnershipSelect::byOwner(qpi.invocator()), AssetPossessionSelect::byPossessor(qpi.invocator())) <= 0) { + if (qpi.invocationReward() > 0) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + } + output.returnCode = QVAULT_NO_VOTING_POWER; return ; } - if(input.newDevPermille == 0 || input.newDevPermille != state.newDevPermille1 || state.newDevPermille1 != state.newDevPermille2 || state.newDevPermille2 != state.newDevPermille3) + if (qpi.invocationReward() < QVAULT_PROPOSAL_FEE) { + if (qpi.invocationReward() > 0) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + } + output.returnCode = QVAULT_INSUFFICIENT_FUND; return ; } - - if(input.newQCAPHolderPermille == 0 || input.newQCAPHolderPermille != state.newQCAPHolderPermille1 || state.newQCAPHolderPermille1 != state.newQCAPHolderPermille2 || state.newQCAPHolderPermille2 != state.newQCAPHolderPermille3) + if (state.numberOfQCP >= QVAULT_MAX_NUMBER_OF_PROPOSAL) { + if (qpi.invocationReward() > 0) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + } + output.returnCode = QVAULT_OVERFLOW_PROPOSAL; return ; } - - if(input.newReinvestingPermille == 0 || input.newReinvestingPermille != state.newReinvestingPermille1 || state.newReinvestingPermille1 != state.newReinvestingPermille2 || state.newReinvestingPermille2 != state.newReinvestingPermille3) + if (qpi.invocationReward() > QVAULT_PROPOSAL_FEE) { - return ; + qpi.transfer(qpi.invocator(), qpi.invocationReward() - QVAULT_PROPOSAL_FEE); } + state.proposalCreateFund += QVAULT_PROPOSAL_FEE; - state.devPermille = state.newDevPermille1; - state.QCAPHolderPermille = state.newQCAPHolderPermille1; - state.reinvestingPermille = state.newReinvestingPermille1; - - state.newDevPermille1 = 0; - state.newDevPermille2 = 0; - state.newDevPermille3 = 0; - - state.newQCAPHolderPermille1 = 0; - state.newQCAPHolderPermille2 = 0; - state.newQCAPHolderPermille3 = 0; - - state.newReinvestingPermille1 = 0; - state.newReinvestingPermille2 = 0; - state.newReinvestingPermille3 = 0; + locals.newProposal.currentQuorumPercent = state.quorumPercent; + locals.newProposal.currentTotalVotingPower = state.totalVotingPower; + for (locals._t = 0; locals._t < QVAULT_MAX_URLS_COUNT; locals._t++) + { + locals.newProposal.url.set(locals._t, input.url.get(locals._t)); + } + locals.newProposal.proposedEpoch = qpi.epoch(); + locals.newProposal.numberOfYes = 0; + locals.newProposal.numberOfNo = 0; + locals.newProposal.proposer = qpi.invocator(); + locals.newProposal.result = QVAULT_PROPOSAL_NOT_STARTED; + + locals.newProposal.newQuorumPercent = input.newQuorumPercent; + + state.QCP.set(state.numberOfQCP++, locals.newProposal); + output.returnCode = QVAULT_SUCCESS; } - PUBLIC_PROCEDURE(submitReinvestingAddress) + /** + * Local variables for submitIPOP function + */ + struct submitIPOP_locals + { + Asset qvaultShare; // QVAULT share asset information + IPOPInfo newProposal; // New IPO participation proposal to create + sint32 _t; // Loop counter variable + }; + + /** + * Submits an IPO Participation Proposal (IPOP) to participate in IPO contracts + * Requires minimum voting power (10000) or QVAULT shares + * Requires sufficient reinvesting fund (minimum 1B qubic) + * Charges proposal fee and creates new proposal record + * + * @param input IPO contract index and URL containing proposal details + * @param output Status code indicating success or failure + */ + PUBLIC_PROCEDURE_WITH_LOCALS(submitIPOP) { - if(qpi.invocator() == state.authAddress1) + if (state.reinvestingFund < QVAULT_IPO_PARTICIPATION_MIN_FUND) { - state.newReinvestingAddress1 = input.newAddress; + if (qpi.invocationReward() > 0) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + } + output.returnCode = QVAULT_INSUFFICIENT_FUND; + return ; } - if(qpi.invocator() == state.authAddress2) + locals.qvaultShare.assetName = QVAULT_QVAULT_ASSETNAME; + locals.qvaultShare.issuer = NULL_ID; + + for (locals._t = 0 ; locals._t < (sint64)state.numberOfVotingPower; locals._t++) { - state.newReinvestingAddress2 = input.newAddress; + if (state.votingPower.get(locals._t).stakerAddress == qpi.invocator()) + { + if (state.votingPower.get(locals._t).amount >= QVAULT_MIN_VOTING_POWER) + { + break; + } + else + { + if (qpi.numberOfShares(locals.qvaultShare, AssetOwnershipSelect::byOwner(qpi.invocator()), AssetPossessionSelect::byPossessor(qpi.invocator())) <= 0) + { + output.returnCode = QVAULT_INSUFFICIENT_VOTING_POWER; + if (qpi.invocationReward() > 0) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + } + return ; + } + break; + } + } } - if(qpi.invocator() == state.authAddress3) + + if(locals._t == state.numberOfVotingPower && qpi.numberOfShares(locals.qvaultShare, AssetOwnershipSelect::byOwner(qpi.invocator()), AssetPossessionSelect::byPossessor(qpi.invocator())) <= 0) { - state.newReinvestingAddress3 = input.newAddress; + if (qpi.invocationReward() > 0) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + } + output.returnCode = QVAULT_NO_VOTING_POWER; + return ; } - } - PUBLIC_PROCEDURE(changeReinvestingAddress) - { - if(qpi.invocator() != state.authAddress1 && qpi.invocator() != state.authAddress2 && qpi.invocator() != state.authAddress3) + if (qpi.invocationReward() < QVAULT_PROPOSAL_FEE) { + if (qpi.invocationReward() > 0) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + } + output.returnCode = QVAULT_INSUFFICIENT_FUND; return ; } - - if(input.newAddress == NULL_ID || input.newAddress != state.newReinvestingAddress1 || state.newReinvestingAddress1 != state.newReinvestingAddress2 || state.newReinvestingAddress2 != state.newReinvestingAddress3) + if (state.numberOfIPOP >= QVAULT_MAX_NUMBER_OF_PROPOSAL) { + if (qpi.invocationReward() > 0) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + } + output.returnCode = QVAULT_OVERFLOW_PROPOSAL; return ; } + if (qpi.invocationReward() > QVAULT_PROPOSAL_FEE) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward() - QVAULT_PROPOSAL_FEE); + } + state.proposalCreateFund += QVAULT_PROPOSAL_FEE; - state.reinvestingAddress = state.newReinvestingAddress1; - - state.newReinvestingAddress1 = NULL_ID; - state.newReinvestingAddress2 = NULL_ID; - state.newReinvestingAddress3 = NULL_ID; + locals.newProposal.currentQuorumPercent = state.quorumPercent; + locals.newProposal.currentTotalVotingPower = state.totalVotingPower; + for (locals._t = 0; locals._t < QVAULT_MAX_URLS_COUNT; locals._t++) + { + locals.newProposal.url.set(locals._t, input.url.get(locals._t)); + } + locals.newProposal.proposedEpoch = qpi.epoch(); + locals.newProposal.numberOfYes = 0; + locals.newProposal.numberOfNo = 0; + locals.newProposal.proposer = qpi.invocator(); + locals.newProposal.result = QVAULT_PROPOSAL_NOT_STARTED; + locals.newProposal.assignedFund = 0; + + locals.newProposal.ipoContractIndex = input.ipoContractIndex; + locals.newProposal.totalWeight = 0; + + state.IPOP.set(state.numberOfIPOP++, locals.newProposal); + output.returnCode = QVAULT_SUCCESS; } - PUBLIC_FUNCTION(getData) + /** + * Local variables for submitQEarnP function + */ + struct submitQEarnP_locals { - output.authAddress1 = state.authAddress1; - output.authAddress2 = state.authAddress2; - output.authAddress3 = state.authAddress3; - output.reinvestingAddress = state.reinvestingAddress; - output.shareholderDividend = state.shareholderDividend; - output.devPermille = state.devPermille; - output.QCAPHolderPermille = state.QCAPHolderPermille; - output.reinvestingPermille = state.reinvestingPermille; - output.adminAddress = state.adminAddress; - output.newAuthAddress1 = state.newAuthAddress1; - output.newAuthAddress2 = state.newAuthAddress2; - output.newAuthAddress3 = state.newAuthAddress3; - output.newAdminAddress1 = state.newAdminAddress1; - output.newAdminAddress2 = state.newAdminAddress2; - output.newAdminAddress3 = state.newAdminAddress3; - output.newReinvestingAddress1 = state.newReinvestingAddress1; - output.newReinvestingAddress2 = state.newReinvestingAddress2; - output.newReinvestingAddress3 = state.newReinvestingAddress3; - output.numberOfBannedAddress = state.numberOfBannedAddress; - output.bannedAddress1 = state.bannedAddress1; - output.bannedAddress2 = state.bannedAddress2; - output.bannedAddress3 = state.bannedAddress3; - output.unbannedAddress1 = state.unbannedAddress1; - output.unbannedAddress2 = state.unbannedAddress2; - output.unbannedAddress3 = state.unbannedAddress3; - - } + Asset qvaultShare; // QVAULT share asset information + QEarnPInfo newProposal; // New QEarn participation proposal to create + sint32 _t; // Loop counter variable + }; - PUBLIC_PROCEDURE(submitAdminAddress) + /** + * Submits a QEarn Participation Proposal (QEarnP) to invest in QEarn contracts + * Requires minimum voting power (10000) or QVAULT shares + * Maximum participation period is 52 epochs + * Requires sufficient reinvesting fund for the investment amount + * Charges proposal fee and creates new proposal record + * + * @param input Investment amount per epoch, number of epochs, and URL containing proposal details + * @param output Status code indicating success or failure + */ + PUBLIC_PROCEDURE_WITH_LOCALS(submitQEarnP) { - if(qpi.invocator() == state.authAddress1) + if (input.numberOfEpoch > 52) { - state.newAdminAddress1 = input.newAddress; + output.returnCode = QVAULT_INPUT_ERROR; + if (qpi.invocationReward() > 0) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + } + return ; } - if(qpi.invocator() == state.authAddress2) + if (input.amountPerEpoch * input.numberOfEpoch > state.reinvestingFund) { - state.newAdminAddress2 = input.newAddress; + output.returnCode = QVAULT_INSUFFICIENT_FUND; + if (qpi.invocationReward() > 0) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + } + return ; } - if(qpi.invocator() == state.authAddress3) + locals.qvaultShare.assetName = QVAULT_QVAULT_ASSETNAME; + locals.qvaultShare.issuer = NULL_ID; + + for (locals._t = 0 ; locals._t < (sint64)state.numberOfVotingPower; locals._t++) { - state.newAdminAddress3 = input.newAddress; + if (state.votingPower.get(locals._t).stakerAddress == qpi.invocator()) + { + if (state.votingPower.get(locals._t).amount >= QVAULT_MIN_VOTING_POWER) + { + break; + } + else + { + if (qpi.numberOfShares(locals.qvaultShare, AssetOwnershipSelect::byOwner(qpi.invocator()), AssetPossessionSelect::byPossessor(qpi.invocator())) <= 0) + { + output.returnCode = QVAULT_INSUFFICIENT_VOTING_POWER; + if (qpi.invocationReward() > 0) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + } + return ; + } + break; + } + } } - } - PUBLIC_PROCEDURE(changeAdminAddress) - { - if(qpi.invocator() != state.authAddress1 && qpi.invocator() != state.authAddress2 && qpi.invocator() != state.authAddress3) + if(locals._t == state.numberOfVotingPower && qpi.numberOfShares(locals.qvaultShare, AssetOwnershipSelect::byOwner(qpi.invocator()), AssetPossessionSelect::byPossessor(qpi.invocator())) <= 0) { + if (qpi.invocationReward() > 0) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + } + output.returnCode = QVAULT_NO_VOTING_POWER; return ; } - if(input.newAddress == NULL_ID || input.newAddress != state.newAdminAddress1 || state.newAdminAddress1 != state.newAdminAddress2 || state.newAdminAddress2 != state.newAdminAddress3) + if (qpi.invocationReward() < QVAULT_PROPOSAL_FEE) { + if (qpi.invocationReward() > 0) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + } + output.returnCode = QVAULT_INSUFFICIENT_FUND; return ; } - - state.adminAddress = state.newAdminAddress1; - - state.newAdminAddress1 = NULL_ID; - state.newAdminAddress2 = NULL_ID; - state.newAdminAddress3 = NULL_ID; - } - - - PUBLIC_PROCEDURE(submitBannedAddress) - { - if(qpi.invocator() == state.authAddress1) + if (state.numberOfQEarnP >= QVAULT_MAX_NUMBER_OF_PROPOSAL) { - state.bannedAddress1 = input.bannedAddress; + if (qpi.invocationReward() > 0) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + } + output.returnCode = QVAULT_OVERFLOW_PROPOSAL; + return ; } - if(qpi.invocator() == state.authAddress2) + if (qpi.invocationReward() > QVAULT_PROPOSAL_FEE) { - state.bannedAddress2 = input.bannedAddress; + qpi.transfer(qpi.invocator(), qpi.invocationReward() - QVAULT_PROPOSAL_FEE); } - if(qpi.invocator() == state.authAddress3) + state.proposalCreateFund += QVAULT_PROPOSAL_FEE; + + locals.newProposal.currentQuorumPercent = state.quorumPercent; + locals.newProposal.currentTotalVotingPower = state.totalVotingPower; + locals.newProposal.proposedEpoch = qpi.epoch(); + locals.newProposal.numberOfYes = 0; + locals.newProposal.numberOfNo = 0; + locals.newProposal.proposer = qpi.invocator(); + locals.newProposal.result = QVAULT_PROPOSAL_NOT_STARTED; + + locals.newProposal.assignedFundPerEpoch = input.amountPerEpoch; + locals.newProposal.amountOfInvestPerEpoch = input.amountPerEpoch; + locals.newProposal.numberOfEpoch = input.numberOfEpoch; + for (locals._t = 0; locals._t < QVAULT_MAX_URLS_COUNT; locals._t++) { - state.bannedAddress3 = input.bannedAddress; + locals.newProposal.url.set(locals._t, input.url.get(locals._t)); } + state.QEarnP.set(state.numberOfQEarnP++, locals.newProposal); + output.returnCode = QVAULT_SUCCESS; } - PUBLIC_PROCEDURE(saveBannedAddress) + /** + * Local variables for submitFundP function + */ + struct submitFundP_locals + { + Asset qvaultShare; // QVAULT share asset information + FundPInfo newProposal; // New fundraising proposal to create + sint32 _t; // Loop counter variable + uint32 curDate; // Current date (packed) + uint8 year; // Current year + uint8 month; // Current month + uint8 day; // Current day + uint8 hour; // Current hour + uint8 minute; // Current minute + uint8 second; // Current second + }; + + /** + * Submits a Fundraising Proposal (FundP) to sell QCAP tokens + * Requires minimum voting power (10000) or QVAULT shares + * Validates yearly QCAP sale limits (2025-2027) + * Charges proposal fee and creates new proposal record + * + * @param input Price per QCAP, amount to sell, and URL containing proposal details + * @param output Status code indicating success or failure + */ + PUBLIC_PROCEDURE_WITH_LOCALS(submitFundP) { - if(state.numberOfBannedAddress >= QVAULT_MAX_NUMBER_OF_BANNED_ADDRESSES) + locals.qvaultShare.assetName = QVAULT_QVAULT_ASSETNAME; + locals.qvaultShare.issuer = NULL_ID; + + for (locals._t = 0 ; locals._t < (sint64)state.numberOfVotingPower; locals._t++) { - return ; + if (state.votingPower.get(locals._t).stakerAddress == qpi.invocator()) + { + if (state.votingPower.get(locals._t).amount >= QVAULT_MIN_VOTING_POWER) + { + break; + } + else + { + if (qpi.numberOfShares(locals.qvaultShare, AssetOwnershipSelect::byOwner(qpi.invocator()), AssetPossessionSelect::byPossessor(qpi.invocator())) <= 0) + { + output.returnCode = QVAULT_INSUFFICIENT_VOTING_POWER; + if (qpi.invocationReward() > 0) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + } + return ; + } + break; + } + } } - if(qpi.invocator() != state.authAddress1 && qpi.invocator() != state.authAddress2 && qpi.invocator() != state.authAddress3) + if(locals._t == state.numberOfVotingPower && qpi.numberOfShares(locals.qvaultShare, AssetOwnershipSelect::byOwner(qpi.invocator()), AssetPossessionSelect::byPossessor(qpi.invocator())) <= 0) { + if (qpi.invocationReward() > 0) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + } + output.returnCode = QVAULT_NO_VOTING_POWER; return ; } - if(input.bannedAddress == NULL_ID || input.bannedAddress != state.bannedAddress1 || state.bannedAddress1 != state.bannedAddress2 || state.bannedAddress2 != state.bannedAddress3) + packQvaultDate(qpi.year(), qpi.month(), qpi.day(), qpi.hour(), qpi.minute(), qpi.second(), locals.curDate); + unpackQvaultDate(locals.year, locals.month, locals.day, locals.hour, locals.minute, locals.second, locals.curDate); + if (locals.year == 25 && state.qcapSoldAmount + input.amountOfQcap > QVAULT_2025MAX_QCAP_SALE_AMOUNT) { + output.returnCode = QVAULT_OVERFLOW_SALE_AMOUNT; + if (qpi.invocationReward() > 0) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + } + return ; + } + else if (locals.year == 26 && state.qcapSoldAmount + input.amountOfQcap > QVAULT_2026MAX_QCAP_SALE_AMOUNT) + { + output.returnCode = QVAULT_OVERFLOW_SALE_AMOUNT; + if (qpi.invocationReward() > 0) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + } + return ; + } + else if (locals.year == 27 && state.qcapSoldAmount + input.amountOfQcap > QVAULT_2027MAX_QCAP_SALE_AMOUNT) + { + output.returnCode = QVAULT_OVERFLOW_SALE_AMOUNT; + if (qpi.invocationReward() > 0) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + } + return ; + } + else if (state.qcapSoldAmount + input.amountOfQcap > QVAULT_QCAP_MAX_SUPPLY) + { + output.returnCode = QVAULT_OVERFLOW_SALE_AMOUNT; + if (qpi.invocationReward() > 0) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + } return ; } - state.bannedAddress.set(state.numberOfBannedAddress, input.bannedAddress); - - state.numberOfBannedAddress++; - state.newAdminAddress1 = NULL_ID; - state.newAdminAddress2 = NULL_ID; - state.newAdminAddress3 = NULL_ID; - - } - - PUBLIC_PROCEDURE(submitUnbannedAddress) - { - if(qpi.invocator() == state.authAddress1) + if (qpi.invocationReward() < QVAULT_PROPOSAL_FEE) { - state.unbannedAddress1 = input.unbannedAddress; + if (qpi.invocationReward() > 0) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + } + output.returnCode = QVAULT_INSUFFICIENT_FUND; + return ; } - if(qpi.invocator() == state.authAddress2) + if (state.numberOfFundP >= QVAULT_MAX_NUMBER_OF_PROPOSAL) { - state.unbannedAddress2 = input.unbannedAddress; + if (qpi.invocationReward() > 0) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + } + output.returnCode = QVAULT_OVERFLOW_PROPOSAL; + return ; } - if(qpi.invocator() == state.authAddress3) + if (qpi.invocationReward() > QVAULT_PROPOSAL_FEE) { - state.unbannedAddress3 = input.unbannedAddress; + qpi.transfer(qpi.invocator(), qpi.invocationReward() - QVAULT_PROPOSAL_FEE); } + state.proposalCreateFund += QVAULT_PROPOSAL_FEE; + locals.newProposal.currentQuorumPercent = state.quorumPercent; + locals.newProposal.currentTotalVotingPower = state.totalVotingPower; + for (locals._t = 0; locals._t < QVAULT_MAX_URLS_COUNT; locals._t++) + { + locals.newProposal.url.set(locals._t, input.url.get(locals._t)); + } + locals.newProposal.proposedEpoch = qpi.epoch(); + locals.newProposal.numberOfYes = 0; + locals.newProposal.numberOfNo = 0; + locals.newProposal.proposer = qpi.invocator(); + locals.newProposal.result = QVAULT_PROPOSAL_NOT_STARTED; + + locals.newProposal.restSaleAmount = input.amountOfQcap; + locals.newProposal.amountOfQcap = input.amountOfQcap; + locals.newProposal.pricePerOneQcap = input.priceOfOneQcap; + + state.FundP.set(state.numberOfFundP++, locals.newProposal); + output.returnCode = QVAULT_SUCCESS; } - struct unblockBannedAddress_locals + /** + * Local variables for submitMKTP function + */ + struct submitMKTP_locals { - uint32 _t, flag; + Asset qvaultShare; // QVAULT share asset information + MKTPInfo newProposal; // New marketplace proposal to create + sint32 _t; // Loop counter variable }; - PUBLIC_PROCEDURE_WITH_LOCALS(unblockBannedAddress) + /** + * Submits a Marketplace Proposal (MKTP) to purchase shares from marketplace + * Requires proposal fee payment + * Transfers shares from user to contract as collateral + * Creates new proposal record for voting + * + * @param input Qubic amount, share details, QCAP amount, and URL containing proposal details + * @param output Status code indicating success or failure + */ + PUBLIC_PROCEDURE_WITH_LOCALS(submitMKTP) { - if(qpi.invocator() != state.authAddress1 && qpi.invocator() != state.authAddress2 && qpi.invocator() != state.authAddress3) + if (qpi.invocationReward() < QVAULT_PROPOSAL_FEE) { + if (qpi.invocationReward() > 0) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + } + output.returnCode = QVAULT_INSUFFICIENT_FUND; return ; } - - if(input.unbannedAddress == NULL_ID || input.unbannedAddress != state.unbannedAddress1 || state.unbannedAddress1 != state.unbannedAddress2 || state.unbannedAddress2 != state.unbannedAddress3) + if (qpi.transferShareOwnershipAndPossession(input.shareName, NULL_ID, qpi.invocator(), qpi.invocator(), input.amountOfShare, SELF) < 0) { + if (qpi.invocationReward() > 0) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + } + output.returnCode = QVAULT_NOT_TRANSFERRED_SHARE; return ; } - - locals.flag = 0; - - for(locals._t = 0; locals._t < state.numberOfBannedAddress; locals._t++) + if (state.numberOfMKTP >= QVAULT_MAX_NUMBER_OF_PROPOSAL) { - if(locals.flag == 1 || input.unbannedAddress == state.bannedAddress.get(locals._t)) + if (qpi.invocationReward() > 0) { - if(locals._t == state.numberOfBannedAddress - 1) - { - state.bannedAddress.set(locals._t, NULL_ID); - locals.flag = 1; - break; - } - state.bannedAddress.set(locals._t, state.bannedAddress.get(locals._t + 1)); - locals.flag = 1; + qpi.transfer(qpi.invocator(), qpi.invocationReward()); } + output.returnCode = QVAULT_OVERFLOW_PROPOSAL; + return ; } - - if(locals.flag == 1) + if (qpi.invocationReward() > QVAULT_PROPOSAL_FEE) { - state.numberOfBannedAddress--; - } - state.unbannedAddress1 = NULL_ID; - state.unbannedAddress2 = NULL_ID; - state.unbannedAddress3 = NULL_ID; + qpi.transfer(qpi.invocator(), qpi.invocationReward() - QVAULT_PROPOSAL_FEE); + } + state.proposalCreateFund += QVAULT_PROPOSAL_FEE; + locals.newProposal.currentQuorumPercent = state.quorumPercent; + locals.newProposal.currentTotalVotingPower = state.totalVotingPower; + for (locals._t = 0; locals._t < QVAULT_MAX_URLS_COUNT; locals._t++) + { + locals.newProposal.url.set(locals._t, input.url.get(locals._t)); + } + locals.newProposal.proposedEpoch = qpi.epoch(); + locals.newProposal.numberOfYes = 0; + locals.newProposal.numberOfNo = 0; + locals.newProposal.proposer = qpi.invocator(); + locals.newProposal.result = QVAULT_PROPOSAL_NOT_STARTED; + + locals.newProposal.shareIndex = input.indexOfShare; + locals.newProposal.amountOfShare = input.amountOfShare; + locals.newProposal.amountOfQubic = input.amountOfQubic; + locals.newProposal.amountOfQcap = input.amountOfQcap; + locals.newProposal.shareName = input.shareName; + + state.MKTP.set(state.numberOfMKTP++, locals.newProposal); + output.returnCode = QVAULT_SUCCESS; } - REGISTER_USER_FUNCTIONS_AND_PROCEDURES() + /** + * Local variables for submitAlloP function + */ + struct submitAlloP_locals { - REGISTER_USER_FUNCTION(getData, 1); - - REGISTER_USER_PROCEDURE(submitAuthAddress, 1); - REGISTER_USER_PROCEDURE(changeAuthAddress, 2); - REGISTER_USER_PROCEDURE(submitDistributionPermille, 3); - REGISTER_USER_PROCEDURE(changeDistributionPermille, 4); - REGISTER_USER_PROCEDURE(submitReinvestingAddress, 5); - REGISTER_USER_PROCEDURE(changeReinvestingAddress, 6); - REGISTER_USER_PROCEDURE(submitAdminAddress, 7); - REGISTER_USER_PROCEDURE(changeAdminAddress, 8); - REGISTER_USER_PROCEDURE(submitBannedAddress, 9); - REGISTER_USER_PROCEDURE(saveBannedAddress, 10); - REGISTER_USER_PROCEDURE(submitUnbannedAddress, 11); - REGISTER_USER_PROCEDURE(unblockBannedAddress, 12); - - } + Asset qvaultShare; // QVAULT share asset information + AlloPInfo newProposal; // New allocation proposal to create + sint32 _t; // Loop counter variable + uint32 curDate; // Current date (packed) + uint8 year; // Current year + uint8 month; // Current month + uint8 day; // Current day + uint8 hour; // Current hour + uint8 minute; // Current minute + uint8 second; // Current second + }; - INITIALIZE() + /** + * Submits an Allocation Proposal (AlloP) to change revenue allocation percentages + * Requires minimum voting power (10000) or QVAULT shares + * Validates allocation percentages sum to 970 (per mille) + * Charges proposal fee and creates new proposal record + * + * @param input Allocation percentages and URL containing proposal details + * @param output Status code indicating success or failure + */ + PUBLIC_PROCEDURE_WITH_LOCALS(submitAlloP) { - state.QCAP_ISSUER = ID(_Q, _C, _A, _P, _W, _M, _Y, _R, _S, _H, _L, _B, _J, _H, _S, _T, _T, _Z, _Q, _V, _C, _I, _B, _A, _R, _V, _O, _A, _S, _K, _D, _E, _N, _A, _S, _A, _K, _N, _O, _B, _R, _G, _P, _F, _W, _W, _K, _R, _C, _U, _V, _U, _A, _X, _Y, _E); - state.authAddress1 = ID(_T, _K, _U, _W, _W, _S, _N, _B, _A, _E, _G, _W, _J, _H, _Q, _J, _D, _F, _L, _G, _Q, _H, _J, _J, _C, _J, _B, _A, _X, _B, _S, _Q, _M, _Q, _A, _Z, _J, _J, _D, _Y, _X, _E, _P, _B, _V, _B, _B, _L, _I, _Q, _A, _N, _J, _T, _I, _D); - state.authAddress2 = ID(_F, _X, _J, _F, _B, _T, _J, _M, _Y, _F, _J, _H, _P, _B, _X, _C, _D, _Q, _T, _L, _Y, _U, _K, _G, _M, _H, _B, _B, _Z, _A, _A, _F, _T, _I, _C, _W, _U, _K, _R, _B, _M, _E, _K, _Y, _N, _U, _P, _M, _R, _M, _B, _D, _N, _D, _R, _G); - state.authAddress3 = ID(_K, _E, _F, _D, _Z, _T, _Y, _L, _F, _E, _R, _A, _H, _D, _V, _L, _N, _Q, _O, _R, _D, _H, _F, _Q, _I, _B, _S, _B, _Z, _C, _W, _S, _Z, _X, _Z, _F, _F, _A, _N, _O, _T, _F, _A, _H, _W, _M, _O, _V, _G, _T, _R, _Q, _J, _P, _X, _D); - state.reinvestingAddress = ID(_R, _U, _U, _Y, _R, _V, _N, _K, _J, _X, _M, _L, _R, _B, _B, _I, _R, _I, _P, _D, _I, _B, _M, _H, _D, _H, _U, _A, _Z, _B, _Q, _K, _N, _B, _J, _T, _R, _D, _S, _P, _G, _C, _L, _Z, _C, _Q, _W, _A, _K, _C, _F, _Q, _J, _K, _K, _E); - state.adminAddress = ID(_H, _E, _C, _G, _U, _G, _H, _C, _J, _K, _Q, _O, _S, _D, _T, _M, _E, _H, _Q, _Y, _W, _D, _D, _T, _L, _F, _D, _A, _S, _Z, _K, _M, _G, _J, _L, _S, _R, _C, _S, _T, _H, _H, _A, _P, _P, _E, _D, _L, _G, _B, _L, _X, _J, _M, _N, _D); - - state.shareholderDividend = 30; - state.QCAPHolderPermille = 500; - state.reinvestingPermille = 450; - state.devPermille = 20; - state.burnPermille = 0; + locals.qvaultShare.assetName = QVAULT_QVAULT_ASSETNAME; + locals.qvaultShare.issuer = NULL_ID; - /* - initial banned addresses - */ - state.bannedAddress.set(0, ID(_K, _E, _F, _D, _Z, _T, _Y, _L, _F, _E, _R, _A, _H, _D, _V, _L, _N, _Q, _O, _R, _D, _H, _F, _Q, _I, _B, _S, _B, _Z, _C, _W, _S, _Z, _X, _Z, _F, _F, _A, _N, _O, _T, _F, _A, _H, _W, _M, _O, _V, _G, _T, _R, _Q, _J, _P, _X, _D)); - state.bannedAddress.set(1, ID(_E, _S, _C, _R, _O, _W, _B, _O, _T, _F, _T, _F, _I, _C, _I, _F, _P, _U, _X, _O, _J, _K, _G, _Q, _P, _Y, _X, _C, _A, _B, _L, _Z, _V, _M, _M, _U, _C, _M, _J, _F, _S, _G, _S, _A, _I, _A, _T, _Y, _I, _N, _V, _T, _Y, _G, _O, _A)); - state.numberOfBannedAddress = 2; + for (locals._t = 0 ; locals._t < (sint64)state.numberOfVotingPower; locals._t++) + { + if (state.votingPower.get(locals._t).stakerAddress == qpi.invocator()) + { + if (state.votingPower.get(locals._t).amount >= QVAULT_MIN_VOTING_POWER) + { + break; + } + else + { + if (qpi.numberOfShares(locals.qvaultShare, AssetOwnershipSelect::byOwner(qpi.invocator()), AssetPossessionSelect::byPossessor(qpi.invocator())) <= 0) + { + output.returnCode = QVAULT_INSUFFICIENT_VOTING_POWER; + if (qpi.invocationReward() > 0) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + } + return ; + } + break; + } + } + } + + if(locals._t == state.numberOfVotingPower && qpi.numberOfShares(locals.qvaultShare, AssetOwnershipSelect::byOwner(qpi.invocator()), AssetPossessionSelect::byPossessor(qpi.invocator())) <= 0) + { + if (qpi.invocationReward() > 0) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + } + output.returnCode = QVAULT_NO_VOTING_POWER; + return ; + } + + packQvaultDate(qpi.year(), qpi.month(), qpi.day(), qpi.hour(), qpi.minute(), qpi.second(), locals.curDate); + unpackQvaultDate(locals.year, locals.month, locals.day, locals.hour, locals.minute, locals.second, locals.curDate); + if (locals.year < 29 && input.burn != 0) + { + output.returnCode = QVAULT_NOT_IN_TIME; + if (qpi.invocationReward() > 0) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + } + return ; + } + + if (input.burn + input.distribute + input.reinvested != QVAULT_SUM_OF_ALLOCATION_PERCENTAGES) + { + output.returnCode = QVAULT_NOT_FAIR; + if (qpi.invocationReward() > 0) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + } + return ; + } + + if (qpi.invocationReward() < QVAULT_PROPOSAL_FEE) + { + if (qpi.invocationReward() > 0) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + } + output.returnCode = QVAULT_INSUFFICIENT_FUND; + return ; + } + if (state.numberOfAlloP >= QVAULT_MAX_NUMBER_OF_PROPOSAL) + { + if (qpi.invocationReward() > 0) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + } + output.returnCode = QVAULT_OVERFLOW_PROPOSAL; + return ; + } + if (qpi.invocationReward() > QVAULT_PROPOSAL_FEE) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward() - QVAULT_PROPOSAL_FEE); + } + state.proposalCreateFund += QVAULT_PROPOSAL_FEE; + + locals.newProposal.currentQuorumPercent = state.quorumPercent; + locals.newProposal.currentTotalVotingPower = state.totalVotingPower; + for (locals._t = 0; locals._t < QVAULT_MAX_URLS_COUNT; locals._t++) + { + locals.newProposal.url.set(locals._t, input.url.get(locals._t)); + } + locals.newProposal.proposedEpoch = qpi.epoch(); + locals.newProposal.numberOfYes = 0; + locals.newProposal.numberOfNo = 0; + locals.newProposal.proposer = qpi.invocator(); + locals.newProposal.result = QVAULT_PROPOSAL_NOT_STARTED; + + locals.newProposal.burnQcap = input.burn; + locals.newProposal.distributed = input.distribute; + locals.newProposal.reinvested = input.reinvested; + + state.AlloP.set(state.numberOfAlloP++, locals.newProposal); + output.returnCode = QVAULT_SUCCESS; + } + + /** + * Local variables for voteInProposal function + */ + struct voteInProposal_locals + { + GPInfo updatedGProposal; // Updated general proposal + QCPInfo updatedQCProposal; // Updated quorum change proposal + IPOPInfo updatedIPOProposal; // Updated IPO participation proposal + QEarnPInfo updatedQEarnProposal; // Updated QEarn participation proposal + FundPInfo updatedFundProposal; // Updated fundraising proposal + MKTPInfo updatedMKTProposal; // Updated marketplace proposal + AlloPInfo updatedAlloProposal; // Updated allocation proposal + Array newVoteList; // Updated vote list for user + voteStatusInfo newVote; // New vote to add + sint32 numberOfYes; // Number of yes votes to add + sint32 numberOfNo; // Number of no votes to add + sint32 _t, _r; // Loop counter variables + uint8 countOfVote; // Current vote count for user + bit statusOfProposal; // Whether proposal is still active + }; + + /** + * Votes on active proposals of various types + * Supports 7 different proposal types with different voting logic + * Updates proposal vote counts and user voting history + * Prevents duplicate votes and enforces voting rules + * + * @param input Proposal type, ID, voting decision, and IPO price (if applicable) + * @param output Status code indicating success or failure + */ + PUBLIC_PROCEDURE_WITH_LOCALS(voteInProposal) + { + if (input.proposalType > 7 || input.proposalType < 1) + { + output.returnCode = QVAULT_INPUT_ERROR; + return ; + } + switch (input.proposalType) + { + case 1: + if (input.proposalId >= state.numberOfGP) + { + output.returnCode = QVAULT_OVERFLOW_PROPOSAL; + return ; + } + break; + case 2: + if (input.proposalId >= state.numberOfQCP) + { + output.returnCode = QVAULT_OVERFLOW_PROPOSAL; + return ; + } + break; + case 3: + if (input.proposalId >= state.numberOfIPOP) + { + output.returnCode = QVAULT_OVERFLOW_PROPOSAL; + return ; + } + break; + case 4: + if (input.proposalId >= state.numberOfQEarnP) + { + output.returnCode = QVAULT_OVERFLOW_PROPOSAL; + return ; + } + break; + case 5: + if (input.proposalId >= state.numberOfFundP) + { + output.returnCode = QVAULT_OVERFLOW_PROPOSAL; + return ; + } + break; + case 6: + if (input.proposalId >= state.numberOfMKTP) + { + output.returnCode = QVAULT_OVERFLOW_PROPOSAL; + return ; + } + break; + case 7: + if (input.proposalId >= state.numberOfAlloP) + { + output.returnCode = QVAULT_OVERFLOW_PROPOSAL; + return ; + } + break; + } + + if (state.countOfVote.get(qpi.invocator(), locals.countOfVote)) + { + state.vote.get(qpi.invocator(), locals.newVoteList); + for (locals._r = 0; locals._r < locals.countOfVote; locals._r++) + { + if (locals.newVoteList.get(locals._r).proposalId == input.proposalId && locals.newVoteList.get(locals._r).proposalType == input.proposalType) + { + break; + } + } + } + if (locals.countOfVote == QVAULT_MAX_USER_VOTES && locals._r == locals.countOfVote) + { + output.returnCode = QVAULT_OVERFLOW_VOTES; + return ; + } + if (locals._r < locals.countOfVote && locals.newVoteList.get(locals._r).decision == input.yes) + { + output.returnCode = QVAULT_SAME_DECISION; + return ; + } + + switch (input.proposalType) + { + case 1: + locals.updatedGProposal = state.GP.get(input.proposalId); + locals.statusOfProposal = state.GP.get(input.proposalId).proposedEpoch == qpi.epoch() ? 1 : 0; + break; + case 2: + locals.updatedQCProposal = state.QCP.get(input.proposalId); + locals.statusOfProposal = state.QCP.get(input.proposalId).proposedEpoch == qpi.epoch() ? 1 : 0; + break; + case 3: + locals.updatedIPOProposal = state.IPOP.get(input.proposalId); + locals.statusOfProposal = state.IPOP.get(input.proposalId).proposedEpoch == qpi.epoch() ? 1 : 0; + break; + case 4: + locals.updatedQEarnProposal = state.QEarnP.get(input.proposalId); + locals.statusOfProposal = state.QEarnP.get(input.proposalId).proposedEpoch == qpi.epoch() ? 1 : 0; + break; + case 5: + locals.updatedFundProposal = state.FundP.get(input.proposalId); + locals.statusOfProposal = state.FundP.get(input.proposalId).proposedEpoch == qpi.epoch() ? 1 : 0; + break; + case 6: + locals.updatedMKTProposal = state.MKTP.get(input.proposalId); + locals.statusOfProposal = state.MKTP.get(input.proposalId).proposedEpoch == qpi.epoch() ? 1 : 0; + break; + case 7: + locals.updatedAlloProposal = state.AlloP.get(input.proposalId); + locals.statusOfProposal = state.AlloP.get(input.proposalId).proposedEpoch == qpi.epoch() ? 1 : 0; + break; + default: + break; + } + + if (locals.statusOfProposal == 0) + { + output.returnCode = QVAULT_ENDED_PROPOSAL; + return ; + } + + if (locals.statusOfProposal == 1) + { + locals.numberOfYes = 0; + locals.numberOfNo = 0; + for (locals._t = 0 ; locals._t < (sint32)state.numberOfVotingPower; locals._t++) + { + if (state.votingPower.get(locals._t).stakerAddress == qpi.invocator()) + { + if (input.yes == 1) + { + locals.numberOfYes = state.votingPower.get(locals._t).amount; + if (locals._r < locals.countOfVote) + { + locals.numberOfNo -= state.votingPower.get(locals._t).amount; + } + } + else + { + locals.numberOfNo = state.votingPower.get(locals._t).amount; + if (locals._r < locals.countOfVote) + { + locals.numberOfYes -= state.votingPower.get(locals._t).amount; + } + } + switch (input.proposalType) + { + case 1: + locals.updatedGProposal.numberOfYes += locals.numberOfYes; + locals.updatedGProposal.numberOfNo += locals.numberOfNo; + state.GP.set(input.proposalId, locals.updatedGProposal); + break; + case 2: + locals.updatedQCProposal.numberOfYes += locals.numberOfYes; + locals.updatedQCProposal.numberOfNo += locals.numberOfNo; + state.QCP.set(input.proposalId, locals.updatedQCProposal); + break; + case 3: + if (input.yes) + { + locals.updatedIPOProposal.totalWeight += locals.numberOfYes * input.priceOfIPO; + } + else if (locals._r < locals.countOfVote) + { + locals.updatedIPOProposal.totalWeight -= locals.numberOfNo * locals.newVoteList.get(locals._r).priceOfIPO; + } + locals.updatedIPOProposal.numberOfYes += locals.numberOfYes; + locals.updatedIPOProposal.numberOfNo += locals.numberOfNo; + state.IPOP.set(input.proposalId, locals.updatedIPOProposal); + break; + case 4: + locals.updatedQEarnProposal.numberOfYes += locals.numberOfYes; + locals.updatedQEarnProposal.numberOfNo += locals.numberOfNo; + state.QEarnP.set(input.proposalId, locals.updatedQEarnProposal); + break; + case 5: + locals.updatedFundProposal.numberOfYes += locals.numberOfYes; + locals.updatedFundProposal.numberOfNo += locals.numberOfNo; + state.FundP.set(input.proposalId, locals.updatedFundProposal); + break; + case 6: + locals.updatedMKTProposal.numberOfYes += locals.numberOfYes; + locals.updatedMKTProposal.numberOfNo += locals.numberOfNo; + state.MKTP.set(input.proposalId, locals.updatedMKTProposal); + break; + case 7: + locals.updatedAlloProposal.numberOfYes += locals.numberOfYes; + locals.updatedAlloProposal.numberOfNo += locals.numberOfNo; + state.AlloP.set(input.proposalId, locals.updatedAlloProposal); + break; + default: + break; + } + if (state.countOfVote.get(qpi.invocator(), locals.countOfVote)) + { + locals.newVote.proposalId = input.proposalId; + locals.newVote.proposalType = input.proposalType; + locals.newVote.decision = input.yes; + locals.newVote.priceOfIPO = input.priceOfIPO; + if (locals._r < locals.countOfVote) + { + locals.newVoteList.set(locals._r, locals.newVote); + } + else + { + locals.newVoteList.set(locals.countOfVote, locals.newVote); + state.countOfVote.set(qpi.invocator(), locals.countOfVote + 1); + } + } + else + { + locals.newVote.proposalId = input.proposalId; + locals.newVote.proposalType = input.proposalType; + locals.newVote.decision = input.yes; + locals.newVote.priceOfIPO = input.priceOfIPO; + locals.newVoteList.set(0, locals.newVote); + state.countOfVote.set(qpi.invocator(), 1); + } + state.vote.set(qpi.invocator(), locals.newVoteList); + output.returnCode = QVAULT_SUCCESS; + return ; + } + } + } + + output.returnCode = QVAULT_NO_VOTING_POWER; + } + + /** + * Local variables for buyQcap function + */ + struct buyQcap_locals + { + QX::TransferShareManagementRights_input transferShareManagementRights_input; // Input for QX contract call + QX::TransferShareManagementRights_output transferShareManagementRights_output; // Output from QX contract call + FundPInfo updatedFundProposal; // Updated fundraising proposal + sint32 _t; // Loop counter variable + uint32 curDate; // Current date (packed) + uint8 year; // Current year + uint8 month; // Current month + uint8 day; // Current day + uint8 hour; // Current hour + uint8 minute; // Current minute + uint8 second; // Current second + }; + + /** + * Purchases QCAP tokens from active fundraising proposals + * Validates yearly QCAP sale limits (2025-2027) + * Finds best available price from passed fundraising proposals + * Transfers QCAP tokens to buyer and updates proposal sale amounts + * + * @param input Amount of QCAP tokens to purchase + * @param output Status code indicating success or failure + */ + PUBLIC_PROCEDURE_WITH_LOCALS(buyQcap) + { + packQvaultDate(qpi.year(), qpi.month(), qpi.day(), qpi.hour(), qpi.minute(), qpi.second(), locals.curDate); + unpackQvaultDate(locals.year, locals.month, locals.day, locals.hour, locals.minute, locals.second, locals.curDate); + if (locals.year == 25 && state.qcapSoldAmount + input.amount > QVAULT_2025MAX_QCAP_SALE_AMOUNT) + { + output.returnCode = QVAULT_OVERFLOW_SALE_AMOUNT; + if (qpi.invocationReward() > 0) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + } + return ; + } + else if (locals.year == 26 && state.qcapSoldAmount + input.amount > QVAULT_2026MAX_QCAP_SALE_AMOUNT) + { + output.returnCode = QVAULT_OVERFLOW_SALE_AMOUNT; + if (qpi.invocationReward() > 0) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + } + return ; + } + else if (locals.year == 27 && state.qcapSoldAmount + input.amount > QVAULT_2027MAX_QCAP_SALE_AMOUNT) + { + output.returnCode = QVAULT_OVERFLOW_SALE_AMOUNT; + if (qpi.invocationReward() > 0) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + } + return ; + } + else if (state.qcapSoldAmount + input.amount > QVAULT_QCAP_MAX_SUPPLY) + { + output.returnCode = QVAULT_OVERFLOW_SALE_AMOUNT; + if (qpi.invocationReward() > 0) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + } + return ; + } + + for (locals._t = state.numberOfFundP - 1; locals._t >= 0; locals._t--) + { + if (state.FundP.get(locals._t).result == 0 && state.FundP.get(locals._t).proposedEpoch + 1 < qpi.epoch() && state.FundP.get(locals._t).restSaleAmount >= input.amount) + { + if (qpi.invocationReward() >= (sint64)state.FundP.get(locals._t).pricePerOneQcap * input.amount) + { + if (qpi.invocationReward() > (sint64)state.FundP.get(locals._t).pricePerOneQcap * input.amount) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward() - (state.FundP.get(locals._t).pricePerOneQcap * input.amount)); + } + + state.rasiedFundByQcap += state.FundP.get(locals._t).pricePerOneQcap * input.amount; + locals.transferShareManagementRights_input.asset.assetName = QVAULT_QCAP_ASSETNAME; + locals.transferShareManagementRights_input.asset.issuer = state.QCAP_ISSUER; + locals.transferShareManagementRights_input.newManagingContractIndex = SELF_INDEX; + locals.transferShareManagementRights_input.numberOfShares = input.amount; + + INVOKE_OTHER_CONTRACT_PROCEDURE(QX, TransferShareManagementRights, locals.transferShareManagementRights_input, locals.transferShareManagementRights_output, 0); + + qpi.transferShareOwnershipAndPossession(QVAULT_QCAP_ASSETNAME, state.QCAP_ISSUER, SELF, SELF, input.amount, qpi.invocator()); + + state.qcapSoldAmount += input.amount; + locals.updatedFundProposal = state.FundP.get(locals._t); + locals.updatedFundProposal.restSaleAmount -= input.amount; + state.FundP.set(locals._t, locals.updatedFundProposal); + + state.reinvestingFund += state.FundP.get(locals._t).pricePerOneQcap * input.amount; + output.returnCode = QVAULT_SUCCESS; + return ; + } + } + } + + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + + output.returnCode = QVAULT_FAILED; + } + + /** + * Transfers share management rights to another contract + * Requires payment of transfer rights fee + * Releases shares from current contract to new managing contract + * Used for QCAP token transfers and other asset management + * + * @param input Asset information, number of shares, and new managing contract index + * @param output Number of shares transferred and status code + */ + PUBLIC_PROCEDURE(TransferShareManagementRights) + { + if (qpi.invocationReward() < state.transferRightsFee) + { + output.returnCode = QVAULT_INSUFFICIENT_FUND; + return ; + } + + if (qpi.numberOfPossessedShares(input.asset.assetName, input.asset.issuer,qpi.invocator(), qpi.invocator(), SELF_INDEX, SELF_INDEX) < input.numberOfShares) + { + // not enough shares available + output.transferredNumberOfShares = 0; + if (qpi.invocationReward() > 0) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + } + + output.returnCode = QVAULT_INSUFFICIENT_SHARE; + } + else + { + if (qpi.releaseShares(input.asset, qpi.invocator(), qpi.invocator(), input.numberOfShares, + input.newManagingContractIndex, input.newManagingContractIndex, state.transferRightsFee) < 0) + { + // error + output.transferredNumberOfShares = 0; + if (qpi.invocationReward() > 0) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward()); + } + + output.returnCode = QVAULT_ERROR_TRANSFER_ASSET; + } + else + { + // success + output.transferredNumberOfShares = input.numberOfShares; + if (qpi.invocationReward() > state.transferRightsFee) + { + qpi.transfer(qpi.invocator(), qpi.invocationReward() - state.transferRightsFee); + } + + output.returnCode = QVAULT_SUCCESS; + } + } + } + +public: + /** + * Input structure for getStakedAmountAndVotingPower function + * @param address User address to query staking information for + */ + struct getStakedAmountAndVotingPower_input + { + id address; + }; + + /** + * Output structure for getStakedAmountAndVotingPower function + * @param stakedAmount Amount of QCAP tokens staked by the user + * @param votingPower Voting power of the user + */ + struct getStakedAmountAndVotingPower_output + { + sint32 returnCode; // Status code indicating success or failure + uint32 stakedAmount; + uint32 votingPower; + }; + + /** + * Local variables for getStakedAmountAndVotingPower function + */ + struct getStakedAmountAndVotingPower_locals + { + sint32 _t; // Loop counter variable + }; + + /** + * Retrieves staking information for a specific user address + * Returns both staked amount and voting power + * + * @param input User address to query + * @param output Staked amount and voting power for the user + */ + PUBLIC_FUNCTION_WITH_LOCALS(getStakedAmountAndVotingPower) + { + for (locals._t = 0; locals._t < (sint64)state.numberOfStaker; locals._t++) + { + if (state.staker.get(locals._t).stakerAddress == input.address) + { + output.stakedAmount = state.staker.get(locals._t).amount; + break; + } + } + for (locals._t = 0; locals._t < (sint64)state.numberOfVotingPower; locals._t++) + { + if (state.votingPower.get(locals._t).stakerAddress == input.address) + { + output.votingPower = state.votingPower.get(locals._t).amount; + break; + } + } + output.returnCode = QVAULT_SUCCESS; + } + + /** + * Input structure for getGP function + * @param proposalId ID of the general proposal to retrieve + */ + struct getGP_input + { + uint32 proposalId; + }; + + /** + * Output structure for getGP function + * @param proposal General proposal information + */ + struct getGP_output + { + sint32 returnCode; // Status code indicating success or failure + GPInfo proposal; + }; + + /** + * Retrieves information about a specific General Proposal (GP) + * + * @param input Proposal ID to query + * @param output General proposal information + */ + PUBLIC_FUNCTION(getGP) + { + if (input.proposalId >= state.numberOfGP) + { + output.returnCode = QVAULT_INPUT_ERROR; + return ; + } + output.proposal = state.GP.get(input.proposalId); + output.returnCode = QVAULT_SUCCESS; + } + + /** + * Input structure for getQCP function + * @param proposalId ID of the quorum change proposal to retrieve + */ + struct getQCP_input + { + uint32 proposalId; + }; + + /** + * Output structure for getQCP function + * @param proposal Quorum change proposal information + */ + struct getQCP_output + { + sint32 returnCode; // Status code indicating success or failure + QCPInfo proposal; + }; + + /** + * Retrieves information about a specific Quorum Change Proposal (QCP) + * + * @param input Proposal ID to query + * @param output Quorum change proposal information + */ + PUBLIC_FUNCTION(getQCP) + { + if (input.proposalId >= state.numberOfQCP) + { + output.returnCode = QVAULT_INPUT_ERROR; + return ; + } + output.proposal = state.QCP.get(input.proposalId); + output.returnCode = QVAULT_SUCCESS; + } + + /** + * Input structure for getIPOP function + * @param proposalId ID of the IPO participation proposal to retrieve + */ + struct getIPOP_input + { + uint32 proposalId; + }; + + /** + * Output structure for getIPOP function + * @param proposal IPO participation proposal information + */ + struct getIPOP_output + { + sint32 returnCode; // Status code indicating success or failure + IPOPInfo proposal; + }; + + /** + * Retrieves information about a specific IPO Participation Proposal (IPOP) + * + * @param input Proposal ID to query + * @param output IPO participation proposal information + */ + PUBLIC_FUNCTION(getIPOP) + { + if (input.proposalId >= state.numberOfIPOP) + { + output.returnCode = QVAULT_INPUT_ERROR; + return ; + } + output.proposal = state.IPOP.get(input.proposalId); + output.returnCode = QVAULT_SUCCESS; + } + + /** + * Input structure for getQEarnP function + * @param proposalId ID of the QEarn participation proposal to retrieve + */ + struct getQEarnP_input + { + uint32 proposalId; + }; + + /** + * Output structure for getQEarnP function + * @param proposal QEarn participation proposal information + */ + struct getQEarnP_output + { + sint32 returnCode; // Status code indicating success or failure + QEarnPInfo proposal; + }; + + /** + * Retrieves information about a specific QEarn Participation Proposal (QEarnP) + * + * @param input Proposal ID to query + * @param output QEarn participation proposal information + */ + PUBLIC_FUNCTION(getQEarnP) + { + if (input.proposalId >= state.numberOfQEarnP) + { + output.returnCode = QVAULT_INPUT_ERROR; + return ; + } + output.proposal = state.QEarnP.get(input.proposalId); + output.returnCode = QVAULT_SUCCESS; + } + + /** + * Input structure for getFundP function + * @param proposalId ID of the fundraising proposal to retrieve + */ + struct getFundP_input + { + uint32 proposalId; + }; + + /** + * Output structure for getFundP function + * @param proposal Fundraising proposal information + */ + struct getFundP_output + { + sint32 returnCode; // Status code indicating success or failure + FundPInfo proposal; + }; + + /** + * Retrieves information about a specific Fundraising Proposal (FundP) + * + * @param input Proposal ID to query + * @param output Fundraising proposal information + */ + PUBLIC_FUNCTION(getFundP) + { + if (input.proposalId >= state.numberOfFundP) + { + output.returnCode = QVAULT_INPUT_ERROR; + return ; + } + output.proposal = state.FundP.get(input.proposalId); + output.returnCode = QVAULT_SUCCESS; + } + + /** + * Input structure for getMKTP function + * @param proposalId ID of the marketplace proposal to retrieve + */ + struct getMKTP_input + { + uint32 proposalId; + }; + + /** + * Output structure for getMKTP function + * @param proposal Marketplace proposal information + */ + struct getMKTP_output + { + sint32 returnCode; // Status code indicating success or failure + MKTPInfo proposal; + }; + + /** + * Retrieves information about a specific Marketplace Proposal (MKTP) + * + * @param input Proposal ID to query + * @param output Marketplace proposal information + */ + PUBLIC_FUNCTION(getMKTP) + { + if (input.proposalId >= state.numberOfMKTP) + { + output.returnCode = QVAULT_INPUT_ERROR; + return ; + } + output.proposal = state.MKTP.get(input.proposalId); + output.returnCode = QVAULT_SUCCESS; + } + + /** + * Input structure for getAlloP function + * @param proposalId ID of the allocation proposal to retrieve + */ + struct getAlloP_input + { + uint32 proposalId; + }; + + /** + * Output structure for getAlloP function + * @param proposal Allocation proposal information + */ + struct getAlloP_output + { + sint32 returnCode; // Status code indicating success or failure + AlloPInfo proposal; + }; + + /** + * Retrieves information about a specific Allocation Proposal (AlloP) + * + * @param input Proposal ID to query + * @param output Allocation proposal information + */ + PUBLIC_FUNCTION(getAlloP) + { + if (input.proposalId >= state.numberOfAlloP) + { + output.returnCode = QVAULT_INPUT_ERROR; + return ; + } + output.proposal = state.AlloP.get(input.proposalId); + output.returnCode = QVAULT_SUCCESS; + } + + /** + * Input structure for getIdentitiesHvVtPw function + * @param offset Starting index for pagination + * @param count Number of identities to retrieve (max 256) + */ + struct getIdentitiesHvVtPw_input + { + uint32 offset; + uint32 count; + }; + + /** + * Output structure for getIdentitiesHvVtPw function + * @param idList Array of user addresses with voting power + * @param amountList Array of voting power amounts corresponding to each address + */ + struct getIdentitiesHvVtPw_output + { + sint32 returnCode; // Status code indicating success or failure + Array idList; + Array amountList; + }; + + /** + * Local variables for getIdentitiesHvVtPw function + */ + struct getIdentitiesHvVtPw_locals + { + sint32 _t, _r; // Loop counter variables + }; + + /** + * Retrieves a paginated list of identities that have voting power + * Returns both addresses and their corresponding voting power amounts + * Useful for governance and analytics purposes + * + * @param input Offset and count for pagination + * @param output Arrays of addresses and voting power amounts + */ + PUBLIC_FUNCTION_WITH_LOCALS(getIdentitiesHvVtPw) + { + if(input.count > QVAULT_MAX_URLS_COUNT || input.offset + input.count > QVAULT_X_MULTIPLIER) + { + output.returnCode = QVAULT_INPUT_ERROR; + return ; + } + for (locals._t = (sint32)input.offset, locals._r = 0; locals._t < (sint32)(input.offset + input.count); locals._t++) + { + if (locals._t > (sint32)state.numberOfVotingPower) + { + output.returnCode = QVAULT_INPUT_ERROR; + return ; + } + output.idList.set(locals._r, state.votingPower.get(locals._t).stakerAddress); + output.amountList.set(locals._r++, state.votingPower.get(locals._t).amount); + } + output.returnCode = QVAULT_SUCCESS; + } + + /** + * Input structure for ppCreationPower function + * @param address User address to check proposal creation power for + */ + struct ppCreationPower_input + { + id address; + }; + + /** + * Output structure for ppCreationPower function + * @param status 0 = no proposal creation power, 1 = has proposal creation power + */ + struct ppCreationPower_output + { + sint32 returnCode; // Status code indicating success or failure + bit status; + }; + + /** + * Local variables for ppCreationPower function + */ + struct ppCreationPower_locals + { + Asset qvaultShare; // QVAULT share asset information + sint32 _t; // Loop counter variable + }; + + /** + * Checks if a user has the power to create proposals + * User must have either minimum voting power (10000) or QVAULT shares + * + * @param input User address to check + * @param output Status indicating whether user can create proposals + */ + PUBLIC_FUNCTION_WITH_LOCALS(ppCreationPower) + { + locals.qvaultShare.assetName = QVAULT_QVAULT_ASSETNAME; + locals.qvaultShare.issuer = NULL_ID; + + for (locals._t = 0; locals._t < (sint32)state.numberOfVotingPower; locals._t++) + { + if (state.votingPower.get(locals._t).stakerAddress == input.address) + { + if (state.votingPower.get(locals._t).amount >= QVAULT_MIN_VOTING_POWER) + { + output.returnCode = QVAULT_SUCCESS; + output.status = 1; + } + else + { + if (qpi.numberOfShares(locals.qvaultShare, AssetOwnershipSelect::byOwner(input.address), AssetPossessionSelect::byPossessor(input.address)) > 0) + { + output.returnCode = QVAULT_SUCCESS; + output.status = 1; + } + else + { + output.returnCode = QVAULT_INSUFFICIENT_SHARE_OR_VOTING_POWER; + output.status = 0; + } + } + return ; + } + } + + if (qpi.numberOfShares(locals.qvaultShare, AssetOwnershipSelect::byOwner(input.address), AssetPossessionSelect::byPossessor(input.address)) > 0) + { + output.returnCode = QVAULT_SUCCESS; + output.status = 1; + } + else + { + output.returnCode = QVAULT_NOT_FOUND_STAKER_ADDRESS; + output.status = 0; + } + } + + /** + * Input structure for getQcapBurntAmountInLastEpoches function + * @param numberOfLastEpoches Number of recent epochs to check (max 100) + */ + struct getQcapBurntAmountInLastEpoches_input + { + uint32 numberOfLastEpoches; + }; + + /** + * Output structure for getQcapBurntAmountInLastEpoches function + * @param burntAmount Total amount of QCAP tokens burned in the specified epochs + */ + struct getQcapBurntAmountInLastEpoches_output + { + sint32 returnCode; // Status code indicating success or failure + uint32 burntAmount; + }; + + /** + * Local variables for getQcapBurntAmountInLastEpoches function + */ + struct getQcapBurntAmountInLastEpoches_locals + { + sint32 _t; // Loop counter variable + }; + + /** + * Calculates the total amount of QCAP tokens burned in recent epochs + * Useful for tracking token deflation and economic metrics + * Maximum lookback period is 100 epochs + * + * @param input Number of recent epochs to check + * @param output Total amount of QCAP tokens burned + */ + PUBLIC_FUNCTION_WITH_LOCALS(getQcapBurntAmountInLastEpoches) + { + if (input.numberOfLastEpoches > 100) + { + output.returnCode = QVAULT_INPUT_ERROR; + return ; + } + for (locals._t = (sint32)qpi.epoch(); locals._t >= (sint32)(qpi.epoch() - input.numberOfLastEpoches); locals._t--) + { + output.burntAmount += state.burntQcapAmPerEpoch.get(locals._t); + } + output.returnCode = QVAULT_SUCCESS; + } + + /** + * Input structure for getAmountToBeSoldPerYear function + * @param year Year to check available QCAP sales for + */ + struct getAmountToBeSoldPerYear_input + { + uint32 year; + }; + + /** + * Output structure for getAmountToBeSoldPerYear function + * @param amount Amount of QCAP tokens available for sale in the specified year + */ + struct getAmountToBeSoldPerYear_output + { + uint32 amount; + }; + + /** + * Calculates the amount of QCAP tokens available for sale in a specific year + * Takes into account yearly limits and already sold amounts + * Returns 0 for years before 2025 + * + * @param input Year to check + * @param output Available QCAP amount for sale + */ + PUBLIC_FUNCTION(getAmountToBeSoldPerYear) + { + if (input.year < 2025) + { + output.amount = 0; + } + else if (input.year == 2025) + { + output.amount = QVAULT_2025MAX_QCAP_SALE_AMOUNT - state.qcapSoldAmount; + } + else if (input.year == 2026) + { + output.amount = QVAULT_2026MAX_QCAP_SALE_AMOUNT - state.qcapSoldAmount; + } + else if (input.year == 2027) + { + output.amount = QVAULT_2027MAX_QCAP_SALE_AMOUNT - state.qcapSoldAmount; + } + else if (input.year > 2027) + { + output.amount = QVAULT_QCAP_MAX_SUPPLY - state.qcapSoldAmount; + } + } + + /** + * Input structure for getTotalRevenueInQcap function + * No input parameters required + */ + struct getTotalRevenueInQcap_input + { + + }; + + /** + * Output structure for getTotalRevenueInQcap function + * @param revenue Total historical revenue in QCAP + */ + struct getTotalRevenueInQcap_output + { + uint64 revenue; + }; + + /** + * Retrieves the total historical revenue accumulated by the contract + * Represents the sum of all revenue across all epochs + * + * @param input No input parameters + * @param output Total historical revenue amount + */ + PUBLIC_FUNCTION(getTotalRevenueInQcap) + { + output.revenue = state.totalHistoryRevenue; + } + + /** + * Input structure for getRevenueInQcapPerEpoch function + * @param epoch Epoch number to retrieve revenue data for + */ + struct getRevenueInQcapPerEpoch_input + { + uint32 epoch; + }; + + /** + * Output structure for getRevenueInQcapPerEpoch function + * @param epochTotalRevenue Total revenue for the specified epoch + * @param epochOneQcapRevenue Revenue per QCAP token for the epoch + * @param epochOneQvaultRevenue Revenue per QVAULT share for the epoch + * @param epochReinvestAmount Amount allocated for reinvestment in the epoch + */ + struct getRevenueInQcapPerEpoch_output + { + uint64 epochTotalRevenue; + uint64 epochOneQcapRevenue; + uint64 epochOneQvaultRevenue; + uint64 epochReinvestAmount; + }; + + /** + * Retrieves detailed revenue information for a specific epoch + * Includes total revenue, per-token revenue, and reinvestment amounts + * Useful for historical analysis and user reward calculations + * + * @param input Epoch number to query + * @param output Detailed revenue breakdown for the epoch + */ + PUBLIC_FUNCTION(getRevenueInQcapPerEpoch) + { + output.epochTotalRevenue = state.revenueInQcapPerEpoch.get(input.epoch); + output.epochOneQcapRevenue = state.revenueForOneQcapPerEpoch.get(input.epoch); + output.epochOneQvaultRevenue = state.revenueForOneQvaultPerEpoch.get(input.epoch); + output.epochReinvestAmount = state.revenueForReinvestPerEpoch.get(input.epoch); + } + + /** + * Input structure for getRevenuePerShare function + * @param contractIndex Index of the contract to get revenue for + */ + struct getRevenuePerShare_input + { + uint32 contractIndex; + }; + + /** + * Output structure for getRevenuePerShare function + * @param revenue Revenue amount for the specified contract + */ + struct getRevenuePerShare_output + { + uint64 revenue; + }; + + /** + * Retrieves the revenue amount for a specific contract index + * Used for tracking revenue distribution across different contracts + * + * @param input Contract index to query + * @param output Revenue amount for the contract + */ + PUBLIC_FUNCTION(getRevenuePerShare) + { + output.revenue = state.revenuePerShare.get(input.contractIndex); + } + + /** + * Input structure for getAmountOfShareQvaultHold function + * @param assetInfo Asset information (name and issuer) to check + */ + struct getAmountOfShareQvaultHold_input + { + Asset assetInfo; + }; + + /** + * Output structure for getAmountOfShareQvaultHold function + * @param amount Amount of shares held by QVAULT contract + */ + struct getAmountOfShareQvaultHold_output + { + uint32 amount; + }; + + /** + * Retrieves the amount of a specific asset held by the QVAULT contract + * Useful for checking contract's asset holdings and portfolio composition + * + * @param input Asset information to query + * @param output Amount of shares held by the contract + */ + PUBLIC_FUNCTION(getAmountOfShareQvaultHold) + { + output.amount = (uint32)qpi.numberOfShares(input.assetInfo, AssetOwnershipSelect::byOwner(SELF), AssetPossessionSelect::byPossessor(SELF)); + } + + /** + * Input structure for getNumberOfHolderAndAvgAm function + * No input parameters required + */ + struct getNumberOfHolderAndAvgAm_input + { + + }; + + /** + * Output structure for getNumberOfHolderAndAvgAm function + * @param numberOfQcapHolder Number of unique QCAP token holders + * @param avgAmount Average QCAP amount per holder + */ + struct getNumberOfHolderAndAvgAm_output + { + sint32 returnCode; // Status code indicating success or failure + uint32 numberOfQcapHolder; + uint32 avgAmount; + }; + + /** + * Local variables for getNumberOfHolderAndAvgAm function + */ + struct getNumberOfHolderAndAvgAm_locals + { + AssetPossessionIterator iter; // Iterator for asset possession + Asset QCAPId; // QCAP asset identifier + uint32 count; // Counter for holders + uint32 numberOfDuplicatedPossesor; // Counter for duplicate possessors + }; + + /** + * Calculates the number of unique QCAP token holders and average amount per holder + * Accounts for duplicate possessors across different contracts + * Useful for token distribution analysis and economic metrics + * + * @param input No input parameters + * @param output Number of holders and average amount per holder + */ + PUBLIC_FUNCTION_WITH_LOCALS(getNumberOfHolderAndAvgAm) + { + locals.QCAPId.assetName = QVAULT_QCAP_ASSETNAME; + locals.QCAPId.issuer = state.QCAP_ISSUER; + + locals.iter.begin(locals.QCAPId); + while (!locals.iter.reachedEnd()) + { + if (locals.iter.numberOfPossessedShares() > 0 && locals.iter.possessor() != SELF) + { + locals.count++; + } + + if (qpi.numberOfPossessedShares(QVAULT_QCAP_ASSETNAME, state.QCAP_ISSUER, locals.iter.possessor(), locals.iter.possessor(), QX_CONTRACT_INDEX, QX_CONTRACT_INDEX) > 0 && qpi.numberOfPossessedShares(QVAULT_QCAP_ASSETNAME, state.QCAP_ISSUER, locals.iter.possessor(), locals.iter.possessor(), QVAULT_CONTRACT_INDEX, QVAULT_CONTRACT_INDEX) > 0 && locals.iter.possessor() != SELF) + { + locals.numberOfDuplicatedPossesor++; + } + + locals.iter.next(); + } + + locals.numberOfDuplicatedPossesor = (uint32)div(locals.numberOfDuplicatedPossesor * 1ULL, 2ULL); - } + output.numberOfQcapHolder = locals.count - locals.numberOfDuplicatedPossesor; + output.avgAmount = (uint32)div(qpi.numberOfShares(locals.QCAPId) - qpi.numberOfShares(locals.QCAPId, AssetOwnershipSelect::byOwner(SELF), AssetPossessionSelect::byPossessor(SELF)) + state.totalStakedQcapAmount * 1ULL, output.numberOfQcapHolder * 1ULL); + output.returnCode = QVAULT_SUCCESS; + } - struct END_EPOCH_locals + /** + * Input structure for getAmountForQearnInUpcomingEpoch function + * @param epoch Future epoch to check QEarn funding for + */ + struct getAmountForQearnInUpcomingEpoch_input { - Entity entity; - AssetPossessionIterator iter; - Asset QCAPId; - uint64 revenue; - uint64 paymentForShareholders; - uint64 paymentForQCAPHolders; - uint64 paymentForReinvest; - uint64 paymentForDevelopment; - uint64 amountOfBurn; - uint64 circulatedSupply; - uint32 _t; - id possessorPubkey; + uint32 epoch; }; - END_EPOCH_WITH_LOCALS() + /** + * Output structure for getAmountForQearnInUpcomingEpoch function + * @param amount Total amount allocated for QEarn in the specified epoch + */ + struct getAmountForQearnInUpcomingEpoch_output { - qpi.getEntity(SELF, locals.entity); - locals.revenue = locals.entity.incomingAmount - locals.entity.outgoingAmount; + sint32 returnCode; // Status code indicating success or failure + uint64 amount; + }; - locals.paymentForShareholders = div(locals.revenue * state.shareholderDividend, 1000ULL); - locals.paymentForQCAPHolders = div(locals.revenue * state.QCAPHolderPermille, 1000ULL); - locals.paymentForReinvest = div(locals.revenue * state.reinvestingPermille, 1000ULL); - locals.amountOfBurn = div(locals.revenue * state.burnPermille, 1000ULL); - locals.paymentForDevelopment = locals.revenue - locals.paymentForShareholders - locals.paymentForQCAPHolders - locals.paymentForReinvest - locals.amountOfBurn; + /** + * Local variables for getAmountForQearnInUpcomingEpoch function + */ + struct getAmountForQearnInUpcomingEpoch_locals + { + sint32 _t; // Loop counter variable + }; - if(locals.paymentForReinvest > QVAULT_MAX_REINVEST_AMOUNT) + /** + * Calculates the total amount allocated for QEarn participation in a future epoch + * Only considers passed QEarn proposals that are still active + * Returns 0 for past or current epochs + * + * @param input Future epoch number to check + * @param output Total QEarn funding amount for the epoch + */ + PUBLIC_FUNCTION_WITH_LOCALS(getAmountForQearnInUpcomingEpoch) + { + if (input.epoch <= qpi.epoch()) + { + output.returnCode = QVAULT_INPUT_ERROR; + return ; + } + for (locals._t = state.numberOfQEarnP - 1; locals._t >= 0; locals._t--) { - locals.paymentForQCAPHolders += locals.paymentForReinvest - QVAULT_MAX_REINVEST_AMOUNT; - locals.paymentForReinvest = QVAULT_MAX_REINVEST_AMOUNT; + if (state.QEarnP.get(locals._t).proposedEpoch + 1 < qpi.epoch() && state.QEarnP.get(locals._t).result == 0 && state.QEarnP.get(locals._t).numberOfEpoch + state.QEarnP.get(locals._t).proposedEpoch + 1 >= input.epoch) + { + output.amount += state.QEarnP.get(locals._t).assignedFundPerEpoch; + } } + output.returnCode = QVAULT_SUCCESS; + } - qpi.distributeDividends(div(locals.paymentForShareholders, 676ULL)); - qpi.transfer(state.adminAddress, locals.paymentForDevelopment); - qpi.transfer(state.reinvestingAddress, locals.paymentForReinvest); - qpi.burn(locals.amountOfBurn); + REGISTER_USER_FUNCTIONS_AND_PROCEDURES() + { + REGISTER_USER_FUNCTION(getData, 1); + REGISTER_USER_FUNCTION(getStakedAmountAndVotingPower, 2); + REGISTER_USER_FUNCTION(getGP, 3); + REGISTER_USER_FUNCTION(getQCP, 4); + REGISTER_USER_FUNCTION(getIPOP, 5); + REGISTER_USER_FUNCTION(getQEarnP, 6); + REGISTER_USER_FUNCTION(getFundP, 7); + REGISTER_USER_FUNCTION(getMKTP, 8); + REGISTER_USER_FUNCTION(getAlloP, 9); + REGISTER_USER_FUNCTION(getIdentitiesHvVtPw, 11); + REGISTER_USER_FUNCTION(ppCreationPower, 12); + REGISTER_USER_FUNCTION(getQcapBurntAmountInLastEpoches, 13); + REGISTER_USER_FUNCTION(getAmountToBeSoldPerYear, 14); + REGISTER_USER_FUNCTION(getTotalRevenueInQcap, 15); + REGISTER_USER_FUNCTION(getRevenueInQcapPerEpoch, 16); + REGISTER_USER_FUNCTION(getRevenuePerShare, 17); + REGISTER_USER_FUNCTION(getAmountOfShareQvaultHold, 18); + REGISTER_USER_FUNCTION(getNumberOfHolderAndAvgAm, 19); + REGISTER_USER_FUNCTION(getAmountForQearnInUpcomingEpoch, 20); + + REGISTER_USER_PROCEDURE(stake, 1); + REGISTER_USER_PROCEDURE(unStake, 2); + REGISTER_USER_PROCEDURE(submitGP, 3); + REGISTER_USER_PROCEDURE(submitQCP, 4); + REGISTER_USER_PROCEDURE(submitIPOP, 5); + REGISTER_USER_PROCEDURE(submitQEarnP, 6); + REGISTER_USER_PROCEDURE(submitFundP, 7); + REGISTER_USER_PROCEDURE(submitMKTP, 8); + REGISTER_USER_PROCEDURE(submitAlloP, 9); + REGISTER_USER_PROCEDURE(voteInProposal, 11); + REGISTER_USER_PROCEDURE(buyQcap, 12); + REGISTER_USER_PROCEDURE(TransferShareManagementRights, 13); + } + + INITIALIZE() + { + state.QCAP_ISSUER = ID(_Q, _C, _A, _P, _W, _M, _Y, _R, _S, _H, _L, _B, _J, _H, _S, _T, _T, _Z, _Q, _V, _C, _I, _B, _A, _R, _V, _O, _A, _S, _K, _D, _E, _N, _A, _S, _A, _K, _N, _O, _B, _R, _G, _P, _F, _W, _W, _K, _R, _C, _U, _V, _U, _A, _X, _Y, _E); + state.qcapSoldAmount = 1909423; + state.transferRightsFee = 100; + state.quorumPercent = 670; + state.qcapBurnPermille = 0; + state.burnPermille = 0; + state.QCAPHolderPermille = 520; + state.reinvestingPermille = 450; + state.shareholderDividend = 30; + + } + + /** + * Local variables for BEGIN_EPOCH function + */ + struct BEGIN_EPOCH_locals + { + QEARN::lock_input lock_input; // Input for QEARN lock function + QEARN::lock_output lock_output; // Output from QEARN lock function + QX::AssetAskOrders_input assetAskOrders_input; // Input for QX asset ask orders + QX::AssetAskOrders_output assetAskOrders_output; // Output from QX asset ask orders + QX::AddToBidOrder_input addToBidOrder_input; // Input for QX add to bid order + QX::AddToBidOrder_output addToBidOrder_output; // Output from QX add to bid order + QX::TransferShareManagementRights_input transferShareManagementRights_input; // Input for QX transfer rights + QX::TransferShareManagementRights_output transferShareManagementRights_output; // Output from QX transfer rights + uint32 purchasedQcap; // Amount of QCAP purchased for burning + sint32 _t; // Loop counter variable + }; + + /** + * Executes at the beginning of each epoch + * Handles QEarn fund locking, quorum changes, allocation updates, and QCAP burning + * Processes passed proposals and updates contract state accordingly + */ + BEGIN_EPOCH_WITH_LOCALS() + { + for (locals._t = 0 ; locals._t < (sint32)state.numberOfQEarnP; locals._t++) + { + if (state.QEarnP.get(locals._t).result == 0 && state.QEarnP.get(locals._t).proposedEpoch + state.QEarnP.get(locals._t).numberOfEpoch + 1 >= qpi.epoch()) + { + if (state.QEarnP.get(locals._t).proposedEpoch + 1 == qpi.epoch()) + { + continue; + } + INVOKE_OTHER_CONTRACT_PROCEDURE(QEARN, lock, locals.lock_input, locals.lock_output, state.QEarnP.get(locals._t).assignedFundPerEpoch); + } + } + + for (locals._t = state.numberOfQCP - 1 ; locals._t >= 0; locals._t--) + { + if (state.QCP.get(locals._t).result == 0 && state.QCP.get(locals._t).proposedEpoch + 2 == qpi.epoch()) + { + state.quorumPercent = state.QCP.get(locals._t).newQuorumPercent; + break; + } + if (state.QCP.get(locals._t).proposedEpoch + 2 < qpi.epoch()) + { + break; + } + } - locals.circulatedSupply = QVAULT_QCAP_MAX_SUPPLY; + for (locals._t = state.numberOfAlloP - 1; locals._t >= 0; locals._t--) + { + if (state.AlloP.get(locals._t).result == 0 && state.AlloP.get(locals._t).proposedEpoch + 2 == qpi.epoch()) + { + state.QCAPHolderPermille = state.AlloP.get(locals._t).distributed; + state.reinvestingPermille = state.AlloP.get(locals._t).reinvested; + state.qcapBurnPermille = state.AlloP.get(locals._t).burnQcap; + break; + } + if (state.AlloP.get(locals._t).proposedEpoch + 2 < qpi.epoch()) + { + break; + } + } - for(locals._t = 0 ; locals._t < state.numberOfBannedAddress; locals._t++) + locals.purchasedQcap = 0; + while(state.fundForBurn > 0) { - locals.circulatedSupply -= qpi.numberOfPossessedShares(QVAULT_QCAP_ASSETNAME, state.QCAP_ISSUER, state.bannedAddress.get(locals._t), state.bannedAddress.get(locals._t), QX_CONTRACT_INDEX, QX_CONTRACT_INDEX); + locals.assetAskOrders_input.assetName = QVAULT_QCAP_ASSETNAME; + locals.assetAskOrders_input.issuer = state.QCAP_ISSUER; + locals.assetAskOrders_input.offset = 0; + CALL_OTHER_CONTRACT_FUNCTION(QX, AssetAskOrders, locals.assetAskOrders_input, locals.assetAskOrders_output); + if (locals.assetAskOrders_output.orders.get(0).price <= (sint64)state.fundForBurn && locals.assetAskOrders_output.orders.get(0).price != 0) + { + locals.addToBidOrder_input.assetName = QVAULT_QCAP_ASSETNAME; + locals.addToBidOrder_input.issuer = state.QCAP_ISSUER; + locals.addToBidOrder_input.numberOfShares = (sint64)div(state.fundForBurn, locals.assetAskOrders_output.orders.get(0).price * 1ULL) > locals.assetAskOrders_output.orders.get(0).numberOfShares ? locals.assetAskOrders_output.orders.get(0).numberOfShares : div(state.fundForBurn, locals.assetAskOrders_output.orders.get(0).price * 1ULL); + locals.addToBidOrder_input.price = locals.assetAskOrders_output.orders.get(0).price; + + INVOKE_OTHER_CONTRACT_PROCEDURE(QX, AddToBidOrder, locals.addToBidOrder_input, locals.addToBidOrder_output, locals.addToBidOrder_input.price * locals.addToBidOrder_input.numberOfShares); + + state.fundForBurn -= locals.addToBidOrder_input.price * locals.addToBidOrder_input.numberOfShares; + state.burntQcapAmPerEpoch.set(qpi.epoch(), state.burntQcapAmPerEpoch.get(qpi.epoch()) + (uint32)locals.addToBidOrder_input.numberOfShares); + state.totalQcapBurntAmount += (uint32)locals.addToBidOrder_input.numberOfShares; + + locals.purchasedQcap += (uint32)locals.addToBidOrder_output.addedNumberOfShares; + } + else + { + break; + } } + locals.transferShareManagementRights_input.asset.assetName = QVAULT_QCAP_ASSETNAME; + locals.transferShareManagementRights_input.asset.issuer = state.QCAP_ISSUER; + locals.transferShareManagementRights_input.newManagingContractIndex = SELF_INDEX; + locals.transferShareManagementRights_input.numberOfShares = locals.purchasedQcap; + + INVOKE_OTHER_CONTRACT_PROCEDURE(QX, TransferShareManagementRights, locals.transferShareManagementRights_input, locals.transferShareManagementRights_output, 0); + + qpi.transferShareOwnershipAndPossession(QVAULT_QCAP_ASSETNAME, state.QCAP_ISSUER, SELF, SELF, locals.purchasedQcap, NULL_ID); + } + + /** + * Local variables for END_EPOCH function + */ + struct END_EPOCH_locals + { + QX::TransferShareManagementRights_input transferShareManagementRights_input; // Input for QX transfer rights + QX::TransferShareManagementRights_output transferShareManagementRights_output; // Output from QX transfer rights + GPInfo updatedGProposal; // Updated general proposal + QCPInfo updatedQCProposal; // Updated quorum change proposal + IPOPInfo updatedIPOProposal; // Updated IPO participation proposal + QEarnPInfo updatedQEarnProposal; // Updated QEarn participation proposal + FundPInfo updatedFundProposal; // Updated fundraising proposal + MKTPInfo updatedMKTProposal; // Updated marketplace proposal + AlloPInfo updatedAlloProposal; // Updated allocation proposal + Entity entity; // Entity information + AssetPossessionIterator iter; // Iterator for asset possession + Asset QCAPId; // QCAP asset identifier + id possessorPubkey; // Possessor public key + uint64 paymentForShareholders; // Payment amount for shareholders + uint64 paymentForQCAPHolders; // Payment amount for QCAP holders + uint64 paymentForReinvest; // Payment amount for reinvestment + uint64 amountOfBurn; // Amount to burn + uint64 circulatedSupply; // Circulating supply of QCAP + uint64 requiredFund; // Required fund amount + uint64 tmpAmount; // Temporary amount variable + uint64 paymentForQcapBurn; // Payment amount for QCAP burning + uint32 numberOfVote; // Number of votes + sint32 _t, _r; // Loop counter variables + uint32 curDate; // Current date (packed) + uint8 year; // Current year + uint8 month; // Current month + uint8 day; // Current day + uint8 hour; // Current hour + uint8 minute; // Current minute + uint8 second; // Current second + }; + + /** + * Executes at the end of each epoch + * Distributes revenue to stakeholders, processes proposal results, and updates voting power + * Handles all proposal voting outcomes and state updates + */ + END_EPOCH_WITH_LOCALS() + { + locals.paymentForShareholders = div(state.totalEpochRevenue * state.shareholderDividend, 1000ULL); + locals.paymentForQCAPHolders = div(state.totalEpochRevenue * state.QCAPHolderPermille, 1000ULL); + locals.paymentForReinvest = div(state.totalEpochRevenue * state.reinvestingPermille, 1000ULL); + locals.paymentForQcapBurn = div(state.totalEpochRevenue * state.qcapBurnPermille, 1000ULL); + locals.amountOfBurn = div(state.totalEpochRevenue * state.burnPermille, 1000ULL); + + qpi.distributeDividends(div(locals.paymentForShareholders + state.proposalCreateFund, 676ULL)); + qpi.burn(locals.amountOfBurn); + locals.QCAPId.assetName = QVAULT_QCAP_ASSETNAME; locals.QCAPId.issuer = state.QCAP_ISSUER; + locals.circulatedSupply = qpi.numberOfShares(locals.QCAPId) - qpi.numberOfShares(locals.QCAPId, AssetOwnershipSelect::byOwner(SELF), AssetPossessionSelect::byPossessor(SELF)) + state.totalStakedQcapAmount; + + state.revenueForOneQcapPerEpoch.set(qpi.epoch(), div(locals.paymentForQCAPHolders, locals.circulatedSupply * 1ULL)); + state.revenueForOneQvaultPerEpoch.set(qpi.epoch(), div(locals.paymentForShareholders + state.proposalCreateFund, 676ULL)); + state.revenueForReinvestPerEpoch.set(qpi.epoch(), locals.paymentForReinvest); + + state.reinvestingFund += locals.paymentForReinvest; + state.fundForBurn += locals.paymentForQcapBurn; + state.totalEpochRevenue = 0; + state.proposalCreateFund = 0; + locals.iter.begin(locals.QCAPId); while (!locals.iter.reachedEnd()) { locals.possessorPubkey = locals.iter.possessor(); - for(locals._t = 0 ; locals._t < state.numberOfBannedAddress; locals._t++) + if (locals.possessorPubkey != SELF) + { + qpi.transfer(locals.possessorPubkey, div(locals.paymentForQCAPHolders, locals.circulatedSupply) * locals.iter.numberOfPossessedShares()); + } + + locals.iter.next(); + } + + for (locals._t = 0 ; locals._t < (sint32)state.numberOfStaker; locals._t++) + { + qpi.transfer(state.staker.get(locals._t).stakerAddress, div(locals.paymentForQCAPHolders, locals.circulatedSupply) * state.staker.get(locals._t).amount); + } + + /* + General Proposal Result + */ + + for (locals._t = state.numberOfGP - 1; locals._t >= 0; locals._t--) + { + if (state.GP.get(locals._t).proposedEpoch == qpi.epoch()) + { + locals.updatedGProposal = state.GP.get(locals._t); + if (state.GP.get(locals._t).numberOfYes + state.GP.get(locals._t).numberOfNo < div(state.GP.get(locals._t).currentQuorumPercent * state.GP.get(locals._t).currentTotalVotingPower * 1ULL, 1000ULL)) + { + locals.updatedGProposal.result = QVAULT_PROPOSAL_INSUFFICIENT_QUORUM; + } + else if (state.GP.get(locals._t).numberOfYes <= state.GP.get(locals._t).numberOfNo) + { + locals.updatedGProposal.result = QVAULT_PROPOSAL_REJECTED; + } + else + { + locals.updatedGProposal.result = QVAULT_PROPOSAL_PASSED; + } + state.GP.set(locals._t, locals.updatedGProposal); + } + else + { + break; + } + } + + /* + Quorum Proposal Result + */ + + locals.numberOfVote = 0; + + for (locals._t = state.numberOfQCP - 1; locals._t >= 0; locals._t--) + { + if (state.QCP.get(locals._t).proposedEpoch == qpi.epoch()) + { + locals.updatedQCProposal = state.QCP.get(locals._t); + if (state.QCP.get(locals._t).numberOfYes + state.QCP.get(locals._t).numberOfNo < div(state.QCP.get(locals._t).currentQuorumPercent * state.QCP.get(locals._t).currentTotalVotingPower * 1ULL, 1000ULL)) + { + locals.updatedQCProposal.result = QVAULT_PROPOSAL_INSUFFICIENT_QUORUM; + } + else if (state.QCP.get(locals._t).numberOfYes <= state.QCP.get(locals._t).numberOfNo) + { + locals.updatedQCProposal.result = QVAULT_PROPOSAL_REJECTED; + } + else + { + locals.updatedQCProposal.result = QVAULT_PROPOSAL_PASSED; + if (locals.numberOfVote < state.QCP.get(locals._t).numberOfYes) + { + locals.numberOfVote = state.QCP.get(locals._t).numberOfYes; + } + + } + state.QCP.set(locals._t, locals.updatedQCProposal); + } + else + { + break; + } + } + + for (locals._t = state.numberOfQCP - 1; locals._t >= 0; locals._t--) + { + if (state.QCP.get(locals._t).proposedEpoch == qpi.epoch() && state.QCP.get(locals._t).numberOfYes != locals.numberOfVote) + { + locals.updatedQCProposal = state.QCP.get(locals._t); + locals.updatedQCProposal.result = QVAULT_PROPOSAL_INSUFFICIENT_VOTING_POWER; + state.QCP.set(locals._t, locals.updatedQCProposal); + } + if (state.QCP.get(locals._t).proposedEpoch != qpi.epoch()) + { + break; + } + } + + /* + IPO Proposal Result + */ + + locals.requiredFund = 0; + + for (locals._t = state.numberOfIPOP - 1; locals._t >= 0; locals._t--) + { + if (state.IPOP.get(locals._t).proposedEpoch == qpi.epoch()) + { + locals.updatedIPOProposal = state.IPOP.get(locals._t); + if (state.IPOP.get(locals._t).numberOfYes + state.IPOP.get(locals._t).numberOfNo < div(state.IPOP.get(locals._t).currentQuorumPercent * state.IPOP.get(locals._t).currentTotalVotingPower * 1ULL, 1000ULL)) + { + locals.updatedIPOProposal.result = QVAULT_PROPOSAL_INSUFFICIENT_QUORUM; + } + else if (state.IPOP.get(locals._t).numberOfYes <= state.IPOP.get(locals._t).numberOfNo) + { + locals.updatedIPOProposal.result = QVAULT_PROPOSAL_REJECTED; + } + else + { + locals.updatedIPOProposal.result = QVAULT_PROPOSAL_PASSED; + locals.requiredFund += div(locals.updatedIPOProposal.totalWeight * 1ULL, locals.updatedIPOProposal.numberOfYes * 1ULL); + locals.updatedIPOProposal.assignedFund = div(locals.updatedIPOProposal.totalWeight * 1ULL, locals.updatedIPOProposal.numberOfYes * 1ULL); + } + state.IPOP.set(locals._t, locals.updatedIPOProposal); + } + else + { + break; + } + } + + locals.tmpAmount = 0; + + for (locals._t = state.numberOfIPOP - 1; locals._t >= 0; locals._t--) + { + if (state.IPOP.get(locals._t).proposedEpoch == qpi.epoch()) + { + if (state.IPOP.get(locals._t).result != 0) + { + continue; + } + locals.updatedIPOProposal = state.IPOP.get(locals._t); + if (state.reinvestingFund < locals.requiredFund) + { + locals.updatedIPOProposal.assignedFund = div(div(div(locals.updatedIPOProposal.totalWeight * 1ULL, locals.updatedIPOProposal.numberOfYes * 1ULL) * 1000, locals.requiredFund) * state.reinvestingFund, 1000ULL); + } + state.IPOP.set(locals._t, locals.updatedIPOProposal); + + for (locals._r = 675 ; locals._r >= 0; locals._r--) + { + if ((676 - locals._r) * (qpi.ipoBidPrice(locals.updatedIPOProposal.ipoContractIndex, locals._r) + 1) > (sint64)locals.updatedIPOProposal.assignedFund) + { + if (locals._r == 675) + { + break; + } + qpi.bidInIPO(locals.updatedIPOProposal.ipoContractIndex, qpi.ipoBidPrice(locals.updatedIPOProposal.ipoContractIndex, locals._r + 1) + 1, 675 - locals._r); + locals.tmpAmount += (qpi.ipoBidPrice(locals.updatedIPOProposal.ipoContractIndex, locals._r + 1) + 1) * (675 - locals._r); + break; + } + } + } + else + { + break; + } + } + + state.reinvestingFund -= locals.tmpAmount; + + /* + QEarn Proposal Result + */ + + locals.requiredFund = 0; + + for (locals._t = state.numberOfQEarnP - 1; locals._t >= 0; locals._t--) + { + if (state.QEarnP.get(locals._t).proposedEpoch == qpi.epoch()) + { + locals.updatedQEarnProposal = state.QEarnP.get(locals._t); + if (state.QEarnP.get(locals._t).numberOfYes + state.QEarnP.get(locals._t).numberOfNo < div(state.QEarnP.get(locals._t).currentQuorumPercent * state.QEarnP.get(locals._t).currentTotalVotingPower * 1ULL, 1000ULL)) + { + locals.updatedQEarnProposal.result = QVAULT_PROPOSAL_INSUFFICIENT_QUORUM; + } + else if (state.QEarnP.get(locals._t).numberOfYes <= state.QEarnP.get(locals._t).numberOfNo) + { + locals.updatedQEarnProposal.result = QVAULT_PROPOSAL_REJECTED; + } + else + { + locals.updatedQEarnProposal.result = QVAULT_PROPOSAL_PASSED; + locals.requiredFund += locals.updatedQEarnProposal.amountOfInvestPerEpoch * locals.updatedQEarnProposal.numberOfEpoch; + } + state.QEarnP.set(locals._t, locals.updatedQEarnProposal); + } + else + { + break; + } + } + + locals.tmpAmount = 0; + + if (state.reinvestingFund < locals.requiredFund) + { + for (locals._t = state.numberOfQEarnP - 1; locals._t >= 0; locals._t--) { - if(locals.possessorPubkey == state.bannedAddress.get(locals._t)) + if (state.QEarnP.get(locals._t).proposedEpoch == qpi.epoch()) + { + if (state.QEarnP.get(locals._t).result != 0) + { + continue; + } + locals.updatedQEarnProposal = state.QEarnP.get(locals._t); + locals.updatedQEarnProposal.assignedFundPerEpoch = div(div(div(locals.updatedQEarnProposal.numberOfEpoch * locals.updatedQEarnProposal.amountOfInvestPerEpoch * 1000, locals.requiredFund) * state.reinvestingFund, 1000ULL), locals.updatedQEarnProposal.numberOfEpoch * 1ULL); + locals.tmpAmount += locals.updatedQEarnProposal.assignedFundPerEpoch * locals.updatedQEarnProposal.numberOfEpoch; + state.QEarnP.set(locals._t, locals.updatedQEarnProposal); + } + else { break; } } + state.reinvestingFund -= locals.tmpAmount; + } + + else + { + state.reinvestingFund -= locals.requiredFund; + } + + /* + Fundrasing Proposal Result + */ + + for (locals._t = state.numberOfFundP - 1; locals._t >= 0; locals._t--) + { + if (state.FundP.get(locals._t).proposedEpoch == qpi.epoch()) + { + locals.updatedFundProposal = state.FundP.get(locals._t); + if (state.FundP.get(locals._t).numberOfYes + state.FundP.get(locals._t).numberOfNo < div(state.FundP.get(locals._t).currentQuorumPercent * state.FundP.get(locals._t).currentTotalVotingPower * 1ULL, 1000ULL)) + { + locals.updatedFundProposal.result = QVAULT_PROPOSAL_INSUFFICIENT_QUORUM; + } + else if (state.FundP.get(locals._t).numberOfYes <= state.FundP.get(locals._t).numberOfNo) + { + locals.updatedFundProposal.result = QVAULT_PROPOSAL_REJECTED; + } + else + { + locals.updatedFundProposal.result = QVAULT_PROPOSAL_PASSED; + + packQvaultDate(qpi.year(), qpi.month(), qpi.day(), qpi.hour(), qpi.minute(), qpi.second(), locals.curDate); + unpackQvaultDate(locals.year, locals.month, locals.day, locals.hour, locals.minute, locals.second, locals.curDate); + if (locals.year == 25 && state.qcapSoldAmount + locals.updatedFundProposal.amountOfQcap > QVAULT_2025MAX_QCAP_SALE_AMOUNT) + { + locals.updatedFundProposal.result = QVAULT_PROPOSAL_INSUFFICIENT_QCAP; + } + else if (locals.year == 26 && state.qcapSoldAmount + locals.updatedFundProposal.amountOfQcap > QVAULT_2026MAX_QCAP_SALE_AMOUNT) + { + locals.updatedFundProposal.result = QVAULT_PROPOSAL_INSUFFICIENT_QCAP; + } + else if (locals.year == 27 && state.qcapSoldAmount + locals.updatedFundProposal.amountOfQcap > QVAULT_2027MAX_QCAP_SALE_AMOUNT) + { + locals.updatedFundProposal.result = QVAULT_PROPOSAL_INSUFFICIENT_QCAP; + } + else if (state.qcapSoldAmount + locals.updatedFundProposal.amountOfQcap > QVAULT_QCAP_MAX_SUPPLY) + { + locals.updatedFundProposal.result = QVAULT_PROPOSAL_INSUFFICIENT_QCAP; + } + } + state.FundP.set(locals._t, locals.updatedFundProposal); + } + else + { + break; + } + } + + /* + Marketplace Proposal Result + */ - if(locals._t == state.numberOfBannedAddress) + for (locals._t = state.numberOfMKTP - 1; locals._t >= 0; locals._t--) + { + if (state.MKTP.get(locals._t).proposedEpoch == qpi.epoch()) + { + locals.updatedMKTProposal = state.MKTP.get(locals._t); + if (state.MKTP.get(locals._t).numberOfYes + state.MKTP.get(locals._t).numberOfNo < div(state.MKTP.get(locals._t).currentQuorumPercent * state.MKTP.get(locals._t).currentTotalVotingPower * 1ULL, 1000ULL)) + { + locals.updatedMKTProposal.result = QVAULT_PROPOSAL_INSUFFICIENT_QUORUM; + qpi.transferShareOwnershipAndPossession(locals.updatedMKTProposal.shareName, NULL_ID, SELF, SELF, locals.updatedMKTProposal.amountOfShare, locals.updatedMKTProposal.proposer); + } + else if (state.MKTP.get(locals._t).numberOfYes <= state.MKTP.get(locals._t).numberOfNo) + { + locals.updatedMKTProposal.result = QVAULT_PROPOSAL_REJECTED; + qpi.transferShareOwnershipAndPossession(locals.updatedMKTProposal.shareName, NULL_ID, SELF, SELF, locals.updatedMKTProposal.amountOfShare, locals.updatedMKTProposal.proposer); + } + else + { + locals.updatedMKTProposal.result = QVAULT_PROPOSAL_PASSED; + + packQvaultDate(qpi.year(), qpi.month(), qpi.day(), qpi.hour(), qpi.minute(), qpi.second(), locals.curDate); + unpackQvaultDate(locals.year, locals.month, locals.day, locals.hour, locals.minute, locals.second, locals.curDate); + if (locals.year == 25 && state.qcapSoldAmount + locals.updatedMKTProposal.amountOfQcap > QVAULT_2025MAX_QCAP_SALE_AMOUNT) + { + locals.updatedMKTProposal.result = QVAULT_PROPOSAL_INSUFFICIENT_QCAP; + qpi.transferShareOwnershipAndPossession(locals.updatedMKTProposal.shareName, NULL_ID, SELF, SELF, locals.updatedMKTProposal.amountOfShare, locals.updatedMKTProposal.proposer); + } + else if (locals.year == 26 && state.qcapSoldAmount + locals.updatedMKTProposal.amountOfQcap > QVAULT_2026MAX_QCAP_SALE_AMOUNT) + { + locals.updatedMKTProposal.result = QVAULT_PROPOSAL_INSUFFICIENT_QCAP; + qpi.transferShareOwnershipAndPossession(locals.updatedMKTProposal.shareName, NULL_ID, SELF, SELF, locals.updatedMKTProposal.amountOfShare, locals.updatedMKTProposal.proposer); + } + else if (locals.year == 27 && state.qcapSoldAmount + locals.updatedMKTProposal.amountOfQcap > QVAULT_2027MAX_QCAP_SALE_AMOUNT) + { + locals.updatedMKTProposal.result = QVAULT_PROPOSAL_INSUFFICIENT_QCAP; + qpi.transferShareOwnershipAndPossession(locals.updatedMKTProposal.shareName, NULL_ID, SELF, SELF, locals.updatedMKTProposal.amountOfShare, locals.updatedMKTProposal.proposer); + } + else if (state.qcapSoldAmount + locals.updatedMKTProposal.amountOfQcap > QVAULT_QCAP_MAX_SUPPLY) + { + locals.updatedMKTProposal.result = QVAULT_PROPOSAL_INSUFFICIENT_QCAP; + qpi.transferShareOwnershipAndPossession(locals.updatedMKTProposal.shareName, NULL_ID, SELF, SELF, locals.updatedMKTProposal.amountOfShare, locals.updatedMKTProposal.proposer); + } + else if (state.reinvestingFund < locals.updatedMKTProposal.amountOfQubic) + { + locals.updatedMKTProposal.result = QVAULT_PROPOSAL_INSUFFICIENT_QCAP; + qpi.transferShareOwnershipAndPossession(locals.updatedMKTProposal.shareName, NULL_ID, SELF, SELF, locals.updatedMKTProposal.amountOfShare, locals.updatedMKTProposal.proposer); + } + else + { + state.qcapSoldAmount += locals.updatedMKTProposal.amountOfQcap; + + qpi.transfer(locals.updatedMKTProposal.proposer, locals.updatedMKTProposal.amountOfQubic); + + state.reinvestingFund -= locals.updatedMKTProposal.amountOfQubic; + + locals.transferShareManagementRights_input.asset.assetName = QVAULT_QCAP_ASSETNAME; + locals.transferShareManagementRights_input.asset.issuer = state.QCAP_ISSUER; + locals.transferShareManagementRights_input.newManagingContractIndex = SELF_INDEX; + locals.transferShareManagementRights_input.numberOfShares = locals.updatedMKTProposal.amountOfQcap; + + INVOKE_OTHER_CONTRACT_PROCEDURE(QX, TransferShareManagementRights, locals.transferShareManagementRights_input, locals.transferShareManagementRights_output, 0); + + qpi.transferShareOwnershipAndPossession(QVAULT_QCAP_ASSETNAME, state.QCAP_ISSUER, SELF, SELF, locals.updatedMKTProposal.amountOfQcap, locals.updatedMKTProposal.proposer); + } + } + state.MKTP.set(locals._t, locals.updatedMKTProposal); + } + else { - qpi.transfer(locals.possessorPubkey, div(locals.paymentForQCAPHolders, locals.circulatedSupply) * qpi.numberOfPossessedShares(QVAULT_QCAP_ASSETNAME, state.QCAP_ISSUER, locals.possessorPubkey, locals.possessorPubkey, QX_CONTRACT_INDEX, QX_CONTRACT_INDEX)); + break; } + } + - locals.iter.next(); + /* + Allocation Proposal Result + */ + + locals.numberOfVote = 0; + + for (locals._t = state.numberOfAlloP - 1; locals._t >= 0; locals._t--) + { + if (state.AlloP.get(locals._t).proposedEpoch == qpi.epoch()) + { + locals.updatedAlloProposal = state.AlloP.get(locals._t); + if (state.AlloP.get(locals._t).numberOfYes + state.AlloP.get(locals._t).numberOfNo < div(state.AlloP.get(locals._t).currentQuorumPercent * state.AlloP.get(locals._t).currentTotalVotingPower * 1ULL, 1000ULL)) + { + locals.updatedAlloProposal.result = QVAULT_PROPOSAL_INSUFFICIENT_QUORUM; + } + else if (state.AlloP.get(locals._t).numberOfYes <= state.AlloP.get(locals._t).numberOfNo) + { + locals.updatedAlloProposal.result = QVAULT_PROPOSAL_REJECTED; + } + else + { + locals.updatedAlloProposal.result = QVAULT_PROPOSAL_PASSED; + if (locals.numberOfVote < state.AlloP.get(locals._t).numberOfYes) + { + locals.numberOfVote = state.AlloP.get(locals._t).numberOfYes; + } + + } + state.AlloP.set(locals._t, locals.updatedAlloProposal); + } + else + { + break; + } + } + + for (locals._t = state.numberOfAlloP - 1; locals._t >= 0; locals._t--) + { + if (state.AlloP.get(locals._t).proposedEpoch == qpi.epoch() && state.AlloP.get(locals._t).numberOfYes != locals.numberOfVote) + { + locals.updatedAlloProposal = state.AlloP.get(locals._t); + locals.updatedAlloProposal.result = 3; + state.AlloP.set(locals._t, locals.updatedAlloProposal); + } + if (state.AlloP.get(locals._t).proposedEpoch != qpi.epoch()) + { + break; + } + } + + state.totalVotingPower = 0; + for (locals._t = 0 ; locals._t < (sint32)state.numberOfStaker; locals._t++) + { + state.votingPower.set(locals._t, state.staker.get(locals._t)); + state.totalVotingPower += state.staker.get(locals._t).amount; + } + state.numberOfVotingPower = state.numberOfStaker; + + state.vote.reset(); + state.countOfVote.reset(); + + } + + /** + * Local variables for POST_INCOMING_TRANSFER function + */ + struct POST_INCOMING_TRANSFER_locals + { + AssetPossessionIterator iter; // Iterator for asset possession + Asset QCAPId; // QCAP asset identifier + id possessorPubkey; // Possessor public key + uint64 lockedFund; // Amount of locked funds + sint32 _t; // Loop counter variable + }; + + /** + * Handles incoming transfers to the contract + * Processes different transfer types (standard, QPI, IPO refunds, dividends) + * Updates revenue tracking and fund allocations accordingly + */ + POST_INCOMING_TRANSFER_WITH_LOCALS() + { + switch (input.type) + { + case TransferType::standardTransaction: + state.totalHistoryRevenue += input.amount; + state.revenueInQcapPerEpoch.set(qpi.epoch(), state.revenueInQcapPerEpoch.get(qpi.epoch()) + input.amount); + + state.totalEpochRevenue += input.amount; + break; + case TransferType::qpiTransfer: + if (input.sourceId.u64._0 == QEARN_CONTRACT_INDEX) + { + locals.lockedFund = 0; + for (locals._t = state.numberOfQEarnP - 1; locals._t >= 0; locals._t--) + { + if (state.QEarnP.get(locals._t).result == 0 && state.QEarnP.get(locals._t).proposedEpoch + 54 <= qpi.epoch() && state.QEarnP.get(locals._t).proposedEpoch + 54 + state.QEarnP.get(locals._t).numberOfEpoch > qpi.epoch()) + { + locals.lockedFund += state.QEarnP.get(locals._t).assignedFundPerEpoch; + } + } + state.reinvestingFund += locals.lockedFund; + state.totalEpochRevenue += input.amount - locals.lockedFund; + state.totalHistoryRevenue += input.amount - locals.lockedFund; + state.revenueInQcapPerEpoch.set(qpi.epoch(), state.revenueInQcapPerEpoch.get(qpi.epoch()) + input.amount - locals.lockedFund); + state.revenueByQearn += input.amount - locals.lockedFund; + + } + else + { + state.totalHistoryRevenue += input.amount; + state.revenueInQcapPerEpoch.set(qpi.epoch(), state.revenueInQcapPerEpoch.get(qpi.epoch()) + input.amount); + + state.totalEpochRevenue += input.amount; + } + break; + case TransferType::ipoBidRefund: + state.reinvestingFund += input.amount; + break; + case TransferType::qpiDistributeDividends: + state.totalHistoryRevenue += input.amount; + state.revenueInQcapPerEpoch.set(qpi.epoch(), state.revenueInQcapPerEpoch.get(qpi.epoch()) + input.amount); + state.revenuePerShare.set(input.sourceId.u64._0, state.revenuePerShare.get(input.sourceId.u64._0) + input.amount); + + state.totalEpochRevenue += input.amount; + break; + default: + break; } + } + /** + * Pre-acquire shares hook function + * Always allows share transfers to the contract + * Used for controlling share acquisition behavior + */ + PRE_ACQUIRE_SHARES() + { + output.allowTransfer = true; } }; \ No newline at end of file diff --git a/test/contract_qvault.cpp b/test/contract_qvault.cpp index d73f8e150..d3661f0e4 100644 --- a/test/contract_qvault.cpp +++ b/test/contract_qvault.cpp @@ -8,20 +8,14 @@ static std::mt19937_64 rand64; static constexpr uint64 QVAULT_QCAP_MAX_HOLDERS = 131072; static constexpr uint64 QVAULT_ISSUE_ASSET_FEE = 1000000000ull; -static constexpr uint64 QVAULT_TOKEN_TRANSFER_FEE = 1000000ull; +static constexpr uint64 QVAULT_TOKEN_TRANSFER_FEE = 100; static constexpr uint32 QVAULT_SMALL_AMOUNT_QCAP_TRANSFER = 1000; static constexpr uint32 QVAULT_BIG_AMOUNT_QCAP_TRANSFER = 1000000; +static constexpr uint32 QVAULT_QCAP_SOLD_AMOUNT = 1909423; static constexpr uint64 QVAULT_MAX_REVENUE = 1000000000000ull; -static constexpr uint64 QVAULT_MIN_REVENUE = 100000000000ull; +static constexpr uint64 QVAULT_MIN_REVENUE = 100000000000ull;; static const id QVAULT_CONTRACT_ID(QVAULT_CONTRACT_INDEX, 0, 0, 0); const id QVAULT_QCAP_ISSUER = ID(_Q, _C, _A, _P, _W, _M, _Y, _R, _S, _H, _L, _B, _J, _H, _S, _T, _T, _Z, _Q, _V, _C, _I, _B, _A, _R, _V, _O, _A, _S, _K, _D, _E, _N, _A, _S, _A, _K, _N, _O, _B, _R, _G, _P, _F, _W, _W, _K, _R, _C, _U, _V, _U, _A, _X, _Y, _E); -const id QVAULT_authAddress1 = ID(_T, _K, _U, _W, _W, _S, _N, _B, _A, _E, _G, _W, _J, _H, _Q, _J, _D, _F, _L, _G, _Q, _H, _J, _J, _C, _J, _B, _A, _X, _B, _S, _Q, _M, _Q, _A, _Z, _J, _J, _D, _Y, _X, _E, _P, _B, _V, _B, _B, _L, _I, _Q, _A, _N, _J, _T, _I, _D); -const id QVAULT_authAddress2 = ID(_F, _X, _J, _F, _B, _T, _J, _M, _Y, _F, _J, _H, _P, _B, _X, _C, _D, _Q, _T, _L, _Y, _U, _K, _G, _M, _H, _B, _B, _Z, _A, _A, _F, _T, _I, _C, _W, _U, _K, _R, _B, _M, _E, _K, _Y, _N, _U, _P, _M, _R, _M, _B, _D, _N, _D, _R, _G); -const id QVAULT_authAddress3 = ID(_K, _E, _F, _D, _Z, _T, _Y, _L, _F, _E, _R, _A, _H, _D, _V, _L, _N, _Q, _O, _R, _D, _H, _F, _Q, _I, _B, _S, _B, _Z, _C, _W, _S, _Z, _X, _Z, _F, _F, _A, _N, _O, _T, _F, _A, _H, _W, _M, _O, _V, _G, _T, _R, _Q, _J, _P, _X, _D); -const id QVAULT_reinvestingAddress = ID(_R, _U, _U, _Y, _R, _V, _N, _K, _J, _X, _M, _L, _R, _B, _B, _I, _R, _I, _P, _D, _I, _B, _M, _H, _D, _H, _U, _A, _Z, _B, _Q, _K, _N, _B, _J, _T, _R, _D, _S, _P, _G, _C, _L, _Z, _C, _Q, _W, _A, _K, _C, _F, _Q, _J, _K, _K, _E); -const id QVAULT_adminAddress = ID(_H, _E, _C, _G, _U, _G, _H, _C, _J, _K, _Q, _O, _S, _D, _T, _M, _E, _H, _Q, _Y, _W, _D, _D, _T, _L, _F, _D, _A, _S, _Z, _K, _M, _G, _J, _L, _S, _R, _C, _S, _T, _H, _H, _A, _P, _P, _E, _D, _L, _G, _B, _L, _X, _J, _M, _N, _D); -const id QVAULT_initialBannedAddress1 = ID(_K, _E, _F, _D, _Z, _T, _Y, _L, _F, _E, _R, _A, _H, _D, _V, _L, _N, _Q, _O, _R, _D, _H, _F, _Q, _I, _B, _S, _B, _Z, _C, _W, _S, _Z, _X, _Z, _F, _F, _A, _N, _O, _T, _F, _A, _H, _W, _M, _O, _V, _G, _T, _R, _Q, _J, _P, _X, _D); -const id QVAULT_initialBannedAddress2 = ID(_E, _S, _C, _R, _O, _W, _B, _O, _T, _F, _T, _F, _I, _C, _I, _F, _P, _U, _X, _O, _J, _K, _G, _Q, _P, _Y, _X, _C, _A, _B, _L, _Z, _V, _M, _M, _U, _C, _M, _J, _F, _S, _G, _S, _A, _I, _A, _T, _Y, _I, _N, _V, _T, _Y, _G, _O, _A); static unsigned long long random(unsigned long long minValue, unsigned long long maxValue) { @@ -53,822 +47,863 @@ static std::vector getRandomUsers(unsigned int totalUsers, unsigned int maxN class QVAULTChecker : public QVAULT { public: - void endEpochChecker(uint64 revenue, const std::vector& QCAPHolders) - { - uint64 paymentForShareholders = QPI::div(revenue * shareholderDividend, 1000ULL); - uint64 paymentForQCAPHolders = QPI::div(revenue * QCAPHolderPermille, 1000ULL); - uint64 paymentForReinvest = QPI::div(revenue * reinvestingPermille, 1000ULL); - uint64 amountOfBurn = QPI::div(revenue * burnPermille, 1000ULL); - uint64 paymentForDevelopment = revenue - paymentForShareholders - paymentForQCAPHolders - paymentForReinvest - amountOfBurn; - - if(paymentForReinvest > QVAULT_MAX_REINVEST_AMOUNT) - { - paymentForQCAPHolders += paymentForReinvest - QVAULT_MAX_REINVEST_AMOUNT; - paymentForReinvest = QVAULT_MAX_REINVEST_AMOUNT; - } - - uint64 QCAPCirculatedSupply = QVAULT_QCAP_MAX_SUPPLY; - - for(uint64 i = 0 ; i < QCAPHolders.size(); i++) - { - for(uint64 j = 0 ; j < numberOfBannedAddress; j++) - { - if(QCAPHolders[i] == bannedAddress.get(j)) - { - QCAPCirculatedSupply -= numberOfPossessedShares(QVAULT_QCAP_ASSETNAME, QCAP_ISSUER, QCAPHolders[i], QCAPHolders[i], QX_CONTRACT_INDEX, QX_CONTRACT_INDEX); - break; - } - } - } - - QCAPCirculatedSupply -= numberOfPossessedShares(QVAULT_QCAP_ASSETNAME, QCAP_ISSUER, QVAULT_initialBannedAddress1, QVAULT_initialBannedAddress1, QX_CONTRACT_INDEX, QX_CONTRACT_INDEX); - QCAPCirculatedSupply -= numberOfPossessedShares(QVAULT_QCAP_ASSETNAME, QCAP_ISSUER, QVAULT_initialBannedAddress2, QVAULT_initialBannedAddress2, QX_CONTRACT_INDEX, QX_CONTRACT_INDEX); - /* - This for loop will check the revenue distributed to QCAPHolders. - */ - for (const auto& user : QCAPHolders) - { - uint64 j = 0; - for(j = 0 ; j < numberOfBannedAddress; j++) - { - if(user == bannedAddress.get(j)) - { - break; - } - } - if(j != numberOfBannedAddress) - { - continue; - } - EXPECT_EQ(QPI::div(paymentForQCAPHolders, QCAPCirculatedSupply) * numberOfPossessedShares(QVAULT_QCAP_ASSETNAME, QCAP_ISSUER, user, user, QX_CONTRACT_INDEX, QX_CONTRACT_INDEX), getBalance(user) - 1); - } - if(paymentForReinvest > QVAULT_MAX_REINVEST_AMOUNT) + void submitGPChecker(uint32 index, id proposer) + { + EXPECT_EQ(GP.get(index).currentQuorumPercent, 670); + EXPECT_EQ(GP.get(index).currentTotalVotingPower, 10000); + EXPECT_EQ(GP.get(index).numberOfNo, 0); + EXPECT_EQ(GP.get(index).numberOfYes, 0); + EXPECT_EQ(GP.get(index).proposedEpoch, 139); + EXPECT_EQ(GP.get(index).proposer, proposer); + EXPECT_EQ(GP.get(index).result, 5); + } + + void submitQCPChecker(uint32 index, id proposer, uint32 newQuorumPercent) + { + EXPECT_EQ(QCP.get(index).currentQuorumPercent, 670); + EXPECT_EQ(QCP.get(index).currentTotalVotingPower, 10000); + EXPECT_EQ(QCP.get(index).numberOfNo, 0); + EXPECT_EQ(QCP.get(index).numberOfYes, 0); + EXPECT_EQ(QCP.get(index).proposedEpoch, 139); + EXPECT_EQ(QCP.get(index).proposer, proposer); + EXPECT_EQ(QCP.get(index).result, 5); + EXPECT_EQ(QCP.get(index).newQuorumPercent, newQuorumPercent); + } + + void submitIPOPChecker(uint32 index, id proposer, uint32 ipoContractIndex) + { + EXPECT_EQ(IPOP.get(index).currentQuorumPercent, 670); + EXPECT_EQ(IPOP.get(index).currentTotalVotingPower, 10000); + EXPECT_EQ(IPOP.get(index).numberOfNo, 0); + EXPECT_EQ(IPOP.get(index).numberOfYes, 0); + EXPECT_EQ(IPOP.get(index).proposedEpoch, 139); + EXPECT_EQ(IPOP.get(index).proposer, proposer); + EXPECT_EQ(IPOP.get(index).result, 4); + EXPECT_EQ(IPOP.get(index).ipoContractIndex, ipoContractIndex); + EXPECT_EQ(IPOP.get(index).totalWeight, 0); + EXPECT_EQ(IPOP.get(index).assignedFund, 0); + } + + void submitQEarnPChecker(uint32 index, id proposer, uint64 amountPerEpoch, uint32 numberOfEpoch) + { + EXPECT_EQ(QEarnP.get(index).currentQuorumPercent, 670); + EXPECT_EQ(QEarnP.get(index).currentTotalVotingPower, 10000); + EXPECT_EQ(QEarnP.get(index).numberOfNo, 0); + EXPECT_EQ(QEarnP.get(index).numberOfYes, 0); + EXPECT_EQ(QEarnP.get(index).proposedEpoch, 139); + EXPECT_EQ(QEarnP.get(index).proposer, proposer); + EXPECT_EQ(QEarnP.get(index).result, 4); + EXPECT_EQ(QEarnP.get(index).amountOfInvestPerEpoch, amountPerEpoch); + EXPECT_EQ(QEarnP.get(index).assignedFundPerEpoch, amountPerEpoch); + EXPECT_EQ(QEarnP.get(index).numberOfEpoch, numberOfEpoch); + } + + void submitFundPChecker(uint32 index, id proposer, uint32 amountOfQcap, uint64 pricePerOneQcap) + { + EXPECT_EQ(FundP.get(index).currentQuorumPercent, 670); + EXPECT_EQ(FundP.get(index).currentTotalVotingPower, 10000); + EXPECT_EQ(FundP.get(index).numberOfNo, 0); + EXPECT_EQ(FundP.get(index).numberOfYes, 0); + EXPECT_EQ(FundP.get(index).proposedEpoch, 139); + EXPECT_EQ(FundP.get(index).proposer, proposer); + EXPECT_EQ(FundP.get(index).result, 5); + EXPECT_EQ(FundP.get(index).amountOfQcap, amountOfQcap); + EXPECT_EQ(FundP.get(index).pricePerOneQcap, pricePerOneQcap); + EXPECT_EQ(FundP.get(index).restSaleAmount, amountOfQcap); + } + + void submitMKTPChecker(uint32 index, id proposer, uint64 amountOfQcap, uint64 amountOfQubic, uint64 shareName, uint32 indexOfShare, uint32 amountOfShare) + { + EXPECT_EQ(MKTP.get(index).currentQuorumPercent, 670); + EXPECT_EQ(MKTP.get(index).currentTotalVotingPower, 10000); + EXPECT_EQ(MKTP.get(index).numberOfNo, 0); + EXPECT_EQ(MKTP.get(index).numberOfYes, 0); + EXPECT_EQ(MKTP.get(index).proposedEpoch, 139); + EXPECT_EQ(MKTP.get(index).proposer, proposer); + EXPECT_EQ(MKTP.get(index).result, 4); + EXPECT_EQ(MKTP.get(index).amountOfQcap, amountOfQcap); + EXPECT_EQ(MKTP.get(index).amountOfQubic, amountOfQubic); + EXPECT_EQ(MKTP.get(index).shareName, shareName); + EXPECT_EQ(MKTP.get(index).shareIndex, indexOfShare); + EXPECT_EQ(MKTP.get(index).amountOfShare, amountOfShare); + } + + void submitAlloPChecker(uint32 index, id proposer, uint32 reinvested, uint32 burn, uint32 distribute) + { + EXPECT_EQ(AlloP.get(index).currentQuorumPercent, 670); + EXPECT_EQ(AlloP.get(index).currentTotalVotingPower, 10000); + EXPECT_EQ(AlloP.get(index).numberOfNo, 0); + EXPECT_EQ(AlloP.get(index).numberOfYes, 0); + EXPECT_EQ(AlloP.get(index).proposedEpoch, 139); + EXPECT_EQ(AlloP.get(index).proposer, proposer); + EXPECT_EQ(AlloP.get(index).result, 5); + EXPECT_EQ(AlloP.get(index).reinvested, reinvested); + EXPECT_EQ(AlloP.get(index).burnQcap, burn); + EXPECT_EQ(AlloP.get(index).distributed, distribute); + } + + void voteInProposalChecker(uint32 proposalId, uint32 proposalType, uint32 numberOfYes, uint32 numberOfNo) + { + switch (proposalType) { - EXPECT_EQ(QVAULT_MAX_REINVEST_AMOUNT, getBalance(reinvestingAddress)); - } - else - { - EXPECT_EQ(paymentForReinvest, getBalance(reinvestingAddress)); + case 1: + EXPECT_EQ(GP.get(proposalId).numberOfYes, numberOfYes); + EXPECT_EQ(GP.get(proposalId).numberOfNo, numberOfNo); + break; + case 2: + EXPECT_EQ(QCP.get(proposalId).numberOfYes, numberOfYes); + EXPECT_EQ(QCP.get(proposalId).numberOfNo, numberOfNo); + break; + case 3: + EXPECT_EQ(IPOP.get(proposalId).numberOfYes, numberOfYes); + EXPECT_EQ(IPOP.get(proposalId).numberOfNo, numberOfNo); + break; + case 4: + EXPECT_EQ(QEarnP.get(proposalId).numberOfYes, numberOfYes); + EXPECT_EQ(QEarnP.get(proposalId).numberOfNo, numberOfNo); + break; + case 5: + EXPECT_EQ(FundP.get(proposalId).numberOfYes, numberOfYes); + EXPECT_EQ(FundP.get(proposalId).numberOfNo, numberOfNo); + break; + case 6: + EXPECT_EQ(MKTP.get(proposalId).numberOfYes, numberOfYes); + EXPECT_EQ(MKTP.get(proposalId).numberOfNo, numberOfNo); + break; + case 7: + EXPECT_EQ(AlloP.get(proposalId).numberOfYes, numberOfYes); + EXPECT_EQ(AlloP.get(proposalId).numberOfNo, numberOfNo); + break; + default: + break; } - EXPECT_EQ(paymentForDevelopment, getBalance(adminAddress)); } - void balanceChecker(const id& user) + void POST_INCOMING_TRANSFER_checker(uint64 distributedAmount, uint32 epoch, uint32 shareIndex) { - EXPECT_EQ(getBalance(user), 1); + EXPECT_EQ(distributedAmount, totalEpochRevenue); + EXPECT_EQ(distributedAmount, totalHistoryRevenue); + EXPECT_EQ(distributedAmount, revenueInQcapPerEpoch.get(epoch)); + EXPECT_EQ(distributedAmount, revenuePerShare.get(shareIndex)); } - void submitAuthAddressChecker() + void endEpochChecker(uint64 qxDistributedAmount) { - EXPECT_EQ(NULL_ID, newAuthAddress1); - EXPECT_EQ(NULL_ID, newAuthAddress2); - EXPECT_EQ(NULL_ID, newAuthAddress3); + EXPECT_EQ(reinvestingFund, div(qxDistributedAmount * reinvestingPermille, 1000ULL)); + EXPECT_EQ(fundForBurn, div(qxDistributedAmount * qcapBurnPermille, 1000ULL)); + EXPECT_EQ(totalEpochRevenue, 0); + EXPECT_EQ(proposalCreateFund, 0); } - void submitAuthAddressWithExactAuthId(const id& newAuthAddress) + void test() { - EXPECT_EQ(newAuthAddress1, newAuthAddress); - EXPECT_EQ(newAuthAddress2, newAuthAddress); - EXPECT_EQ(newAuthAddress3, newAuthAddress); + EXPECT_EQ(fundForBurn, 34523); + EXPECT_EQ(totalEpochRevenue, 0); } +}; - void changeAuthAddressChecker(uint32 numberOfAuth, const id& newAuthAddress) +class QXChecker : public QX +{ +public: + uint64 getDistributedAmountInEndTick() { - if(numberOfAuth == 1) - { - EXPECT_EQ(authAddress1, newAuthAddress); - } - else if(numberOfAuth == 2) - { - EXPECT_EQ(authAddress2, newAuthAddress); - } - else - { - EXPECT_EQ(authAddress3, newAuthAddress); - } + return _distributedAmount; } +}; - void submitDistributionPermilleChecker(uint32 newQCAPHolderPt, uint32 newReinvestingPt, uint32 newDevPt) +class ContractTestingQvault : protected ContractTesting +{ +public: + ContractTestingQvault() { - EXPECT_EQ(newQCAPHolderPt, newQCAPHolderPermille1); - EXPECT_EQ(newQCAPHolderPt, newQCAPHolderPermille2); - EXPECT_EQ(newQCAPHolderPt, newQCAPHolderPermille3); - - EXPECT_EQ(newReinvestingPt, newReinvestingPermille1); - EXPECT_EQ(newReinvestingPt, newReinvestingPermille2); - EXPECT_EQ(newReinvestingPt, newReinvestingPermille3); - - EXPECT_EQ(newDevPt, newDevPermille1); - EXPECT_EQ(newDevPt, newDevPermille2); - EXPECT_EQ(newDevPt, newDevPermille3); + initEmptySpectrum(); + initEmptyUniverse(); + INIT_CONTRACT(QVAULT); + callSystemProcedure(QVAULT_CONTRACT_INDEX, INITIALIZE); + INIT_CONTRACT(QX); + callSystemProcedure(QX_CONTRACT_INDEX, INITIALIZE); } - void changeDistributionPermilleChecker(uint32 newQCAPHolderPt, uint32 newReinvestingPt, uint32 newDevPt) + QVAULTChecker* getState() { - EXPECT_EQ(newQCAPHolderPt, QCAPHolderPermille); - EXPECT_EQ(newReinvestingPt, reinvestingPermille); - EXPECT_EQ(newDevPt, devPermille); + return (QVAULTChecker*)contractStates[QVAULT_CONTRACT_INDEX]; } - void submitReinvestingAddressChecker(const id& newReinvestingAddress) + QXChecker* qxGetState() { - EXPECT_EQ(newReinvestingAddress1, newReinvestingAddress); - EXPECT_EQ(newReinvestingAddress2, newReinvestingAddress); - EXPECT_EQ(newReinvestingAddress3, newReinvestingAddress); + return (QXChecker*)contractStates[QX_CONTRACT_INDEX]; } - void changeReinvestingAddressChecker(const id& newReinvestingAddress) + void endEpoch(bool expectSuccess = true) { - EXPECT_EQ(reinvestingAddress, newReinvestingAddress); + callSystemProcedure(QVAULT_CONTRACT_INDEX, END_EPOCH, expectSuccess); } - void submitAdminAddressChecker(const id& newAdminAddress) + void beginEpoch(bool expectSuccess = true) { - EXPECT_EQ(newAdminAddress1, newAdminAddress); - EXPECT_EQ(newAdminAddress2, newAdminAddress); - EXPECT_EQ(newAdminAddress3, newAdminAddress); + callSystemProcedure(QVAULT_CONTRACT_INDEX, BEGIN_EPOCH, expectSuccess); } - void changeAdminAddressChecker(const id& newAdminAddress) + void qxEndTick(bool expectSuccess = true) { - EXPECT_EQ(adminAddress, newAdminAddress); + callSystemProcedure(QX_CONTRACT_INDEX, END_TICK, expectSuccess); } - void submitBannedAddressChecker(const id& newBannedAddress) + QVAULT::getData_output getData() const { - EXPECT_EQ(bannedAddress1, newBannedAddress); - EXPECT_EQ(bannedAddress2, newBannedAddress); - EXPECT_EQ(bannedAddress3, newBannedAddress); + QVAULT::getData_input input; + QVAULT::getData_output output; + + callFunction(QVAULT_CONTRACT_INDEX, 1, input, output); + return output; } - void saveBannedAddressChecker(const id& newBannedAddress) + QVAULT::getStakedAmountAndVotingPower_output getStakedAmountAndVotingPower(id address) const { - EXPECT_EQ(bannedAddress.get(numberOfBannedAddress - 1), newBannedAddress); + QVAULT::getStakedAmountAndVotingPower_input input; + QVAULT::getStakedAmountAndVotingPower_output output; + + input.address = address; + + callFunction(QVAULT_CONTRACT_INDEX, 2, input, output); + return output; } - void submitUnbannedAddressChecker(const id& newUnbannedAddress) + QVAULT::getGP_output getGP(uint32 proposalId) const { - EXPECT_EQ(unbannedAddress1, newUnbannedAddress); - EXPECT_EQ(unbannedAddress2, newUnbannedAddress); - EXPECT_EQ(unbannedAddress3, newUnbannedAddress); + QVAULT::getGP_input input; + QVAULT::getGP_output output; + + input.proposalId = proposalId; + + callFunction(QVAULT_CONTRACT_INDEX, 3, input, output); + return output; } - void saveUnbannedAddressChecker(const id& unbannedAddress) + QVAULT::getQCP_output getQCP(uint32 proposalId) const { - for(uint32 i = 0 ; i < numberOfBannedAddress; i++) - { - EXPECT_NE(unbannedAddress, bannedAddress.get(i)); - } - } + QVAULT::getQCP_input input; + QVAULT::getQCP_output output; + + input.proposalId = proposalId; - void getDataChecker(const getData_output& output) - { - EXPECT_EQ(output.authAddress1, authAddress1); - EXPECT_EQ(output.authAddress2, authAddress2); - EXPECT_EQ(output.authAddress3, authAddress3); - EXPECT_EQ(output.reinvestingAddress, reinvestingAddress); - EXPECT_EQ(output.shareholderDividend, shareholderDividend); - EXPECT_EQ(output.devPermille, devPermille); - EXPECT_EQ(output.QCAPHolderPermille, QCAPHolderPermille); - EXPECT_EQ(output.reinvestingPermille, reinvestingPermille); - EXPECT_EQ(output.adminAddress, adminAddress); - EXPECT_EQ(output.newAuthAddress1, newAuthAddress1); - EXPECT_EQ(output.newAuthAddress2, newAuthAddress2); - EXPECT_EQ(output.newAuthAddress3, newAuthAddress3); - EXPECT_EQ(output.newAdminAddress1, newAdminAddress1); - EXPECT_EQ(output.newAdminAddress2, newAdminAddress2); - EXPECT_EQ(output.newAdminAddress3, newAdminAddress3); - EXPECT_EQ(output.newReinvestingAddress1, newReinvestingAddress1); - EXPECT_EQ(output.newReinvestingAddress2, newReinvestingAddress2); - EXPECT_EQ(output.newReinvestingAddress3, newReinvestingAddress3); - EXPECT_EQ(output.numberOfBannedAddress, numberOfBannedAddress); - EXPECT_EQ(output.bannedAddress1, bannedAddress1); - EXPECT_EQ(output.bannedAddress2, bannedAddress2); - EXPECT_EQ(output.bannedAddress3, bannedAddress3); - EXPECT_EQ(output.unbannedAddress1, unbannedAddress1); - EXPECT_EQ(output.unbannedAddress2, unbannedAddress2); - EXPECT_EQ(output.unbannedAddress3, unbannedAddress3); + callFunction(QVAULT_CONTRACT_INDEX, 4, input, output); + return output; } -}; -class ContractTestingQvault : protected ContractTesting -{ -public: - ContractTestingQvault() + QVAULT::getIPOP_output getIPOP(uint32 proposalId) const { - initEmptySpectrum(); - initEmptyUniverse(); - INIT_CONTRACT(QVAULT); - callSystemProcedure(QVAULT_CONTRACT_INDEX, INITIALIZE); - INIT_CONTRACT(QX); - callSystemProcedure(QX_CONTRACT_INDEX, INITIALIZE); + QVAULT::getIPOP_input input; + QVAULT::getIPOP_output output; + + input.proposalId = proposalId; + + callFunction(QVAULT_CONTRACT_INDEX, 5, input, output); + return output; } - QVAULTChecker* getState() + QVAULT::getQEarnP_output getQEarnP(uint32 proposalId) const { - return (QVAULTChecker*)contractStates[QVAULT_CONTRACT_INDEX]; + QVAULT::getQEarnP_input input; + QVAULT::getQEarnP_output output; + + input.proposalId = proposalId; + + callFunction(QVAULT_CONTRACT_INDEX, 6, input, output); + return output; } - void endEpoch(bool expectSuccess = true) + QVAULT::getFundP_output getFundP(uint32 proposalId) const { - callSystemProcedure(QVAULT_CONTRACT_INDEX, END_EPOCH, expectSuccess); + QVAULT::getFundP_input input; + QVAULT::getFundP_output output; + + input.proposalId = proposalId; + + callFunction(QVAULT_CONTRACT_INDEX, 7, input, output); + return output; } - QVAULT::getData_output getData() const + QVAULT::getMKTP_output getMKTP(uint32 proposalId) const { - QVAULT::getData_input input; - QVAULT::getData_output output; + QVAULT::getMKTP_input input; + QVAULT::getMKTP_output output; - callFunction(QVAULT_CONTRACT_INDEX, 1, input, output); + input.proposalId = proposalId; + + callFunction(QVAULT_CONTRACT_INDEX, 8, input, output); return output; } - void submitAuthAddress(const id& authAddress, const id& newAuthAddress) + QVAULT::getAlloP_output getAlloP(uint32 proposalId) const { - QVAULT::submitAuthAddress_input input; - QVAULT::submitAuthAddress_output output; + QVAULT::getAlloP_input input; + QVAULT::getAlloP_output output; - input.newAddress = newAuthAddress; + input.proposalId = proposalId; - invokeUserProcedure(QVAULT_CONTRACT_INDEX, 1, input, output, authAddress, 0); + callFunction(QVAULT_CONTRACT_INDEX, 9, input, output); + return output; } - void changeAuthAddress(const id& authAddress, uint32 numberOfChangedAddress) + QVAULT::getIdentitiesHvVtPw_output getIdentitiesHvVtPw(uint32 offset, uint32 count) const { - QVAULT::changeAuthAddress_input input{numberOfChangedAddress}; - QVAULT::changeAuthAddress_output output; + QVAULT::getIdentitiesHvVtPw_input input; + QVAULT::getIdentitiesHvVtPw_output output; + + input.count = count; + input.offset = offset; - invokeUserProcedure(QVAULT_CONTRACT_INDEX, 2, input, output, authAddress, 0); + callFunction(QVAULT_CONTRACT_INDEX, 11, input, output); + return output; } - void submitDistributionPermille(const id& authAddress, uint32 newQCAPHolderPermille, uint32 newReinvestingPermille, uint32 newDevPermille) + QVAULT::ppCreationPower_output ppCreationPower(id address) const { - QVAULT::submitDistributionPermille_input input; - QVAULT::submitDistributionPermille_output output; + QVAULT::ppCreationPower_input input; + QVAULT::ppCreationPower_output output; - input.newDevPermille = newDevPermille; - input.newQCAPHolderPermille = newQCAPHolderPermille; - input.newReinvestingPermille = newReinvestingPermille; + input.address = address; - invokeUserProcedure(QVAULT_CONTRACT_INDEX, 3, input, output, authAddress, 0); + callFunction(QVAULT_CONTRACT_INDEX, 12, input, output); + return output; } - void changeDistributionPermille(const id& authAddress, uint32 newQCAPHolderPermille, uint32 newReinvestingPermille, uint32 newDevPermille) + QVAULT::getQcapBurntAmountInLastEpoches_output getQcapBurntAmountInLastEpoches(uint32 numberOfLastEpoches) const { - QVAULT::changeDistributionPermille_input input; - QVAULT::changeDistributionPermille_output output; + QVAULT::getQcapBurntAmountInLastEpoches_input input; + QVAULT::getQcapBurntAmountInLastEpoches_output output; - input.newDevPermille = newDevPermille; - input.newQCAPHolderPermille = newQCAPHolderPermille; - input.newReinvestingPermille = newReinvestingPermille; + input.numberOfLastEpoches = numberOfLastEpoches; - invokeUserProcedure(QVAULT_CONTRACT_INDEX, 4, input, output, authAddress, 0); + callFunction(QVAULT_CONTRACT_INDEX, 13, input, output); + return output; } - void submitReinvestingAddress(const id& authAddress, const id& newReinvestingAddress) + QVAULT::getAmountToBeSoldPerYear_output getAmountToBeSoldPerYear(uint32 year) const { - QVAULT::submitReinvestingAddress_input input; - QVAULT::submitReinvestingAddress_output output; + QVAULT::getAmountToBeSoldPerYear_input input; + QVAULT::getAmountToBeSoldPerYear_output output; - input.newAddress = newReinvestingAddress; + input.year = year; - invokeUserProcedure(QVAULT_CONTRACT_INDEX, 5, input, output, authAddress, 0); + callFunction(QVAULT_CONTRACT_INDEX, 14, input, output); + return output; } - void changeReinvestingAddress(const id& authAddress, const id& newReinvestingAddress) + QVAULT::getTotalRevenueInQcap_output getTotalRevenueInQcap() const { - QVAULT::changeReinvestingAddress_input input; - QVAULT::changeReinvestingAddress_output output; - - input.newAddress = newReinvestingAddress; + QVAULT::getTotalRevenueInQcap_input input; + QVAULT::getTotalRevenueInQcap_output output; - invokeUserProcedure(QVAULT_CONTRACT_INDEX, 6, input, output, authAddress, 0); + callFunction(QVAULT_CONTRACT_INDEX, 15, input, output); + return output; } - void submitAdminAddress(const id& authAddress, const id& newAdminAddress) + QVAULT::getRevenueInQcapPerEpoch_output getRevenueInQcapPerEpoch(uint32 epoch) const { - QVAULT::submitAdminAddress_input input; - QVAULT::submitAdminAddress_output output; + QVAULT::getRevenueInQcapPerEpoch_input input; + QVAULT::getRevenueInQcapPerEpoch_output output; - input.newAddress = newAdminAddress; + input.epoch = epoch; - invokeUserProcedure(QVAULT_CONTRACT_INDEX, 7, input, output, authAddress, 0); + callFunction(QVAULT_CONTRACT_INDEX, 16, input, output); + return output; } - void changeAdminAddress(const id& authAddress, const id& newAdminAddress) + QVAULT::getRevenuePerShare_output getRevenuePerShare(uint32 contractIndex) const { - QVAULT::changeAdminAddress_input input; - QVAULT::changeAdminAddress_output output; + QVAULT::getRevenuePerShare_input input; + QVAULT::getRevenuePerShare_output output; - input.newAddress = newAdminAddress; + input.contractIndex = contractIndex; - invokeUserProcedure(QVAULT_CONTRACT_INDEX, 8, input, output, authAddress, 0); + callFunction(QVAULT_CONTRACT_INDEX, 17, input, output); + return output; } - void submitBannedAddress(const id& authAddress, const id& bannedAddress) + QVAULT::getAmountOfShareQvaultHold_output getAmountOfShareQvaultHold(Asset assetInfo) const { - QVAULT::submitBannedAddress_input input; - QVAULT::submitBannedAddress_output output; + QVAULT::getAmountOfShareQvaultHold_input input; + QVAULT::getAmountOfShareQvaultHold_output output; + + input.assetInfo = assetInfo; + + callFunction(QVAULT_CONTRACT_INDEX, 18, input, output); + return output; + } - input.bannedAddress = bannedAddress; + QVAULT::getNumberOfHolderAndAvgAm_output getNumberOfHolderAndAvgAm() const + { + QVAULT::getNumberOfHolderAndAvgAm_input input; + QVAULT::getNumberOfHolderAndAvgAm_output output; - invokeUserProcedure(QVAULT_CONTRACT_INDEX, 9, input, output, authAddress, 0); + callFunction(QVAULT_CONTRACT_INDEX, 19, input, output); + return output; } - void saveBannedAddress(const id& authAddress, const id& bannedAddress) + QVAULT::getAmountForQearnInUpcomingEpoch_output getAmountForQearnInUpcomingEpoch(uint32 epoch) const { - QVAULT::saveBannedAddress_input input; - QVAULT::saveBannedAddress_output output; + QVAULT::getAmountForQearnInUpcomingEpoch_input input; + QVAULT::getAmountForQearnInUpcomingEpoch_output output; - input.bannedAddress = bannedAddress; + input.epoch = epoch; - invokeUserProcedure(QVAULT_CONTRACT_INDEX, 10, input, output, authAddress, 0); + callFunction(QVAULT_CONTRACT_INDEX, 20, input, output); + return output; } - void submitUnbannedAddress(const id& authAddress, const id& unbannedAddress) + uint32 stake(const id& address, uint32 amount) { - QVAULT::submitUnbannedAddress_input input; - QVAULT::submitUnbannedAddress_output output; + QVAULT::stake_input input; + QVAULT::stake_output output; + + input.amount = amount; - input.unbannedAddress = unbannedAddress; + invokeUserProcedure(QVAULT_CONTRACT_INDEX, 1, input, output, address, 0); - invokeUserProcedure(QVAULT_CONTRACT_INDEX, 11, input, output, authAddress, 0); + return output.returnCode; } - void saveUnbannedAddress(const id& authAddress, const id& unbannedAddress) + uint32 unStake(const id& address, uint32 amount) { - QVAULT::unblockBannedAddress_input input; - QVAULT::unblockBannedAddress_output output; + QVAULT::unStake_input input{amount}; + QVAULT::unStake_output output; - input.unbannedAddress = unbannedAddress; + invokeUserProcedure(QVAULT_CONTRACT_INDEX, 2, input, output, address, 0); - invokeUserProcedure(QVAULT_CONTRACT_INDEX, 12, input, output, authAddress, 0); + return output.returnCode; } - sint64 issueAsset(const id& issuer, uint64 assetName, sint64 numberOfShares, uint64 unitOfMeasurement, sint8 numberOfDecimalPlaces) + uint32 submitGP(const id& address) { - QX::IssueAsset_input input{ assetName, numberOfShares, unitOfMeasurement, numberOfDecimalPlaces }; - QX::IssueAsset_output output; - invokeUserProcedure(QX_CONTRACT_INDEX, 1, input, output, issuer, QVAULT_ISSUE_ASSET_FEE); - return output.issuedNumberOfShares; + QVAULT::submitGP_input input; + QVAULT::submitGP_output output; + + invokeUserProcedure(QVAULT_CONTRACT_INDEX, 3, input, output, address, QVAULT_PROPOSAL_FEE); + + return output.returnCode; } - sint64 TransferShareOwnershipAndPossession(const id& issuer, uint64 assetName, sint64 numberOfShares, id newOwnerAndPossesor) + uint32 submitQCP(const id& address, uint32 newQuorumPercent) { - QX::TransferShareOwnershipAndPossession_input input; - QX::TransferShareOwnershipAndPossession_output output; + QVAULT::submitQCP_input input; + QVAULT::submitQCP_output output; - input.assetName = assetName; - input.issuer = issuer; - input.newOwnerAndPossessor = newOwnerAndPossesor; - input.numberOfShares = numberOfShares; + input.newQuorumPercent = newQuorumPercent; - invokeUserProcedure(QX_CONTRACT_INDEX, 2, input, output, issuer, QVAULT_TOKEN_TRANSFER_FEE); + invokeUserProcedure(QVAULT_CONTRACT_INDEX, 4, input, output, address, QVAULT_PROPOSAL_FEE); - return output.transferredNumberOfShares; + return output.returnCode; } -}; -TEST(ContractQvault, END_EPOCH) -{ - ContractTestingQvault qvault; + uint32 submitIPOP(const id& address, uint32 ipoContractIndex) + { + QVAULT::submitIPOP_input input; + QVAULT::submitIPOP_output output; - id issuer = QVAULT_QCAP_ISSUER; - uint64 assetName = assetNameFromString("QCAP"); - sint64 numberOfShares = QVAULT_QCAP_MAX_SUPPLY; + input.ipoContractIndex = ipoContractIndex; + invokeUserProcedure(QVAULT_CONTRACT_INDEX, 5, input, output, address, QVAULT_PROPOSAL_FEE); - increaseEnergy(issuer, QVAULT_ISSUE_ASSET_FEE); - EXPECT_EQ(qvault.issueAsset(issuer, assetName, numberOfShares, 0, 0), numberOfShares); + return output.returnCode; + } - uint64 currentAmount = QVAULT_QCAP_MAX_SUPPLY; - uint64 numberOfHolder = 0; - bool flag = 0; - auto QCAPHolders = getRandomUsers(QVAULT_QCAP_MAX_HOLDERS, QVAULT_QCAP_MAX_HOLDERS); + uint32 submitQEarnP(const id& address, uint64 amountPerEpoch, uint32 numberOfEpoch) + { + QVAULT::submitQEarnP_input input; + QVAULT::submitQEarnP_output output; - std::map transferChecker; + input.amountPerEpoch = amountPerEpoch; + input.numberOfEpoch = numberOfEpoch; - /* - sending the QCAP token to bannedAddresses - */ - increaseEnergy(issuer, QVAULT_TOKEN_TRANSFER_FEE); - increaseEnergy(QVAULT_initialBannedAddress1, 1); - currentAmount -= qvault.TransferShareOwnershipAndPossession(issuer, assetName, random(0, QVAULT_BIG_AMOUNT_QCAP_TRANSFER), QVAULT_initialBannedAddress1); + invokeUserProcedure(QVAULT_CONTRACT_INDEX, 6, input, output, address, QVAULT_PROPOSAL_FEE); - increaseEnergy(issuer, QVAULT_TOKEN_TRANSFER_FEE); - increaseEnergy(QVAULT_initialBannedAddress2, 1); - currentAmount -= qvault.TransferShareOwnershipAndPossession(issuer, assetName, random(0, QVAULT_BIG_AMOUNT_QCAP_TRANSFER), QVAULT_initialBannedAddress2); - /* - this while statement will distribute the QCAP token to holders. - */ - while(1) + return output.returnCode; + } + + uint32 submitFundP(const id& address, uint32 amountOfQcap, uint64 priceOfOneQcap) { - uint64 amountOfQCAPTransfer; - if(flag) - { - amountOfQCAPTransfer = random(0, QVAULT_BIG_AMOUNT_QCAP_TRANSFER); - } - else - { - amountOfQCAPTransfer = random(0, QVAULT_SMALL_AMOUNT_QCAP_TRANSFER); - } - if(currentAmount < amountOfQCAPTransfer || numberOfHolder == QCAPHolders.size()) - { - break; - } + QVAULT::submitFundP_input input; + QVAULT::submitFundP_output output; - if(transferChecker[QCAPHolders[numberOfHolder]]) - { - QCAPHolders.erase(QCAPHolders.begin() + numberOfHolder); - continue; - } - transferChecker[QCAPHolders[numberOfHolder]] = 1; - currentAmount -= amountOfQCAPTransfer; - increaseEnergy(issuer, QVAULT_TOKEN_TRANSFER_FEE); - increaseEnergy(QCAPHolders[numberOfHolder], 1); - qvault.getState()->balanceChecker(QCAPHolders[numberOfHolder]); - EXPECT_EQ(qvault.TransferShareOwnershipAndPossession(issuer, assetName, amountOfQCAPTransfer, QCAPHolders[numberOfHolder]), amountOfQCAPTransfer); - numberOfHolder++; - } - - uint64 revenue = random(QVAULT_MIN_REVENUE, QVAULT_MAX_REVENUE); - increaseEnergy(QVAULT_CONTRACT_ID, revenue); - qvault.endEpoch(); - qvault.getState()->endEpochChecker(revenue, QCAPHolders); -} + input.amountOfQcap = amountOfQcap; + input.priceOfOneQcap = priceOfOneQcap; -TEST(ContractQvault, submitAuthAddress) -{ - ContractTestingQvault qvault; + invokeUserProcedure(QVAULT_CONTRACT_INDEX, 7, input, output, address, QVAULT_PROPOSAL_FEE); - auto randomAddresses = getRandomUsers(QVAULT_QCAP_MAX_HOLDERS, QVAULT_QCAP_MAX_HOLDERS); + return output.returnCode; + } - for (const auto& user : randomAddresses) + uint32 submitMKTP(const id& address, uint32 amountOfQcap, uint64 amountOfQubic, uint64 shareName, uint32 indexOfShare, uint32 amountOfShare) { - // make sure that user exists in spectrum - increaseEnergy(user, 1); + QVAULT::submitMKTP_input input; + QVAULT::submitMKTP_output output; - // checking to change the auth address using the non-authAddress - qvault.submitAuthAddress(user, user); - qvault.getState()->submitAuthAddressChecker(); - } + input.amountOfQcap = amountOfQcap; + input.amountOfQubic = amountOfQubic; + input.amountOfShare = amountOfShare; + input.indexOfShare = indexOfShare; + input.shareName = shareName; - // make sure that user exists in spectrum - increaseEnergy(QVAULT_authAddress1, 1); - increaseEnergy(QVAULT_authAddress2, 1); - increaseEnergy(QVAULT_authAddress3, 1); + invokeUserProcedure(QVAULT_CONTRACT_INDEX, 8, input, output, address, QVAULT_PROPOSAL_FEE); - // checking to change the auth address using the exact authAddresss - qvault.submitAuthAddress(QVAULT_authAddress1, randomAddresses[0]); - qvault.submitAuthAddress(QVAULT_authAddress2, randomAddresses[0]); - qvault.submitAuthAddress(QVAULT_authAddress3, randomAddresses[0]); - qvault.getState()->submitAuthAddressWithExactAuthId(randomAddresses[0]); -} + return output.returnCode; + } -TEST(ContractQvault, changeAuthAddress) -{ - ContractTestingQvault qvault; - - auto randomAddresses = getRandomUsers(QVAULT_QCAP_MAX_HOLDERS, QVAULT_QCAP_MAX_HOLDERS); - - // make sure that user exists in spectrum - increaseEnergy(QVAULT_authAddress1, 1); - increaseEnergy(QVAULT_authAddress2, 1); - increaseEnergy(QVAULT_authAddress3, 1); - - // checking to change the authAddress3 with exact process - qvault.submitAuthAddress(QVAULT_authAddress1, randomAddresses[0]); - qvault.submitAuthAddress(QVAULT_authAddress2, randomAddresses[0]); - qvault.changeAuthAddress(QVAULT_authAddress1, 3); - qvault.getState()->changeAuthAddressChecker(3, randomAddresses[0]); - - qvault.submitAuthAddress(QVAULT_authAddress1, randomAddresses[1]); - qvault.submitAuthAddress(QVAULT_authAddress2, randomAddresses[1]); - qvault.changeAuthAddress(QVAULT_authAddress2, 3); - qvault.getState()->changeAuthAddressChecker(3, randomAddresses[1]); - - qvault.submitAuthAddress(QVAULT_authAddress1, QVAULT_authAddress3); - qvault.submitAuthAddress(QVAULT_authAddress2, QVAULT_authAddress3); - qvault.changeAuthAddress(QVAULT_authAddress2, 3); - qvault.getState()->changeAuthAddressChecker(3, QVAULT_authAddress3); - - // checking to change the authAddress2 with exact process - qvault.submitAuthAddress(QVAULT_authAddress1, randomAddresses[0]); - qvault.submitAuthAddress(QVAULT_authAddress3, randomAddresses[0]); - qvault.changeAuthAddress(QVAULT_authAddress1, 2); - qvault.getState()->changeAuthAddressChecker(2, randomAddresses[0]); - - qvault.submitAuthAddress(QVAULT_authAddress1, randomAddresses[1]); - qvault.submitAuthAddress(QVAULT_authAddress3, randomAddresses[1]); - qvault.changeAuthAddress(QVAULT_authAddress3, 2); - qvault.getState()->changeAuthAddressChecker(2, randomAddresses[1]); - - qvault.submitAuthAddress(QVAULT_authAddress1, QVAULT_authAddress2); - qvault.submitAuthAddress(QVAULT_authAddress3, QVAULT_authAddress2); - qvault.changeAuthAddress(QVAULT_authAddress3, 2); - qvault.getState()->changeAuthAddressChecker(2, QVAULT_authAddress2); - - // checking to change the authAddress1 with exact process - qvault.submitAuthAddress(QVAULT_authAddress2, randomAddresses[0]); - qvault.submitAuthAddress(QVAULT_authAddress3, randomAddresses[0]); - qvault.changeAuthAddress(QVAULT_authAddress2, 1); - qvault.getState()->changeAuthAddressChecker(1, randomAddresses[0]); - - qvault.submitAuthAddress(QVAULT_authAddress2, randomAddresses[1]); - qvault.submitAuthAddress(QVAULT_authAddress3, randomAddresses[1]); - qvault.changeAuthAddress(QVAULT_authAddress3, 1); - qvault.getState()->changeAuthAddressChecker(1, randomAddresses[1]); - - qvault.submitAuthAddress(QVAULT_authAddress2, QVAULT_authAddress1); - qvault.submitAuthAddress(QVAULT_authAddress3, QVAULT_authAddress1); - qvault.changeAuthAddress(QVAULT_authAddress3, 1); - qvault.getState()->changeAuthAddressChecker(1, QVAULT_authAddress1); -} + uint32 submitAlloP(const id& address, uint32 reinvested, uint32 burn, uint32 distribute) + { + QVAULT::submitAlloP_input input; + QVAULT::submitAlloP_output output; -TEST(ContractQvault, submitDistributionPermille) -{ - ContractTestingQvault qvault; - - // make sure that user exists in spectrum - increaseEnergy(QVAULT_authAddress1, 1); - increaseEnergy(QVAULT_authAddress2, 1); - increaseEnergy(QVAULT_authAddress3, 1); - - // checking to change the Permille - qvault.submitDistributionPermille(QVAULT_authAddress1, 500, 400, 70); - qvault.submitDistributionPermille(QVAULT_authAddress2, 500, 400, 70); - qvault.submitDistributionPermille(QVAULT_authAddress3, 500, 400, 70); - qvault.getState()->submitDistributionPermilleChecker(500, 400, 70); -} + input.reinvested = reinvested; + input.burn = burn ; + input.distribute = distribute; -TEST(ContractQvault, changeDistributionPermille) -{ - ContractTestingQvault qvault; - - // make sure that user exists in spectrum - increaseEnergy(QVAULT_authAddress1, 1); - increaseEnergy(QVAULT_authAddress2, 1); - increaseEnergy(QVAULT_authAddress3, 1); - - // checking to change the Permille - qvault.submitDistributionPermille(QVAULT_authAddress1, 500, 400, 70); - qvault.submitDistributionPermille(QVAULT_authAddress2, 500, 400, 70); - qvault.submitDistributionPermille(QVAULT_authAddress3, 500, 400, 70); - qvault.changeDistributionPermille(QVAULT_authAddress1, 500, 400, 70); - qvault.getState()->changeDistributionPermilleChecker(500, 400, 70); - - qvault.submitDistributionPermille(QVAULT_authAddress1, 500, 400, 70); - qvault.submitDistributionPermille(QVAULT_authAddress2, 500, 400, 70); - qvault.submitDistributionPermille(QVAULT_authAddress3, 500, 400, 70); - qvault.changeDistributionPermille(QVAULT_authAddress2, 500, 400, 70); - qvault.getState()->changeDistributionPermilleChecker(500, 400, 70); - - qvault.submitDistributionPermille(QVAULT_authAddress1, 500, 400, 70); - qvault.submitDistributionPermille(QVAULT_authAddress2, 500, 400, 70); - qvault.submitDistributionPermille(QVAULT_authAddress3, 500, 400, 70); - qvault.changeDistributionPermille(QVAULT_authAddress3, 500, 400, 70); - qvault.getState()->changeDistributionPermilleChecker(500, 400, 70); -} + invokeUserProcedure(QVAULT_CONTRACT_INDEX, 9, input, output, address, QVAULT_PROPOSAL_FEE); -TEST(ContractQvault, submitReinvestingAddress) -{ - ContractTestingQvault qvault; + return output.returnCode; + } - auto randomAddresses = getRandomUsers(QVAULT_QCAP_MAX_HOLDERS, QVAULT_QCAP_MAX_HOLDERS); + uint32 voteInProposal(const id& address, uint64 priceOfIPO, uint32 proposalType, uint32 proposalId, bit yes) + { + QVAULT::voteInProposal_input input; + QVAULT::voteInProposal_output output; - // make sure that user exists in spectrum - increaseEnergy(QVAULT_authAddress1, 1); - increaseEnergy(QVAULT_authAddress2, 1); - increaseEnergy(QVAULT_authAddress3, 1); + input.priceOfIPO = priceOfIPO; + input.proposalType = proposalType; + input.proposalId = proposalId; + input.yes = yes; - // checking to change the reinvestingAddress - qvault.submitReinvestingAddress(QVAULT_authAddress1, randomAddresses[0]); - qvault.submitReinvestingAddress(QVAULT_authAddress2, randomAddresses[0]); - qvault.submitReinvestingAddress(QVAULT_authAddress3, randomAddresses[0]); - qvault.getState()->submitReinvestingAddressChecker(randomAddresses[0]); -} + invokeUserProcedure(QVAULT_CONTRACT_INDEX, 11, input, output, address, 0); -TEST(ContractQvault, changeReinvestingAddress) -{ - ContractTestingQvault qvault; - - auto randomAddresses = getRandomUsers(QVAULT_QCAP_MAX_HOLDERS, QVAULT_QCAP_MAX_HOLDERS); - - // make sure that user exists in spectrum - increaseEnergy(QVAULT_authAddress1, 1); - increaseEnergy(QVAULT_authAddress2, 1); - increaseEnergy(QVAULT_authAddress3, 1); - - // checking to change the reinvestingAddress - qvault.submitReinvestingAddress(QVAULT_authAddress1, randomAddresses[0]); - qvault.submitReinvestingAddress(QVAULT_authAddress2, randomAddresses[0]); - qvault.submitReinvestingAddress(QVAULT_authAddress3, randomAddresses[0]); - qvault.changeReinvestingAddress(QVAULT_authAddress1, randomAddresses[0]); - qvault.getState()->changeReinvestingAddressChecker(randomAddresses[0]); - - qvault.submitReinvestingAddress(QVAULT_authAddress1, randomAddresses[1]); - qvault.submitReinvestingAddress(QVAULT_authAddress2, randomAddresses[1]); - qvault.submitReinvestingAddress(QVAULT_authAddress3, randomAddresses[1]); - qvault.changeReinvestingAddress(QVAULT_authAddress2, randomAddresses[1]); - qvault.getState()->changeReinvestingAddressChecker(randomAddresses[1]); - - qvault.submitReinvestingAddress(QVAULT_authAddress1, randomAddresses[2]); - qvault.submitReinvestingAddress(QVAULT_authAddress2, randomAddresses[2]); - qvault.submitReinvestingAddress(QVAULT_authAddress3, randomAddresses[2]); - qvault.changeReinvestingAddress(QVAULT_authAddress3, randomAddresses[2]); - qvault.getState()->changeReinvestingAddressChecker(randomAddresses[2]); -} + return output.returnCode; + } -TEST(ContractQvault, submitAdminAddress) -{ - ContractTestingQvault qvault; + uint32 buyQcap(const id& address, uint32 amount, uint64 fund) + { + QVAULT::buyQcap_input input; + QVAULT::buyQcap_output output; - auto randomAddresses = getRandomUsers(QVAULT_QCAP_MAX_HOLDERS, QVAULT_QCAP_MAX_HOLDERS); + input.amount = amount; - // make sure that user exists in spectrum - increaseEnergy(QVAULT_authAddress1, 1); - increaseEnergy(QVAULT_authAddress2, 1); - increaseEnergy(QVAULT_authAddress3, 1); + invokeUserProcedure(QVAULT_CONTRACT_INDEX, 12, input, output, address, fund); - // checking to change the adminAddress - qvault.submitAdminAddress(QVAULT_authAddress1, randomAddresses[0]); - qvault.submitAdminAddress(QVAULT_authAddress2, randomAddresses[0]); - qvault.submitAdminAddress(QVAULT_authAddress3, randomAddresses[0]); - qvault.getState()->submitAdminAddressChecker(randomAddresses[0]); -} + return output.returnCode; + } -TEST(ContractQvault, changeAdminAddress) -{ - ContractTestingQvault qvault; - - auto randomAddresses = getRandomUsers(QVAULT_QCAP_MAX_HOLDERS, QVAULT_QCAP_MAX_HOLDERS); - - // make sure that user exists in spectrum - increaseEnergy(QVAULT_authAddress1, 1); - increaseEnergy(QVAULT_authAddress2, 1); - increaseEnergy(QVAULT_authAddress3, 1); - - // checking to change the adminAddress - qvault.submitAdminAddress(QVAULT_authAddress1, randomAddresses[0]); - qvault.submitAdminAddress(QVAULT_authAddress2, randomAddresses[0]); - qvault.submitAdminAddress(QVAULT_authAddress3, randomAddresses[0]); - qvault.changeAdminAddress(QVAULT_authAddress1, randomAddresses[0]); - qvault.getState()->changeAdminAddressChecker(randomAddresses[0]); - - qvault.submitAdminAddress(QVAULT_authAddress1, randomAddresses[1]); - qvault.submitAdminAddress(QVAULT_authAddress2, randomAddresses[1]); - qvault.submitAdminAddress(QVAULT_authAddress3, randomAddresses[1]); - qvault.changeAdminAddress(QVAULT_authAddress2, randomAddresses[1]); - qvault.getState()->changeAdminAddressChecker(randomAddresses[1]); - - qvault.submitAdminAddress(QVAULT_authAddress1, randomAddresses[2]); - qvault.submitAdminAddress(QVAULT_authAddress2, randomAddresses[2]); - qvault.submitAdminAddress(QVAULT_authAddress3, randomAddresses[2]); - qvault.changeAdminAddress(QVAULT_authAddress3, randomAddresses[2]); - qvault.getState()->changeAdminAddressChecker(randomAddresses[2]); -} + uint64 TransferShareManagementRights(const id& address, Asset asset, sint64 numberOfShares, uint32 newManagingContractIndex) + { + QVAULT::TransferShareManagementRights_input input; + QVAULT::TransferShareManagementRights_output output; -TEST(ContractQvault, submitBannedAddress) -{ - ContractTestingQvault qvault; + input.asset.assetName = QVAULT_QCAP_ASSETNAME; + input.asset.issuer = QVAULT_QCAP_ISSUER; + input.numberOfShares = numberOfShares; + input.newManagingContractIndex = newManagingContractIndex; - auto randomAddresses = getRandomUsers(QVAULT_QCAP_MAX_HOLDERS, QVAULT_QCAP_MAX_HOLDERS); + invokeUserProcedure(QVAULT_CONTRACT_INDEX, 13, input, output, address, 100); - // make sure that user exists in spectrum - increaseEnergy(QVAULT_authAddress1, 1); - increaseEnergy(QVAULT_authAddress2, 1); - increaseEnergy(QVAULT_authAddress3, 1); + return output.transferredNumberOfShares; + } - // checking to submit the bannedAddress - qvault.submitBannedAddress(QVAULT_authAddress1, randomAddresses[0]); - qvault.submitBannedAddress(QVAULT_authAddress2, randomAddresses[0]); - qvault.submitBannedAddress(QVAULT_authAddress3, randomAddresses[0]); - qvault.getState()->submitBannedAddressChecker(randomAddresses[0]); -} + sint64 QXTransferShareManagementRights(const id& issuer, uint64 assetName, uint32 newManagingContractIndex, sint64 numberOfShares, id currentOwner) + { + QX::TransferShareManagementRights_input input; + QX::TransferShareManagementRights_output output; -TEST(ContractQvault, saveBannedAddress) -{ - ContractTestingQvault qvault; - - auto randomAddresses = getRandomUsers(QVAULT_QCAP_MAX_HOLDERS, QVAULT_QCAP_MAX_HOLDERS); - - // make sure that user exists in spectrum - increaseEnergy(QVAULT_authAddress1, 1); - increaseEnergy(QVAULT_authAddress2, 1); - increaseEnergy(QVAULT_authAddress3, 1); - - // checking to save the bannedAddress - qvault.submitBannedAddress(QVAULT_authAddress1, randomAddresses[0]); - qvault.submitBannedAddress(QVAULT_authAddress2, randomAddresses[0]); - qvault.submitBannedAddress(QVAULT_authAddress3, randomAddresses[0]); - qvault.saveBannedAddress(QVAULT_authAddress1, randomAddresses[0]); - qvault.getState()->saveBannedAddressChecker(randomAddresses[0]); - - qvault.submitBannedAddress(QVAULT_authAddress1, randomAddresses[1]); - qvault.submitBannedAddress(QVAULT_authAddress2, randomAddresses[1]); - qvault.submitBannedAddress(QVAULT_authAddress3, randomAddresses[1]); - qvault.saveBannedAddress(QVAULT_authAddress2, randomAddresses[1]); - qvault.getState()->saveBannedAddressChecker(randomAddresses[1]); - - qvault.submitBannedAddress(QVAULT_authAddress1, randomAddresses[2]); - qvault.submitBannedAddress(QVAULT_authAddress2, randomAddresses[2]); - qvault.submitBannedAddress(QVAULT_authAddress3, randomAddresses[2]); - qvault.saveBannedAddress(QVAULT_authAddress3, randomAddresses[2]); - qvault.getState()->saveBannedAddressChecker(randomAddresses[2]); -} + input.asset.assetName = assetName; + input.asset.issuer = issuer; + input.newManagingContractIndex = newManagingContractIndex; + input.numberOfShares = numberOfShares; -TEST(ContractQvault, submitUnbannedAddress) -{ - ContractTestingQvault qvault; + invokeUserProcedure(QX_CONTRACT_INDEX, 9, input, output, currentOwner, 0); - auto randomAddresses = getRandomUsers(QVAULT_QCAP_MAX_HOLDERS, QVAULT_QCAP_MAX_HOLDERS); + return output.transferredNumberOfShares; + } - // make sure that user exists in spectrum - increaseEnergy(QVAULT_authAddress1, 1); - increaseEnergy(QVAULT_authAddress2, 1); - increaseEnergy(QVAULT_authAddress3, 1); + sint64 issueAsset(const id& issuer, uint64 assetName, sint64 numberOfShares, uint64 unitOfMeasurement, sint8 numberOfDecimalPlaces) + { + QX::IssueAsset_input input{ assetName, numberOfShares, unitOfMeasurement, numberOfDecimalPlaces }; + QX::IssueAsset_output output; + invokeUserProcedure(QX_CONTRACT_INDEX, 1, input, output, issuer, QVAULT_ISSUE_ASSET_FEE); + return output.issuedNumberOfShares; + } - // checking to submit unbannedAddress - qvault.submitUnbannedAddress(QVAULT_authAddress1, randomAddresses[0]); - qvault.submitUnbannedAddress(QVAULT_authAddress2, randomAddresses[0]); - qvault.submitUnbannedAddress(QVAULT_authAddress3, randomAddresses[0]); - qvault.getState()->submitUnbannedAddressChecker(randomAddresses[0]); -} + sint64 TransferShareOwnershipAndPossession(const id& issuer, uint64 assetName, sint64 numberOfShares, id oldOwnerAndPossessor, id newOwnerAndPossesor) + { + QX::TransferShareOwnershipAndPossession_input input; + QX::TransferShareOwnershipAndPossession_output output; -TEST(ContractQvault, unblockBannedAddress) -{ - ContractTestingQvault qvault; + input.assetName = assetName; + input.issuer = issuer; + input.newOwnerAndPossessor = newOwnerAndPossesor; + input.numberOfShares = numberOfShares; - auto randomAddresses = getRandomUsers(QVAULT_QCAP_MAX_HOLDERS, QVAULT_QCAP_MAX_HOLDERS); + invokeUserProcedure(QX_CONTRACT_INDEX, 2, input, output, oldOwnerAndPossessor, QVAULT_TOKEN_TRANSFER_FEE); - // make sure that user exists in spectrum - increaseEnergy(QVAULT_authAddress1, 1); - increaseEnergy(QVAULT_authAddress2, 1); - increaseEnergy(QVAULT_authAddress3, 1); + return output.transferredNumberOfShares; + } - for(uint32 i = 0 ; i < 10; i++) + QX::AddToAskOrder_output AddToAskOrder(const id& issuer, uint64 assetName, sint64 price, sint64 numberOfShares, id user) { - qvault.submitBannedAddress(QVAULT_authAddress1, randomAddresses[i]); - qvault.submitBannedAddress(QVAULT_authAddress2, randomAddresses[i]); - qvault.submitBannedAddress(QVAULT_authAddress3, randomAddresses[i]); - qvault.saveBannedAddress(QVAULT_authAddress1, randomAddresses[i]); + QX::AddToAskOrder_input input{ issuer, assetName, price, numberOfShares}; + QX::AddToAskOrder_output output; + invokeUserProcedure(QX_CONTRACT_INDEX, 5, input, output, user, 0); + return output; } - // checking to unblock the bannedAddress - for(uint32 i = 0 ; i < 10; i++) + QX::AssetAskOrders_output AssetAskOrders(const id& issuer, uint64 assetName, uint64 offset) { - qvault.submitUnbannedAddress(QVAULT_authAddress1, randomAddresses[i]); - qvault.submitUnbannedAddress(QVAULT_authAddress2, randomAddresses[i]); - qvault.submitUnbannedAddress(QVAULT_authAddress3, randomAddresses[i]); - qvault.saveUnbannedAddress(QVAULT_authAddress1, randomAddresses[i]); - qvault.getState()->saveUnbannedAddressChecker(randomAddresses[i]); + QX::AssetAskOrders_input input{ issuer, assetName, offset}; + QX::AssetAskOrders_output output; + callFunction(QX_CONTRACT_INDEX, 2, input, output); + return output; } -} +}; -TEST(ContractQvault, getData) + +TEST(TestContractQvault, testingAllProceduresAndFunctions) { - ContractTestingQvault qvault; + system.epoch = 138; + ContractTestingQvault QvaultV2; + + uint64 qcapAssetName = assetNameFromString("QCAP"); + + increaseEnergy(QVAULT_QCAP_ISSUER, QVAULT_ISSUE_ASSET_FEE); + EXPECT_EQ(QvaultV2.issueAsset(QVAULT_QCAP_ISSUER, qcapAssetName, QVAULT_QCAP_MAX_SUPPLY, 0, 0), QVAULT_QCAP_MAX_SUPPLY); + + increaseEnergy(QVAULT_QCAP_ISSUER, QVAULT_TOKEN_TRANSFER_FEE); + EXPECT_EQ(QvaultV2.TransferShareOwnershipAndPossession(QVAULT_QCAP_ISSUER, qcapAssetName, QVAULT_QCAP_MAX_SUPPLY, QVAULT_QCAP_ISSUER, QVAULT_CONTRACT_ID), QVAULT_QCAP_MAX_SUPPLY); + + auto stakers = getRandomUsers(QVAULT_QCAP_MAX_HOLDERS, QVAULT_QCAP_MAX_HOLDERS); + + uint32 maxTransferAmount = QVAULT_QCAP_SOLD_AMOUNT; + for (const auto& user : stakers) + { + uint32 amountTransfer = (uint32)random(1000ULL, 100000ULL); + if(maxTransferAmount < amountTransfer) + { + break; + } + maxTransferAmount -= amountTransfer; + increaseEnergy(QVAULT_CONTRACT_ID, QVAULT_TOKEN_TRANSFER_FEE); + + EXPECT_EQ(QvaultV2.TransferShareOwnershipAndPossession(QVAULT_QCAP_ISSUER, qcapAssetName, amountTransfer, QVAULT_CONTRACT_ID, user), amountTransfer); + + increaseEnergy(user, 1); + EXPECT_EQ(QvaultV2.QXTransferShareManagementRights(QVAULT_QCAP_ISSUER, qcapAssetName, QVAULT_CONTRACT_INDEX, amountTransfer, user), amountTransfer); + EXPECT_EQ(numberOfPossessedShares(qcapAssetName, QVAULT_QCAP_ISSUER, user, user, QVAULT_CONTRACT_INDEX, QVAULT_CONTRACT_INDEX), amountTransfer); + EXPECT_EQ(QvaultV2.stake(user, amountTransfer), 0); + EXPECT_EQ(numberOfPossessedShares(qcapAssetName, QVAULT_QCAP_ISSUER, QVAULT_CONTRACT_ID, QVAULT_CONTRACT_ID, QVAULT_CONTRACT_INDEX, QVAULT_CONTRACT_INDEX), amountTransfer); + EXPECT_EQ(QvaultV2.unStake(user, amountTransfer), 0); + EXPECT_EQ(numberOfPossessedShares(qcapAssetName, QVAULT_QCAP_ISSUER, user, user, QVAULT_CONTRACT_INDEX, QVAULT_CONTRACT_INDEX), amountTransfer); + } + + QvaultV2.TransferShareOwnershipAndPossession(QVAULT_QCAP_ISSUER, qcapAssetName, 10000, QVAULT_CONTRACT_ID, stakers[0]); + EXPECT_EQ(QvaultV2.stake(stakers[0], 10000), 0); + + QvaultV2.endEpoch(); + system.epoch++; + + increaseEnergy(stakers[0], QVAULT_PROPOSAL_FEE); + EXPECT_EQ(QvaultV2.submitGP(stakers[0]), QVAULT_SUCCESS); + EXPECT_EQ(getBalance(QVAULT_CONTRACT_ID), QVAULT_PROPOSAL_FEE); + QvaultV2.getState()->submitGPChecker(0, stakers[0]); + + increaseEnergy(stakers[0], QVAULT_PROPOSAL_FEE); + EXPECT_EQ(QvaultV2.submitQCP(stakers[0], 500), QVAULT_SUCCESS); + EXPECT_EQ(getBalance(QVAULT_CONTRACT_ID), QVAULT_PROPOSAL_FEE * 2); + QvaultV2.getState()->submitQCPChecker(0, stakers[0], 500); + + increaseEnergy(stakers[0], QVAULT_PROPOSAL_FEE); + EXPECT_EQ(QvaultV2.submitIPOP(stakers[0], 15), QVAULT_INSUFFICIENT_FUND); + EXPECT_EQ(getBalance(QVAULT_CONTRACT_ID), QVAULT_PROPOSAL_FEE * 2); + // QvaultV2.getState()->submitIPOPChecker(0, stakers[0], 15); + + increaseEnergy(stakers[0], QVAULT_PROPOSAL_FEE); + EXPECT_EQ(QvaultV2.submitQEarnP(stakers[0], 1000000000, 10), QVAULT_INSUFFICIENT_FUND); + EXPECT_EQ(getBalance(QVAULT_CONTRACT_ID), QVAULT_PROPOSAL_FEE * 2); + // QvaultV2.getState()->submitQEarnPChecker(0, stakers[0], 1000000000, 10); + + updateTime(); + updateQpiTime(); + + setMemory(utcTime, 0); + utcTime.Year = 2025; + utcTime.Month = 1; + utcTime.Day = 3; + utcTime.Hour = 0; + updateQpiTime(); + + increaseEnergy(stakers[0], 10000000); + EXPECT_EQ(QvaultV2.submitFundP(stakers[0], QVAULT_2025MAX_QCAP_SALE_AMOUNT, 100000), QVAULT_OVERFLOW_SALE_AMOUNT); + EXPECT_EQ(getBalance(QVAULT_CONTRACT_ID), QVAULT_PROPOSAL_FEE * 2); + EXPECT_EQ(QvaultV2.submitFundP(stakers[0], QVAULT_2025MAX_QCAP_SALE_AMOUNT - 1909423, 100000), QVAULT_SUCCESS); + EXPECT_EQ(getBalance(QVAULT_CONTRACT_ID), QVAULT_PROPOSAL_FEE * 3); + QvaultV2.getState()->submitFundPChecker(0, stakers[0], QVAULT_2025MAX_QCAP_SALE_AMOUNT - 1909423, 100000); + + setMemory(utcTime, 0); + utcTime.Year = 2026; + utcTime.Month = 1; + utcTime.Day = 3; + utcTime.Hour = 0; + updateQpiTime(); + + increaseEnergy(stakers[0], 10000000); + EXPECT_EQ(QvaultV2.submitFundP(stakers[0], QVAULT_2026MAX_QCAP_SALE_AMOUNT, 100000), QVAULT_OVERFLOW_SALE_AMOUNT); + EXPECT_EQ(getBalance(QVAULT_CONTRACT_ID), QVAULT_PROPOSAL_FEE * 3); + EXPECT_EQ(QvaultV2.submitFundP(stakers[0], QVAULT_2026MAX_QCAP_SALE_AMOUNT - 1909423, 100000), QVAULT_SUCCESS); + EXPECT_EQ(getBalance(QVAULT_CONTRACT_ID), QVAULT_PROPOSAL_FEE * 4); + QvaultV2.getState()->submitFundPChecker(1, stakers[0], QVAULT_2026MAX_QCAP_SALE_AMOUNT - 1909423, 100000); + + setMemory(utcTime, 0); + utcTime.Year = 2027; + utcTime.Month = 1; + utcTime.Day = 3; + utcTime.Hour = 0; + updateQpiTime(); + + increaseEnergy(stakers[0], 10000000); + EXPECT_EQ(QvaultV2.submitFundP(stakers[0], QVAULT_2027MAX_QCAP_SALE_AMOUNT, 100000), QVAULT_OVERFLOW_SALE_AMOUNT); + EXPECT_EQ(getBalance(QVAULT_CONTRACT_ID), QVAULT_PROPOSAL_FEE * 4); + EXPECT_EQ(QvaultV2.submitFundP(stakers[0], QVAULT_2027MAX_QCAP_SALE_AMOUNT - 1909423, 100000), QVAULT_SUCCESS); + EXPECT_EQ(getBalance(QVAULT_CONTRACT_ID), QVAULT_PROPOSAL_FEE * 5); + QvaultV2.getState()->submitFundPChecker(2, stakers[0], QVAULT_2027MAX_QCAP_SALE_AMOUNT - 1909423, 100000); + + setMemory(utcTime, 0); + utcTime.Year = 2028; + utcTime.Month = 1; + utcTime.Day = 3; + utcTime.Hour = 0; + updateQpiTime(); + + increaseEnergy(stakers[0], 10000000); + EXPECT_EQ(QvaultV2.submitFundP(stakers[0], QVAULT_QCAP_MAX_SUPPLY, 100000), QVAULT_OVERFLOW_SALE_AMOUNT); + EXPECT_EQ(getBalance(QVAULT_CONTRACT_ID), QVAULT_PROPOSAL_FEE * 5); + EXPECT_EQ(QvaultV2.submitFundP(stakers[0], QVAULT_QCAP_MAX_SUPPLY - 1909423, 100000), QVAULT_SUCCESS); + EXPECT_EQ(getBalance(QVAULT_CONTRACT_ID), QVAULT_PROPOSAL_FEE * 6); + QvaultV2.getState()->submitFundPChecker(3, stakers[0], QVAULT_QCAP_MAX_SUPPLY - 1909423, 100000); + + increaseEnergy(stakers[0], 10000000); + EXPECT_EQ(QvaultV2.submitMKTP(stakers[0], 10000, 1000000000, assetNameFromString("QX"), 1, 5), QVAULT_NOT_TRANSFERRED_SHARE); + EXPECT_EQ(getBalance(QVAULT_CONTRACT_ID), QVAULT_PROPOSAL_FEE * 6); + // QvaultV2.getState()->submitMKTPChecker(0, stakers[0], 10000, 1000000000, assetNameFromString("QX"), 1, 5); + + increaseEnergy(stakers[0], 10000000); + EXPECT_EQ(QvaultV2.submitAlloP(stakers[0], 450, 120, 400), QVAULT_NOT_IN_TIME); + EXPECT_EQ(getBalance(QVAULT_CONTRACT_ID), QVAULT_PROPOSAL_FEE * 6); + + setMemory(utcTime, 0); + utcTime.Year = 2029; + utcTime.Month = 1; + utcTime.Day = 3; + utcTime.Hour = 0; + updateQpiTime(); + + EXPECT_EQ(QvaultV2.submitAlloP(stakers[0], 450, 120, 400), QVAULT_SUCCESS); + EXPECT_EQ(getBalance(QVAULT_CONTRACT_ID), QVAULT_PROPOSAL_FEE * 7); + + setMemory(utcTime, 0); + utcTime.Year = 2029; + utcTime.Month = 1; + utcTime.Day = 3; + utcTime.Hour = 0; + updateQpiTime(); + + EXPECT_EQ(QvaultV2.submitAlloP(stakers[0], 450, 120, 400), QVAULT_SUCCESS); + EXPECT_EQ(getBalance(QVAULT_CONTRACT_ID), QVAULT_PROPOSAL_FEE * 8); + QvaultV2.getState()->submitAlloPChecker(0, stakers[0], 450, 120, 400); + + EXPECT_EQ(QvaultV2.voteInProposal(stakers[0], 100000000, 1, 0, 1), QVAULT_SUCCESS); + QvaultV2.getState()->voteInProposalChecker(0, 1, 10000, 0); + + EXPECT_EQ(QvaultV2.voteInProposal(stakers[0], 100000000, 2, 0, 1), QVAULT_SUCCESS); + QvaultV2.getState()->voteInProposalChecker(0, 2, 10000, 0); + + EXPECT_EQ(QvaultV2.voteInProposal(stakers[0], 100000000, 5, 0, 1), QVAULT_SUCCESS); + QvaultV2.getState()->voteInProposalChecker(0, 5, 10000, 0); + + EXPECT_EQ(QvaultV2.voteInProposal(stakers[0], 100000000, 7, 0, 1), QVAULT_SUCCESS); + QvaultV2.getState()->voteInProposalChecker(0, 7, 10000, 0); + + Asset qcapShare; + qcapShare.assetName = qcapAssetName; + qcapShare.issuer = QVAULT_QCAP_ISSUER; + + EXPECT_EQ(QvaultV2.QXTransferShareManagementRights(QVAULT_QCAP_ISSUER, qcapAssetName, QVAULT_CONTRACT_INDEX, 10000, QVAULT_CONTRACT_ID), 10000); + increaseEnergy(QVAULT_QCAP_ISSUER, 1000000); + EXPECT_EQ(QvaultV2.TransferShareManagementRights(QVAULT_CONTRACT_ID, qcapShare, 10000, QX_CONTRACT_INDEX), 10000); + + std::vector> qxSharesHolers{{QVAULT_CONTRACT_ID, 676}}; + issueContractShares(QX_CONTRACT_INDEX, qxSharesHolers); + + std::vector> qvaultSharesHolers{ {stakers[1], 1}, {stakers[2], 675}}; + issueContractShares(QVAULT_CONTRACT_INDEX, qvaultSharesHolers); + + increaseEnergy(stakers[1], 10000000); + EXPECT_EQ(QvaultV2.submitGP(stakers[1]), QVAULT_SUCCESS); + QvaultV2.getState()->submitGPChecker(1, stakers[1]); + + for (uint32 t = 0 ; t < 100; t++) + { + uint64 newAssetName; + char strAssetName[6]; + for (uint32 r = 0 ; r < 5; r++) + { + strAssetName[r] = 'A' + (uint32)random(0, 25); + } + strAssetName[5] = 0; + newAssetName = assetNameFromString(strAssetName); + increaseEnergy(stakers[2], 1000000000); + QvaultV2.issueAsset(stakers[2], newAssetName, 1000000000, 0, 0); + } - auto randomAddresses = getRandomUsers(QVAULT_QCAP_MAX_HOLDERS, QVAULT_QCAP_MAX_HOLDERS); + QvaultV2.qxEndTick(); + uint64 qxDistributedAmount = QvaultV2.qxGetState()->getDistributedAmountInEndTick(); + + QvaultV2.getState()->POST_INCOMING_TRANSFER_checker(qxDistributedAmount, system.epoch, QX_CONTRACT_INDEX); - // make sure that user exists in spectrum - increaseEnergy(QVAULT_authAddress1, 1); - increaseEnergy(QVAULT_authAddress2, 1); - increaseEnergy(QVAULT_authAddress3, 1); + QvaultV2.endEpoch(); - qvault.submitAuthAddress(QVAULT_authAddress1, randomAddresses[0]); - qvault.submitAuthAddress(QVAULT_authAddress2, randomAddresses[0]); - qvault.submitAuthAddress(QVAULT_authAddress3, randomAddresses[0]); + QvaultV2.getState()->endEpochChecker(qxDistributedAmount); - qvault.submitDistributionPermille(QVAULT_authAddress1, 500, 400, 70); - qvault.submitDistributionPermille(QVAULT_authAddress2, 500, 400, 70); - qvault.submitDistributionPermille(QVAULT_authAddress3, 500, 400, 70); + QvaultV2.TransferShareOwnershipAndPossession(QVAULT_QCAP_ISSUER, qcapAssetName, 30000, QVAULT_CONTRACT_ID, stakers[0]); - qvault.submitReinvestingAddress(QVAULT_authAddress1, randomAddresses[0]); - qvault.submitReinvestingAddress(QVAULT_authAddress2, randomAddresses[0]); - qvault.submitReinvestingAddress(QVAULT_authAddress3, randomAddresses[0]); + EXPECT_EQ(numberOfPossessedShares(qcapAssetName, QVAULT_QCAP_ISSUER, stakers[0], stakers[0], QX_CONTRACT_INDEX, QX_CONTRACT_INDEX), 30000); + QvaultV2.AddToAskOrder(QVAULT_QCAP_ISSUER, qcapAssetName, 1000, 30000, stakers[0]); - qvault.submitAdminAddress(QVAULT_authAddress1, randomAddresses[0]); - qvault.submitAdminAddress(QVAULT_authAddress2, randomAddresses[0]); - qvault.submitAdminAddress(QVAULT_authAddress3, randomAddresses[0]); + system.epoch += 2; + + QvaultV2.beginEpoch(); + + for (uint32 t = 0 ; t < 100; t++) + { + uint64 newAssetName; + char strAssetName[6]; + for (uint32 r = 0 ; r < 5; r++) + { + strAssetName[r] = 'B' + (uint32)random(0, 24); + } + strAssetName[5] = 0; + newAssetName = assetNameFromString(strAssetName); + increaseEnergy(stakers[2], 1000000000); + QvaultV2.issueAsset(stakers[2], newAssetName, 1000000000, 0, 0); + } - qvault.submitBannedAddress(QVAULT_authAddress1, randomAddresses[0]); - qvault.submitBannedAddress(QVAULT_authAddress2, randomAddresses[0]); - qvault.submitBannedAddress(QVAULT_authAddress3, randomAddresses[0]); + QvaultV2.qxEndTick(); - qvault.submitUnbannedAddress(QVAULT_authAddress1, randomAddresses[0]); - qvault.submitUnbannedAddress(QVAULT_authAddress2, randomAddresses[0]); - qvault.submitUnbannedAddress(QVAULT_authAddress3, randomAddresses[0]); + QvaultV2.endEpoch(); - auto output = qvault.getData(); - qvault.getState()->getDataChecker(output); + system.epoch++; + uint64 currentNumberOfQcapInQvault = numberOfPossessedShares(qcapAssetName, QVAULT_QCAP_ISSUER, QVAULT_CONTRACT_ID, QVAULT_CONTRACT_ID, QX_CONTRACT_INDEX, QX_CONTRACT_INDEX) + numberOfPossessedShares(qcapAssetName, QVAULT_QCAP_ISSUER, QVAULT_CONTRACT_ID, QVAULT_CONTRACT_ID, QVAULT_CONTRACT_INDEX, QVAULT_CONTRACT_INDEX); + QvaultV2.beginEpoch(); - qvault.changeAuthAddress(QVAULT_authAddress1, 3); - qvault.changeDistributionPermille(QVAULT_authAddress1, 500, 400, 70); - qvault.changeReinvestingAddress(QVAULT_authAddress1, randomAddresses[0]); - qvault.changeAdminAddress(QVAULT_authAddress1, randomAddresses[0]); - qvault.saveBannedAddress(QVAULT_authAddress1, randomAddresses[0]); + EXPECT_EQ(numberOfPossessedShares(qcapAssetName, QVAULT_QCAP_ISSUER, stakers[0], stakers[0], QX_CONTRACT_INDEX, QX_CONTRACT_INDEX), 0); + EXPECT_EQ(currentNumberOfQcapInQvault, numberOfPossessedShares(qcapAssetName, QVAULT_QCAP_ISSUER, QVAULT_CONTRACT_ID, QVAULT_CONTRACT_ID, QX_CONTRACT_INDEX, QX_CONTRACT_INDEX) + numberOfPossessedShares(qcapAssetName, QVAULT_QCAP_ISSUER, QVAULT_CONTRACT_ID, QVAULT_CONTRACT_ID, QVAULT_CONTRACT_INDEX, QVAULT_CONTRACT_INDEX)); - output = qvault.getData(); - qvault.getState()->getDataChecker(output); + increaseEnergy(stakers[3], 100000000); + uint64 tmpAmount = numberOfPossessedShares(qcapAssetName, QVAULT_QCAP_ISSUER, stakers[3], stakers[3], QVAULT_CONTRACT_INDEX, QVAULT_CONTRACT_INDEX); + QvaultV2.buyQcap(stakers[3], 100, 10000000); + EXPECT_EQ(numberOfPossessedShares(qcapAssetName, QVAULT_QCAP_ISSUER, stakers[3], stakers[3], QVAULT_CONTRACT_INDEX, QVAULT_CONTRACT_INDEX), tmpAmount + 100); } \ No newline at end of file