Skip to content

Commit c043ea1

Browse files
committed
Snapshot QMINE shares in all SC
1 parent 6467b0d commit c043ea1

File tree

2 files changed

+96
-5
lines changed

2 files changed

+96
-5
lines changed

src/contracts/qRWA.h

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1288,6 +1288,8 @@ struct QRWA : public ContractBase
12881288
AssetPossessionIterator iter;
12891289
uint64 balance;
12901290
qRWALogger logger;
1291+
id holder;
1292+
uint64 existingBalance;
12911293
};
12921294
BEGIN_EPOCH_WITH_LOCALS()
12931295
{
@@ -1311,18 +1313,27 @@ struct QRWA : public ContractBase
13111313
continue;
13121314
}
13131315
locals.balance = locals.iter.numberOfPossessedShares();
1316+
locals.holder = locals.iter.possessor();
1317+
13141318
if (locals.balance > 0)
13151319
{
1316-
if (state.mBeginEpochBalances.set(locals.iter.possessor(), locals.balance) != NULL_INDEX)
1320+
// Check if holder already exists in the map (e.g. from a different manager)
1321+
// If so, add to existing balance.
1322+
locals.existingBalance = 0;
1323+
state.mBeginEpochBalances.get(locals.holder, locals.existingBalance);
1324+
1325+
locals.balance = sadd(locals.existingBalance, locals.balance);
1326+
1327+
if (state.mBeginEpochBalances.set(locals.holder, locals.balance) != NULL_INDEX)
13171328
{
1318-
state.mTotalQmineBeginEpoch = sadd(state.mTotalQmineBeginEpoch, locals.balance);
1329+
state.mTotalQmineBeginEpoch = sadd(state.mTotalQmineBeginEpoch, (uint64)locals.iter.numberOfPossessedShares());
13191330
}
13201331
else
13211332
{
13221333
// Log error - Max holders reached for snapshot
13231334
locals.logger.contractId = CONTRACT_INDEX;
13241335
locals.logger.logType = QRWA_LOG_TYPE_ERROR;
1325-
locals.logger.primaryId = locals.iter.possessor();
1336+
locals.logger.primaryId = locals.holder;
13261337
locals.logger.valueA = 11; // Error code: Begin Epoch Snapshot full
13271338
locals.logger.valueB = state.mBeginEpochBalances.population();
13281339
LOG_INFO(locals.logger);
@@ -1387,6 +1398,9 @@ struct QRWA : public ContractBase
13871398
qRWAAsset wrapper;
13881399

13891400
qRWAGovProposal govPoll;
1401+
1402+
id holder;
1403+
uint64 existingBalance;
13901404
};
13911405
END_EPOCH_WITH_LOCALS()
13921406
{
@@ -1403,14 +1417,22 @@ struct QRWA : public ContractBase
14031417
continue;
14041418
}
14051419
locals.balance = locals.iter.numberOfPossessedShares();
1420+
locals.holder = locals.iter.possessor();
1421+
14061422
if (locals.balance > 0)
14071423
{
1408-
if (state.mEndEpochBalances.set(locals.iter.possessor(), locals.balance) == NULL_INDEX)
1424+
// Check if holder already exists (multiple SC management)
1425+
locals.existingBalance = 0;
1426+
state.mEndEpochBalances.get(locals.holder, locals.existingBalance);
1427+
1428+
locals.balance = sadd(locals.existingBalance, locals.balance);
1429+
1430+
if (state.mEndEpochBalances.set(locals.holder, locals.balance) == NULL_INDEX)
14091431
{
14101432
// Log error - Max holders reached for snapshot
14111433
locals.logger.contractId = CONTRACT_INDEX;
14121434
locals.logger.logType = QRWA_LOG_TYPE_ERROR;
1413-
locals.logger.primaryId = locals.iter.possessor();
1435+
locals.logger.primaryId = locals.holder;
14141436
locals.logger.valueA = 12; // Error code: End Epoch Snapshot full
14151437
locals.logger.valueB = state.mEndEpochBalances.population();
14161438
LOG_INFO(locals.logger);

test/contract_qrwa.cpp

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ class ContractTestingQRWA : protected ContractTesting
9898
callSystemProcedure(QX_CONTRACT_INDEX, INITIALIZE);
9999
INIT_CONTRACT(QUTIL);
100100
callSystemProcedure(QUTIL_CONTRACT_INDEX, INITIALIZE);
101+
INIT_CONTRACT(QSWAP);
102+
callSystemProcedure(QSWAP_CONTRACT_INDEX, INITIALIZE);
101103

102104
// Custom Initialization for qRWA State
103105
// (Overrides defaults from INITIALIZE() for testing purposes)
@@ -1679,3 +1681,70 @@ TEST(ContractQRWA, FullScenario_DividendsAndGovernance)
16791681
EXPECT_EQ(getBalance(Q1), prevBalances[Q1] + divQQ1);
16801682
EXPECT_EQ(getBalance(Q2), prevBalances[Q2] + divQQ2);
16811683
}
1684+
1685+
TEST(ContractQRWA, Payout_MultiContractManagement)
1686+
{
1687+
ContractTestingQRWA qrwa;
1688+
1689+
const sint64 totalShares = 1000000;
1690+
const sint64 qxManagedShares = 700000;
1691+
const sint64 qswapManagedShares = 300000; // 30% moved to QSWAP management
1692+
1693+
// Issue QMINE and give to HOLDER_A
1694+
// Initially, all 1M shares are managed by QX (default for transfers via QX)
1695+
increaseEnergy(QMINE_ISSUER, 1000000000);
1696+
increaseEnergy(HOLDER_A, 1000000); // For fees
1697+
1698+
qrwa.issueAsset(QMINE_ISSUER, QMINE_ASSET.assetName, totalShares);
1699+
qrwa.transferAsset(QMINE_ISSUER, HOLDER_A, QMINE_ASSET, totalShares);
1700+
1701+
// Verify initial state managed by QX
1702+
EXPECT_EQ(numberOfPossessedShares(QMINE_ASSET.assetName, QMINE_ASSET.issuer, HOLDER_A, HOLDER_A, QX_CONTRACT_INDEX, QX_CONTRACT_INDEX), totalShares);
1703+
EXPECT_EQ(numberOfPossessedShares(QMINE_ASSET.assetName, QMINE_ASSET.issuer, HOLDER_A, HOLDER_A, QSWAP_CONTRACT_INDEX, QSWAP_CONTRACT_INDEX), 0);
1704+
1705+
// Transfer management rights of 300k shares to QSWAP
1706+
// The user (HOLDER_A) remains the Possessor.
1707+
qrwa.transferManagementRights(HOLDER_A, QMINE_ASSET, qswapManagedShares, QSWAP_CONTRACT_INDEX);
1708+
1709+
// Verify the split in management rights
1710+
// 700k should remain under QX
1711+
EXPECT_EQ(numberOfPossessedShares(QMINE_ASSET.assetName, QMINE_ASSET.issuer, HOLDER_A, HOLDER_A, QX_CONTRACT_INDEX, QX_CONTRACT_INDEX), qxManagedShares);
1712+
// 300k should now be under QSWAP
1713+
EXPECT_EQ(numberOfPossessedShares(QMINE_ASSET.assetName, QMINE_ASSET.issuer, HOLDER_A, HOLDER_A, QSWAP_CONTRACT_INDEX, QSWAP_CONTRACT_INDEX), qswapManagedShares);
1714+
1715+
qrwa.beginEpoch();
1716+
1717+
// Generate Revenue
1718+
// pool A revenue: 1,000,000 QUs
1719+
// fees (50%): 500,000
1720+
// net revenue: 500,000
1721+
// QMINE pool (90%): 450,000
1722+
qrwa.sendToMany(ADMIN_ADDRESS, id(QRWA_CONTRACT_INDEX, 0, 0, 0), 1000000);
1723+
1724+
qrwa.endEpoch();
1725+
1726+
// trigger Payout
1727+
etalonTick.year = 25; etalonTick.month = 11; etalonTick.day = 7; // Friday
1728+
etalonTick.hour = 12; etalonTick.minute = 1; etalonTick.second = 0;
1729+
qrwa.resetPayoutTime();
1730+
1731+
// snapshot balances for check
1732+
sint64 balanceBefore = getBalance(HOLDER_A);
1733+
1734+
qrwa.endTick();
1735+
1736+
// Calculate Expected Payout
1737+
// Payout = (UserTotalShares * PoolAmount) / TotalSupply
1738+
// UserTotalShares = 1,000,000 (regardless of manager)
1739+
// PoolAmount = 450,000
1740+
// TotalSupply = 1,000,000
1741+
// Expected = 450,000
1742+
sint64 expectedPayout = (totalShares * 450000) / totalShares;
1743+
1744+
sint64 balanceAfter = getBalance(HOLDER_A);
1745+
1746+
// If qRWA only counted QX shares, the payout would be (700k/1M * 450k) = 315,000.
1747+
// If qRWA counts ALL shares, the payout is 450,000.
1748+
EXPECT_EQ(balanceAfter - balanceBefore, expectedPayout);
1749+
EXPECT_EQ(balanceAfter - balanceBefore, 450000);
1750+
}

0 commit comments

Comments
 (0)