diff --git a/src/Qubic.vcxproj b/src/Qubic.vcxproj
index 961ee7dc..1165b1ab 100644
--- a/src/Qubic.vcxproj
+++ b/src/Qubic.vcxproj
@@ -36,7 +36,8 @@
-
+
+
diff --git a/src/Qubic.vcxproj.filters b/src/Qubic.vcxproj.filters
index cfcb739a..33711b95 100644
--- a/src/Qubic.vcxproj.filters
+++ b/src/Qubic.vcxproj.filters
@@ -98,6 +98,9 @@
contracts
+
+
+ contracts
contracts
diff --git a/src/contract_core/contract_def.h b/src/contract_core/contract_def.h
index f6f11909..591dd9d3 100644
--- a/src/contract_core/contract_def.h
+++ b/src/contract_core/contract_def.h
@@ -302,6 +302,16 @@
#endif
+#undef CONTRACT_INDEX
+#undef CONTRACT_STATE_TYPE
+#undef CONTRACT_STATE2_TYPE
+
+#define QASSAND_CONTRACT_INDEX 29
+#define CONTRACT_INDEX QASSAND_CONTRACT_INDEX
+#define CONTRACT_STATE_TYPE QASSAND
+#define CONTRACT_STATE2_TYPE QASSAND2
+#include "contracts/Qassand.h"
+
// new contracts should be added above this line
#ifdef INCLUDE_CONTRACT_TEST_EXAMPLES
@@ -417,8 +427,9 @@ constexpr struct ContractDescription
{"QUSINO", 208, 10000, sizeof(QUSINO::StateData)}, // proposal in epoch 206, IPO in 207, construction and first use in 208
{"ESCROW", 210, 10000, sizeof(ESCROW::StateData)}, // proposal in epoch 208, IPO in 209, construction and first use in 210
#ifndef NO_GGWP
- {"GGWP", 217, 10000, sizeof(WOLFPACK::StateData)}, // proposal in epoch 215, IPO in 216, construction and first use in 217
+ {"GGWP", 218, 10000, sizeof(WOLFPACK::StateData)}, // proposal in epoch 216, IPO in 217, construction and first use in 218
#endif
+ {"QASSAND", QASSAND_CONSTRUCTION_EPOCH_PLACEHOLDER, 10000, sizeof(QASSAND::StateData)},
// new contracts should be added above this line
#ifdef INCLUDE_CONTRACT_TEST_EXAMPLES
{"TESTEXA", 138, 10000, sizeof(TESTEXA::StateData)},
@@ -545,6 +556,7 @@ static void initializeContracts()
#ifndef NO_GGWP
REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(WOLFPACK);
#endif
+ REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(QASSAND);
// new contracts should be added above this line
#ifdef INCLUDE_CONTRACT_TEST_EXAMPLES
REGISTER_CONTRACT_FUNCTIONS_AND_PROCEDURES(TESTEXA);
@@ -557,7 +569,9 @@ static void initializeContracts()
// Automatic Contract State Changes
enum ContractStateChangeType
{
+ // Keeps the saved state's old bytes, only zero-fills the new bytes at the end (used when struct grew; old fields preserved)
PADDING,
+ // Discards the saved state entirely, zeros the whole buffer
RESET,
};
struct ContractStateChangeInfo
@@ -566,8 +580,9 @@ struct ContractStateChangeInfo
ContractStateChangeType changeType;
};
// Contracts whose state struct changed this epoch. Update this list each epoch as needed.
+// Each entry is { CONTRACT_INDEX, PADDING or RESET }
// When enabling, replace both lines below, e.g.:
-constexpr ContractStateChangeInfo contractStateChangeInfos[] = { { QIP_CONTRACT_INDEX, RESET } };
+constexpr ContractStateChangeInfo contractStateChangeInfos[] = { {RANDOM_CONTRACT_INDEX, PADDING} };
constexpr unsigned int contractStateChangeCount = sizeof(contractStateChangeInfos) / sizeof(contractStateChangeInfos[0]);
// constexpr const ContractStateChangeInfo* contractStateChangeInfos = nullptr;
// constexpr unsigned int contractStateChangeCount = 0;
diff --git a/src/contracts/Qassand.h b/src/contracts/Qassand.h
new file mode 100644
index 00000000..9053351d
--- /dev/null
+++ b/src/contracts/Qassand.h
@@ -0,0 +1,256 @@
+using namespace QPI;
+
+// QASSAND is the Qubic contract implementation name for the Qassandra protocol v0.
+constexpr uint16 QASSAND_VERSION = 0;
+constexpr uint16 QASSAND_CONSTRUCTION_EPOCH_PLACEHOLDER = 10000;
+constexpr uint64 QASSAND_PROTOCOL_FEE = 75000;
+constexpr uint64 QASSAND_BURN_FEE = 25000;
+constexpr uint64 QASSAND_PING_FEE = QASSAND_PROTOCOL_FEE + QASSAND_BURN_FEE;
+constexpr uint8 QASSAND_SUCCESS = 0;
+constexpr uint8 QASSAND_UNDERPAID = 1;
+constexpr uint8 QASSAND_UNKNOWN_LANE = 2;
+constexpr uint16 QASSAND_LANE_UNKNOWN = 0;
+constexpr uint16 QASSAND_LANE_FORECASTING = 1;
+constexpr uint16 QASSAND_LANE_STABLE_OPERATIONS = 2;
+constexpr uint16 QASSAND_LANE_DATA_ATTESTATION = 3;
+
+struct QASSAND2
+{
+};
+
+struct QASSAND : public ContractBase
+{
+ struct StateData
+ {
+ uint16 version;
+ uint16 constructionEpoch;
+ uint64 protocolFee;
+ uint64 burnFee;
+ uint64 totalPingCount;
+ uint64 protocolEarnedFee;
+ uint64 burnEarnedFee;
+ uint64 pendingBurnAmount;
+ uint64 totalBurnedAmount;
+ };
+
+ struct Ping_input
+ {
+ };
+
+ struct Ping_output
+ {
+ uint8 returnCode;
+ uint64 acceptedFee;
+ uint64 refundedAmount;
+ uint64 protocolEarnedFee;
+ uint64 burnEarnedFee;
+ uint64 totalPingCount;
+ };
+
+ struct Ping_locals
+ {
+ uint64 paidAmount;
+ uint64 refundAmount;
+ };
+
+ struct GetInfo_input
+ {
+ };
+
+ struct GetInfo_output
+ {
+ Array protocolName;
+ uint16 version;
+ uint16 constructionEpoch;
+ uint64 totalPingCount;
+ };
+
+ struct GetFeeInfo_input
+ {
+ };
+
+ struct GetFeeInfo_output
+ {
+ uint64 pingFee;
+ uint64 protocolFee;
+ uint64 burnFee;
+ uint64 protocolEarnedFee;
+ uint64 burnEarnedFee;
+ };
+
+ struct GetBurnInfo_input
+ {
+ };
+
+ struct GetBurnInfo_output
+ {
+ uint64 pendingBurnAmount;
+ uint64 totalBurnedAmount;
+ };
+
+ struct GetLaneInfo_input
+ {
+ uint16 laneId;
+ };
+
+ struct GetLaneInfo_output
+ {
+ uint8 returnCode;
+ uint16 laneId;
+ Array laneName;
+ uint64 requiredFee;
+ };
+
+ struct END_TICK_locals
+ {
+ uint64 burnAmount;
+ };
+
+ INITIALIZE()
+ {
+ state.mut().version = QASSAND_VERSION;
+ state.mut().constructionEpoch = QASSAND_CONSTRUCTION_EPOCH_PLACEHOLDER;
+ state.mut().protocolFee = QASSAND_PROTOCOL_FEE;
+ state.mut().burnFee = QASSAND_BURN_FEE;
+ }
+
+ REGISTER_USER_FUNCTIONS_AND_PROCEDURES()
+ {
+ REGISTER_USER_PROCEDURE(Ping, 1);
+ REGISTER_USER_FUNCTION(GetInfo, 1);
+ REGISTER_USER_FUNCTION(GetFeeInfo, 2);
+ REGISTER_USER_FUNCTION(GetBurnInfo, 3);
+ REGISTER_USER_FUNCTION(GetLaneInfo, 4);
+ }
+
+ END_TICK_WITH_LOCALS()
+ {
+ locals.burnAmount = state.get().pendingBurnAmount;
+ if (locals.burnAmount == 0)
+ {
+ return;
+ }
+
+ qpi.burn(locals.burnAmount);
+ state.mut().pendingBurnAmount = 0;
+ state.mut().totalBurnedAmount += locals.burnAmount;
+ }
+
+ PUBLIC_PROCEDURE_WITH_LOCALS(Ping)
+ {
+ locals.paidAmount = qpi.invocationReward();
+
+ if (locals.paidAmount < (state.get().protocolFee + state.get().burnFee))
+ {
+ if (locals.paidAmount != 0)
+ {
+ qpi.transfer(qpi.invocator(), locals.paidAmount);
+ }
+ output.returnCode = QASSAND_UNDERPAID;
+ output.refundedAmount = locals.paidAmount;
+ return;
+ }
+
+ locals.refundAmount = locals.paidAmount - (state.get().protocolFee + state.get().burnFee);
+ if (locals.refundAmount != 0)
+ {
+ qpi.transfer(qpi.invocator(), locals.refundAmount);
+ }
+
+ state.mut().totalPingCount += 1;
+ state.mut().protocolEarnedFee += state.get().protocolFee;
+ state.mut().burnEarnedFee += state.get().burnFee;
+ state.mut().pendingBurnAmount += state.get().burnFee;
+
+ output.returnCode = QASSAND_SUCCESS;
+ output.acceptedFee = (state.get().protocolFee + state.get().burnFee);
+ output.refundedAmount = locals.refundAmount;
+ output.protocolEarnedFee = state.get().protocolFee;
+ output.burnEarnedFee = state.get().burnFee;
+ output.totalPingCount = state.get().totalPingCount;
+ }
+
+ PUBLIC_FUNCTION(GetInfo)
+ {
+ output.protocolName.set(0, 81);
+ output.protocolName.set(1, 97);
+ output.protocolName.set(2, 115);
+ output.protocolName.set(3, 115);
+ output.protocolName.set(4, 97);
+ output.protocolName.set(5, 110);
+ output.protocolName.set(6, 100);
+ output.protocolName.set(7, 114);
+ output.protocolName.set(8, 97);
+ output.version = state.get().version;
+ output.constructionEpoch = state.get().constructionEpoch;
+ output.totalPingCount = state.get().totalPingCount;
+ }
+
+ PUBLIC_FUNCTION(GetFeeInfo)
+ {
+ output.pingFee = (state.get().protocolFee + state.get().burnFee);
+ output.protocolFee = state.get().protocolFee;
+ output.burnFee = state.get().burnFee;
+ output.protocolEarnedFee = state.get().protocolEarnedFee;
+ output.burnEarnedFee = state.get().burnEarnedFee;
+ }
+
+ PUBLIC_FUNCTION(GetBurnInfo)
+ {
+ output.pendingBurnAmount = state.get().pendingBurnAmount;
+ output.totalBurnedAmount = state.get().totalBurnedAmount;
+ }
+
+ PUBLIC_FUNCTION(GetLaneInfo)
+ {
+ if (input.laneId == QASSAND_LANE_FORECASTING)
+ {
+ output.returnCode = QASSAND_SUCCESS;
+ output.laneId = QASSAND_LANE_FORECASTING;
+ output.laneName.set(0, 70);
+ output.laneName.set(1, 111);
+ output.laneName.set(2, 114);
+ output.laneName.set(3, 101);
+ output.laneName.set(4, 99);
+ output.laneName.set(5, 97);
+ output.laneName.set(6, 115);
+ output.laneName.set(7, 116);
+ output.laneName.set(8, 105);
+ output.laneName.set(9, 110);
+ output.laneName.set(10, 103);
+ output.requiredFee = 0;
+ return;
+ }
+
+ if (input.laneId == QASSAND_LANE_STABLE_OPERATIONS)
+ {
+ output.returnCode = QASSAND_SUCCESS;
+ output.laneId = QASSAND_LANE_STABLE_OPERATIONS;
+ output.laneName.set(0, 83);
+ output.laneName.set(1, 116);
+ output.laneName.set(2, 97);
+ output.laneName.set(3, 98);
+ output.laneName.set(4, 108);
+ output.laneName.set(5, 101);
+ output.laneName.set(6, 79);
+ output.laneName.set(7, 112);
+ output.laneName.set(8, 115);
+ output.requiredFee = 0;
+ return;
+ }
+
+ if (input.laneId == QASSAND_LANE_DATA_ATTESTATION)
+ {
+ output.returnCode = QASSAND_SUCCESS;
+ output.laneId = QASSAND_LANE_DATA_ATTESTATION;
+ output.laneName.set(0, 68);
+ output.laneName.set(1, 97);
+ output.laneName.set(2, 116);
+ output.laneName.set(3, 97);
+ output.requiredFee = 0;
+ return;
+ }
+
+ output.returnCode = QASSAND_UNKNOWN_LANE;
+ }
+};
diff --git a/test/contract_qassand.cpp b/test/contract_qassand.cpp
new file mode 100644
index 00000000..6c46a4c3
--- /dev/null
+++ b/test/contract_qassand.cpp
@@ -0,0 +1,230 @@
+#define NO_UEFI
+
+#include "contract_testing.h"
+
+constexpr uint16 QASSAND_PROC_PING = 1;
+constexpr uint16 QASSAND_FUNC_GET_INFO = 1;
+constexpr uint16 QASSAND_FUNC_GET_FEE_INFO = 2;
+constexpr uint16 QASSAND_FUNC_GET_BURN_INFO = 3;
+constexpr uint16 QASSAND_FUNC_GET_LANE_INFO = 4;
+
+static const id QASSAND_CONTRACT_ID(QASSAND_CONTRACT_INDEX, 0, 0, 0);
+
+class QassandChecker : public QASSAND, public QASSAND::StateData
+{
+public:
+ uint16 versionValue() const { return version; }
+ uint16 constructionEpochValue() const { return constructionEpoch; }
+ uint64 totalPingCountValue() const { return totalPingCount; }
+ uint64 protocolEarnedFeeValue() const { return protocolEarnedFee; }
+ uint64 burnEarnedFeeValue() const { return burnEarnedFee; }
+ uint64 pendingBurnAmountValue() const { return pendingBurnAmount; }
+ uint64 totalBurnedAmountValue() const { return totalBurnedAmount; }
+};
+
+class ContractTestingQassand : protected ContractTesting
+{
+public:
+ ContractTestingQassand()
+ {
+ initEmptySpectrum();
+ initEmptyUniverse();
+ INIT_CONTRACT(QASSAND);
+ callSystemProcedure(QASSAND_CONTRACT_INDEX, INITIALIZE);
+ }
+
+ QassandChecker* state() { return reinterpret_cast(contractStates[QASSAND_CONTRACT_INDEX]); }
+
+ uint64 balanceOf(const id& account) const { return static_cast(getBalance(account)); }
+ uint64 balanceQassand() const { return balanceOf(QASSAND_CONTRACT_ID); }
+ void fund(const id& account, uint64 amount) { increaseEnergy(account, amount); }
+
+ QASSAND::Ping_output ping(const id& invocator, uint64 amount)
+ {
+ QASSAND::Ping_input input{};
+ QASSAND::Ping_output output{};
+ invokeUserProcedure(QASSAND_CONTRACT_INDEX, QASSAND_PROC_PING, input, output, invocator, amount);
+ return output;
+ }
+
+ QASSAND::GetInfo_output getInfo() const
+ {
+ QASSAND::GetInfo_input input{};
+ QASSAND::GetInfo_output output{};
+ callFunction(QASSAND_CONTRACT_INDEX, QASSAND_FUNC_GET_INFO, input, output);
+ return output;
+ }
+
+ QASSAND::GetFeeInfo_output getFeeInfo() const
+ {
+ QASSAND::GetFeeInfo_input input{};
+ QASSAND::GetFeeInfo_output output{};
+ callFunction(QASSAND_CONTRACT_INDEX, QASSAND_FUNC_GET_FEE_INFO, input, output);
+ return output;
+ }
+
+ QASSAND::GetBurnInfo_output getBurnInfo() const
+ {
+ QASSAND::GetBurnInfo_input input{};
+ QASSAND::GetBurnInfo_output output{};
+ callFunction(QASSAND_CONTRACT_INDEX, QASSAND_FUNC_GET_BURN_INFO, input, output);
+ return output;
+ }
+
+ QASSAND::GetLaneInfo_output getLaneInfo(uint16 laneId) const
+ {
+ QASSAND::GetLaneInfo_input input{laneId};
+ QASSAND::GetLaneInfo_output output{};
+ callFunction(QASSAND_CONTRACT_INDEX, QASSAND_FUNC_GET_LANE_INFO, input, output);
+ return output;
+ }
+
+ void endTick() { callSystemProcedure(QASSAND_CONTRACT_INDEX, END_TICK); }
+};
+
+static bool arrayStartsWith(const Array& value, const char* expected)
+{
+ for (uint16 i = 0; expected[i] != 0; ++i)
+ {
+ if (value.get(i) != static_cast(expected[i]))
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+TEST(ContractQassand, InitializeSetsPingV0Metadata)
+{
+ ContractTestingQassand qassand;
+ QassandChecker* state = qassand.state();
+
+ EXPECT_EQ(state->versionValue(), QASSAND_VERSION);
+ EXPECT_EQ(state->constructionEpochValue(), QASSAND_CONSTRUCTION_EPOCH_PLACEHOLDER);
+ EXPECT_EQ(state->totalPingCountValue(), 0ull);
+ EXPECT_EQ(state->protocolEarnedFeeValue(), 0ull);
+ EXPECT_EQ(state->burnEarnedFeeValue(), 0ull);
+ EXPECT_EQ(state->pendingBurnAmountValue(), 0ull);
+ EXPECT_EQ(state->totalBurnedAmountValue(), 0ull);
+}
+
+TEST(ContractQassand, ReadsMetadataAndFeeState)
+{
+ ContractTestingQassand qassand;
+
+ const QASSAND::GetInfo_output info = qassand.getInfo();
+ EXPECT_TRUE(arrayStartsWith(info.protocolName, "Qassandra"));
+ EXPECT_EQ(info.version, QASSAND_VERSION);
+ EXPECT_EQ(info.constructionEpoch, QASSAND_CONSTRUCTION_EPOCH_PLACEHOLDER);
+ EXPECT_EQ(info.totalPingCount, 0ull);
+
+ const QASSAND::GetFeeInfo_output fees = qassand.getFeeInfo();
+ EXPECT_EQ(fees.pingFee, QASSAND_PING_FEE);
+ EXPECT_EQ(fees.protocolFee, QASSAND_PROTOCOL_FEE);
+ EXPECT_EQ(fees.burnFee, QASSAND_BURN_FEE);
+ EXPECT_EQ(fees.protocolEarnedFee, 0ull);
+ EXPECT_EQ(fees.burnEarnedFee, 0ull);
+
+ const QASSAND::GetBurnInfo_output burn = qassand.getBurnInfo();
+ EXPECT_EQ(burn.pendingBurnAmount, 0ull);
+ EXPECT_EQ(burn.totalBurnedAmount, 0ull);
+}
+
+TEST(ContractQassand, ReadsLaneTaxonomy)
+{
+ ContractTestingQassand qassand;
+
+ const QASSAND::GetLaneInfo_output forecastingLane = qassand.getLaneInfo(QASSAND_LANE_FORECASTING);
+ EXPECT_EQ(forecastingLane.returnCode, QASSAND_SUCCESS);
+ EXPECT_EQ(forecastingLane.laneId, QASSAND_LANE_FORECASTING);
+ EXPECT_TRUE(arrayStartsWith(forecastingLane.laneName, "Forecasting"));
+ EXPECT_EQ(forecastingLane.requiredFee, 0ull);
+
+ const QASSAND::GetLaneInfo_output stableLane = qassand.getLaneInfo(QASSAND_LANE_STABLE_OPERATIONS);
+ EXPECT_EQ(stableLane.returnCode, QASSAND_SUCCESS);
+ EXPECT_EQ(stableLane.laneId, QASSAND_LANE_STABLE_OPERATIONS);
+ EXPECT_TRUE(arrayStartsWith(stableLane.laneName, "StableOps"));
+ EXPECT_EQ(stableLane.requiredFee, 0ull);
+
+ const QASSAND::GetLaneInfo_output attestationLane = qassand.getLaneInfo(QASSAND_LANE_DATA_ATTESTATION);
+ EXPECT_EQ(attestationLane.returnCode, QASSAND_SUCCESS);
+ EXPECT_EQ(attestationLane.laneId, QASSAND_LANE_DATA_ATTESTATION);
+ EXPECT_TRUE(arrayStartsWith(attestationLane.laneName, "Data"));
+ EXPECT_EQ(attestationLane.requiredFee, 0ull);
+
+ const QASSAND::GetLaneInfo_output unknownLane = qassand.getLaneInfo(QASSAND_LANE_UNKNOWN);
+ EXPECT_EQ(unknownLane.returnCode, QASSAND_UNKNOWN_LANE);
+}
+
+TEST(ContractQassand, UnderpaymentRefundsAndDoesNotAccountFee)
+{
+ ContractTestingQassand qassand;
+ const id user = id::randomValue();
+ qassand.fund(user, QASSAND_PING_FEE);
+
+ const uint64 underpaidAmount = QASSAND_PING_FEE - 1;
+ const QASSAND::Ping_output underpaid = qassand.ping(user, underpaidAmount);
+ EXPECT_EQ(underpaid.returnCode, QASSAND_UNDERPAID);
+ EXPECT_EQ(underpaid.refundedAmount, underpaidAmount);
+ EXPECT_EQ(qassand.balanceOf(user), QASSAND_PING_FEE);
+ EXPECT_EQ(qassand.balanceQassand(), 0ull);
+ EXPECT_EQ(qassand.state()->totalPingCountValue(), 0ull);
+ EXPECT_EQ(qassand.state()->protocolEarnedFeeValue(), 0ull);
+ EXPECT_EQ(qassand.state()->burnEarnedFeeValue(), 0ull);
+ EXPECT_EQ(qassand.state()->pendingBurnAmountValue(), 0ull);
+}
+
+TEST(ContractQassand, ExactFeeAccountsProtocolAndDeferredBurn)
+{
+ ContractTestingQassand qassand;
+ const id user = id::randomValue();
+ qassand.fund(user, QASSAND_PING_FEE);
+
+ const QASSAND::Ping_output ping = qassand.ping(user, QASSAND_PING_FEE);
+ EXPECT_EQ(ping.returnCode, QASSAND_SUCCESS);
+ EXPECT_EQ(ping.acceptedFee, QASSAND_PING_FEE);
+ EXPECT_EQ(ping.refundedAmount, 0ull);
+ EXPECT_EQ(ping.protocolEarnedFee, QASSAND_PROTOCOL_FEE);
+ EXPECT_EQ(ping.burnEarnedFee, QASSAND_BURN_FEE);
+ EXPECT_EQ(ping.totalPingCount, 1ull);
+
+ EXPECT_EQ(qassand.balanceOf(user), 0ull);
+ EXPECT_EQ(qassand.balanceQassand(), QASSAND_PING_FEE);
+ EXPECT_EQ(qassand.state()->totalPingCountValue(), 1ull);
+ EXPECT_EQ(qassand.state()->protocolEarnedFeeValue(), QASSAND_PROTOCOL_FEE);
+ EXPECT_EQ(qassand.state()->burnEarnedFeeValue(), QASSAND_BURN_FEE);
+ EXPECT_EQ(qassand.state()->pendingBurnAmountValue(), QASSAND_BURN_FEE);
+}
+
+TEST(ContractQassand, ExcessFeeRefundsOnlyOverage)
+{
+ ContractTestingQassand qassand;
+ const id user = id::randomValue();
+ const uint64 paidAmount = QASSAND_PING_FEE + 12345;
+ qassand.fund(user, paidAmount);
+
+ const QASSAND::Ping_output ping = qassand.ping(user, paidAmount);
+ EXPECT_EQ(ping.returnCode, QASSAND_SUCCESS);
+ EXPECT_EQ(ping.acceptedFee, QASSAND_PING_FEE);
+ EXPECT_EQ(ping.refundedAmount, 12345ull);
+ EXPECT_EQ(qassand.balanceOf(user), 12345ull);
+ EXPECT_EQ(qassand.balanceQassand(), QASSAND_PING_FEE);
+ EXPECT_EQ(qassand.state()->totalPingCountValue(), 1ull);
+ EXPECT_EQ(qassand.state()->pendingBurnAmountValue(), QASSAND_BURN_FEE);
+}
+
+TEST(ContractQassand, EndTickBurnsDeferredAmount)
+{
+ ContractTestingQassand qassand;
+ const id user = id::randomValue();
+ qassand.fund(user, QASSAND_PING_FEE);
+
+ qassand.ping(user, QASSAND_PING_FEE);
+ EXPECT_EQ(qassand.state()->pendingBurnAmountValue(), QASSAND_BURN_FEE);
+ EXPECT_EQ(qassand.state()->totalBurnedAmountValue(), 0ull);
+
+ qassand.endTick();
+ EXPECT_EQ(qassand.state()->pendingBurnAmountValue(), 0ull);
+ EXPECT_EQ(qassand.state()->totalBurnedAmountValue(), QASSAND_BURN_FEE);
+ EXPECT_EQ(qassand.balanceQassand(), QASSAND_PROTOCOL_FEE);
+}
diff --git a/test/test.vcxproj b/test/test.vcxproj
index 2372e73d..c9d15ba7 100644
--- a/test/test.vcxproj
+++ b/test/test.vcxproj
@@ -148,6 +148,7 @@
+
diff --git a/test/test.vcxproj.filters b/test/test.vcxproj.filters
index 8eebbc6f..683c43bb 100644
--- a/test/test.vcxproj.filters
+++ b/test/test.vcxproj.filters
@@ -22,6 +22,7 @@
+