Did you ever though about a token that is mined by smart contract? And proof-of-donation?
The system consists in two types of smart contracts: TMGMiner and TMGPicker. Check the concepts:
This smart contract is deployed by users. After add balance to the contract it will start to send deadlines to the main Picker contract. By default it will start by mining with the least amount possible (0.32 Signa per hour or 7.6 Signa per day). The creator can change the mining intensity. Send at least 0.5 Signa and add a plain message with the new value. Ex: 1.344. The contract will continue the loop until there is no more balance. At any time creator can send some amount of Signa to top up the balance and the mining intesity will continue the same. To stop the contract and receive the remaining balance, send a transaction with amount over 0.5 and the plain text stop. In less than one hour the contract will be activated again and the balance will be sent back.
Smart contract deployed by deleterium, will centralize all received deadlines, pick the best, forge one token and send directly to the creator of that Miner. The picker will inspect the contract that send the deadline to ensure it is a valid Miner contract, to avoid deadline tampering. When a new deadline is submited after 15 or more blocks from previous forging, the contract will mint one token and transfer to the creator of the contract that sent the best deadline. This process will cause balance to build up in the contract and once a day it will be distributed to the holders of TMG asset, if the balance is greater than 250 Signa.
The miner contract will send a transaction to the picker. This transaction has an ID that can not be controlled by the creators. It depends on the time that the block was forged, so it is suposed to be random (note 1). To calculate the deadline, the Picker will:
- Get the transaction ID value and divide by two. This is the base deadline. Division is needed to avoid negative values internally.
- Get the transaction amount (mining intensity) and divide by the minimum amount (0.32 Signa). This factor (always greater or equal one) is called mining factor.
- The final deadline is the base deadline divided by the mining factor. So the greater the mining factor, lower the deadline. In this way, users can adjust the mining intensity to increase change to win a block, and with bigger overall mining intensity, more Signa will be distributed to TMG token holders.
Once per day (actually every 24 TMG forged) the Picker will check the accumulated balance in the contract. If this amount is greater than 250 Signa, 98% of it will be distributed to holders that have at least 1.0 TMG in account (the sum of free and locked tokens in sell orders). There is also a creator fee of 2%, that will be forwarded to SmartC NFT contract and distributed to the holders of SmartC Keywords.
When the contracts are running at the least mining intensity, almost all Signa will be consumed by contract execution. This is the way the smart contracts work, because each machine code instructions costs 0.001 Signa. With more players with bigger mining intensity, more balance will accumulate in the Picker contract to be distributed.
Check https://tmg.notallmine.net/ for a beautful website to check network information, link your account and control your miner contract.
- In theory the transaction ID from a smart contract can be manipulated during an attack from a bad actor with more than 50% Signum mining power. This is unlikely and, if done, the entire blockchain will be compromised. For this game purpose this situation is negligible.
// Select the picker contract accordingly:
// 999 for SIMULATOR
// 8749786126809286749 for TESTNET
// 18339269626061634110 for MAINNET
#define PICKER_CONTRACT 18339269626061634110
// #pragma verboseAssembly
/* Do not change below this line */
/* Details for carbon copy TESTNET
* Fullhash: 7ab1ed13566118fb8f36c05e25963d184875b4f0651d234f754161da5fd88059
* feeNQT: 10000000
*/
#define ACTIVATION_AMOUNT .5
#program name TMGminer
#program description The Mining Game - Standard miner smart contract
#program activationAmount ACTIVATION_AMOUNT
#program creator 123124
#program codeHashId 5817622329198284865
#pragma maxConstVars 1
#pragma optimizationLevel 3
#pragma version 2.0
#define CLOCK 15
#define DEFAULT_MINING_INTENSITY .32
// Constants initialization
const long pickerContract = PICKER_CONTRACT;
const fixed defaultIntensity = DEFAULT_MINING_INTENSITY;
const long n8 = 8, n10 = 10, n15 = 15, n16 = 16, n32 = 32, n46 = 46;
const long n48 = 48, n57 = 57;
const long n255 = 255, n100000000 = 100000000;
// Other variables
fixed miningIntensity, availableBalance;
long txid, message[4];
fixed messageIntensity;
// Initialization
miningIntensity = defaultIntensity;
while (true){
while ((txid = getNextTx()) != 0) {
// process transaction
if (getSender(txid) != getCreator()) {
// only process transactions from creator
continue;
}
readMessage(txid, 0, message);
if (message[0] == 'stop') {
miningIntensity = defaultIntensity;
sendBalance(getCreator());
continue;
}
// start of decodeAmountInMessage
long multiplier = 1_0000_0000;
long retVal = 0;
long ch;
long decimals = false;
for (long i = 0; i < 16; i++) {
ch = message[i / 8] >> ((i % 8) * 8);
ch &= 0xff;
if (ch == 0 || ch == ' ') break;
if (ch == '.') {
decimals = true;
continue;
}
if (ch < '0' || ch > '9' ) {
// invalid char
retVal = 0;
break;
}
if (decimals) {
multiplier /= 10;
} else {
retVal *= 10;
}
ch &= 0xF;
ch *= multiplier;
retVal += ch;
}
memcopy(&messageIntensity, &retVal);
// end of decodeAmountInMessage
if (messageIntensity < defaultIntensity) {
// Error decoding, no message sent or value too low
continue;
}
miningIntensity = messageIntensity;
}
sendAmountFx(miningIntensity, pickerContract);
sleep CLOCK;
}// Choose the target (only one of ):
//#define SIMULATOR
//#define TESTNET
#define MAINNET
// #pragma verboseAssembly
/* Do not change below this line */
#program name TMGpicker
#program description The Mining Game - Picker smart contract
#define ACTIVATION_AMOUNT 0.32
#program activationAmount ACTIVATION_AMOUNT
#define CLOCK 15
#define GAME_FEE .02
#define MINIMUM_AMOUNT_TO_DISTRIBUTE 250.0
#define DIVIDENDS_MSG 0x83CDA47900000000
#define authorizedCodeHashId 5817622329198284865
#ifdef SIMULATOR
#program codeHashId 1136995200036281707
#define SMARTCNFT 12341234
#define TRY_DISTRIBUTION_INTERVAL 1
#endif
#ifdef TESTNET
#program codeHashId 13677852211975617521
#define SMARTCNFT "TS-J8X4-6WB2-62W5-6ZTGZ"
#define TRY_DISTRIBUTION_INTERVAL 24
#endif
#ifdef MAINNET
#program codeHashId 18180423945324926420
#define SMARTCNFT "S-NFT2-6MA4-KLA2-DNM8T"
#define TRY_DISTRIBUTION_INTERVAL 24
#endif
#pragma maxConstVars 2
#pragma maxAuxVars 3
#pragma optimizationLevel 3
// #pragma version 2.0.0
const long n0 = 0, n100 = 100, n100000000 = 100000000;
const long maxPositive = 0x7fffffffffffffff;
const fixed activationAmount = ACTIVATION_AMOUNT;
long tokenId;
long dividendsMessage[4];
struct TXINFO {
long txId,
baseDeadline,
sender;
fixed miningIntensity;
} currentTX;
struct BEST {
long deadline;
long sender;
} best;
struct STATS {
fixed overallMiningFactor,
lastOverallMiningFactor;
long processedDeadlines,
currentHeight,
lastWinnerId,
lastWinnerDeadline;
} stats;
// startUp routine
tokenId = issueAsset("TMG", "", 2);
best.deadline = maxPositive;
dividendsMessage[] = DIVIDENDS_MSG;
void main() {
while ((currentTX.txId = getNextTx()) != 0) {
// Get transaction details
currentTX.sender = getSender(currentTX.txId);
currentTX.miningIntensity = getAmountFx(currentTX.txId) + activationAmount;
// base deadline is unsigned long txId divided by two
currentTX.baseDeadline = currentTX.txId / 2;
if (currentTX.baseDeadline < 0) {
currentTX.baseDeadline += maxPositive;
currentTX.baseDeadline++;
}
processTX();
}
// All transactions processed, try to forge new token
forgeTokens();
}
void processTX() {
fixed miningFactor = currentTX.miningIntensity / activationAmount;
long currentDeadline = mdv(currentTX.baseDeadline, 100000000, bcftol(miningFactor));
stats.processedDeadlines++;
stats.overallMiningFactor += miningFactor;
if (currentDeadline < best.deadline) {
if (getCodeHashOf(currentTX.sender) != authorizedCodeHashId) {
// Avoid deadline tampering
return;
}
best.deadline = currentDeadline;
best.sender = currentTX.sender;
}
}
void forgeTokens() {
long lastForging;
long currentBlock = getCurrentBlockheight();
if (best.deadline == maxPositive || currentBlock - lastForging < CLOCK ) {
return;
}
// Mint new token and send to winner
stats.lastOverallMiningFactor = stats.overallMiningFactor;
stats.lastWinnerDeadline = best.deadline;
lastForging = currentBlock;
stats.lastWinnerId = getCreatorOf(best.sender);
mintAsset(100, tokenId);
sendQuantity(100, tokenId, stats.lastWinnerId);
// Try distribuition
if (stats.currentHeight % TRY_DISTRIBUTION_INTERVAL == 0) {
distributeBalance();
}
// Prepare variables for next round
best.deadline = maxPositive;
best.sender = 0;
stats.overallMiningFactor = 0.0;
stats.currentHeight++;
}
void distributeBalance() {
fixed currentAvailableBalance = getCurrentBalanceFx() - activationAmount;
if (currentAvailableBalance < MINIMUM_AMOUNT_TO_DISTRIBUTE) {
return;
}
distributeToHoldersFx(100, tokenId, currentAvailableBalance * (1.0 - GAME_FEE), n0, n0);
if (getCurrentBalanceFx() > currentAvailableBalance / 2) {
// Do not pay fee if the distribution was unsucessful
return;
}
// Pay Game Fee
sendAmountAndMessageFx(currentAvailableBalance * GAME_FEE, dividendsMessage, SMARTCNFT);
}