private static ContributionSums ContributionSumsFromBytes(byte[] bytes) { ContributionSums sums = new ContributionSums(); sums.TotalUnitsContributed = bytes.Range(0, 8).AsBigInteger(); sums.LastAppliedFeeSnapshot = bytes.Range(8, 16).AsBigInteger(); sums.TotalFeeUnits = bytes.Range(24, 16).AsBigInteger(); return(sums); }
private static void UpdateTotalFeeUnits(BigInteger totalFeesCollected, ContributionSums contributionSums) { if (contributionSums.LastAppliedFeeSnapshot < totalFeesCollected) { BigInteger feesCollectedSinceLastSnapshot = totalFeesCollected - contributionSums.LastAppliedFeeSnapshot; BigInteger feeUnits = contributionSums.TotalUnitsContributed * feesCollectedSinceLastSnapshot; contributionSums.TotalFeeUnits = contributionSums.TotalFeeUnits + feeUnits; Runtime.Notify("updateTotalFeeUnits", contributionSums.TotalFeeUnits, feeUnits, feesCollectedSinceLastSnapshot, contributionSums.TotalUnitsContributed, totalFeesCollected); contributionSums.LastAppliedFeeSnapshot = totalFeesCollected; } }
private static bool Compound(byte[] userAddress) { if (userAddress == GetWhitelister() || userAddress == GetManager()) { Transaction tx = (Transaction)ExecutionEngine.ScriptContainer; userAddress = ReadWithdrawToAddress(tx); } Contribution contribution = GetContribution(userAddress); if (contribution.UnitsContributed == 0) { Runtime.Notify("compoundFail", "No quantity committed", userAddress); return(false); } BigInteger minBlocks = GetClaimMinimumBlocks(); if (contribution.CompoundHeight + minBlocks > Blockchain.GetHeight()) { // Not yet eligible to compound. Runtime.Notify("compoundFail", "Not yet eligible to compound", userAddress, contribution.CompoundHeight, minBlocks, Blockchain.GetHeight()); return(false); } BigInteger totalFeesCollected = BigIntegerFrom8Bytes( Storage.Get(Storage.CurrentContext, FEES_POOL_KEY).Range(0, 8)); ContributionSums contributionSums = GetContributionSums(); // Update total fee units if fees have been collected since the last applied fee snapshot UpdateTotalFeeUnits(totalFeesCollected, contributionSums); BigInteger availableToClaim = GetAvailableToClaim(contribution, totalFeesCollected, contributionSums); contribution.CompoundHeight = Blockchain.GetHeight(); contribution.UnitsContributed += availableToClaim; contributionSums.TotalUnitsContributed += availableToClaim; contribution.FeesCollectedSnapshot = totalFeesCollected; contribution.FeeUnitsSnapshot = contributionSums.TotalFeeUnits; // Write back the contribution sums. Storage.Put(Storage.CurrentContext, CURRENT_CONTRIBUTIONS_KEY, ContributionSumsToBytes(contributionSums)); PutContribution(contribution); Runtime.Notify("compound", userAddress, contribution.UnitsContributed, contribution.CompoundHeight, contribution.FeesCollectedSnapshot, contribution.FeeUnitsSnapshot); return(true); }
public static BigInteger GetAvailableToClaim(byte[] userAddress) { Contribution contribution = GetContribution(userAddress); if (contribution.UnitsContributed == 0) { return(0); } BigInteger totalFeesCollected = Storage.Get(Storage.CurrentContext, FEES_POOL_KEY).AsBigInteger(); ContributionSums contributionSums = GetContributionSums(); return(GetAvailableToClaim(contribution, totalFeesCollected, contributionSums)); }
private static ContributionSums GetContributionSums() { byte[] bytes = Storage.Get(Storage.CurrentContext, CURRENT_CONTRIBUTIONS_KEY); if (bytes.Length != 0) { return(ContributionSumsFromBytes(bytes)); } ContributionSums sums = new ContributionSums(); // Initializing this way makes Runtime.Notify output more uniform when dispalying these values. sums.TotalUnitsContributed = EIGHT_BYTES.AsBigInteger(); sums.LastAppliedFeeSnapshot = SIXTEEN_BYTES.AsBigInteger(); sums.TotalFeeUnits = SIXTEEN_BYTES.AsBigInteger(); return(sums); }
private static byte[] ContributionSumsToBytes(ContributionSums contributionSums) { return(NormalizeBigIntegerTo8ByteArray(contributionSums.TotalUnitsContributed) .Concat(NormalizeBigIntegerTo16ByteArray(contributionSums.LastAppliedFeeSnapshot)) .Concat(NormalizeBigIntegerTo16ByteArray(contributionSums.TotalFeeUnits))); }
private static bool Claim(byte[] userAddress) { if (userAddress == GetManager()) { Transaction tx = (Transaction)ExecutionEngine.ScriptContainer; userAddress = ReadWithdrawToAddress(tx); } Contribution contribution = GetContribution(userAddress); if (contribution.UnitsContributed == 0) { Runtime.Notify("claimFail", "No quantity committed", userAddress); return(false); } byte[] poolData = Storage.Get(Storage.CurrentContext, FEES_POOL_KEY); BigInteger totalFeesCollected = BigIntegerFrom8Bytes(poolData.Range(0, 8)); ContributionSums contributionSums = GetContributionSums(); // Update total fee units if fees have been collected since the last applied fee snapshot UpdateTotalFeeUnits(totalFeesCollected, contributionSums); BigInteger availableToClaim = GetAvailableToClaim(contribution, totalFeesCollected, contributionSums); // Delete their investment DeleteContribution(contribution); // Reduce the total pool of investments contributionSums.TotalUnitsContributed -= contribution.UnitsContributed; // Write back the contribution sums. Storage.Put(Storage.CurrentContext, CURRENT_CONTRIBUTIONS_KEY, ContributionSumsToBytes(contributionSums)); BigInteger amountToReturn = contribution.UnitsContributed; if (contribution.ContributionHeight + GetClaimMinimumBlocks() <= Blockchain.GetHeight()) { amountToReturn += availableToClaim; } else { Runtime.Notify("claimFeesWentToOwner", "Less min blocks since commit, fees awarded to owner.", userAddress, availableToClaim); // Add the sum of the amount they would have received back to the contract owner's account BigInteger contractOwnerBalance = poolData.Range(8, 8).AsBigInteger(); contractOwnerBalance += availableToClaim; poolData = NormalizeBigIntegerTo8ByteArray(totalFeesCollected) .Concat(NormalizeBigIntegerTo8ByteArray(contractOwnerBalance)); Storage.Put(Storage.CurrentContext, FEES_POOL_KEY, poolData); } // Send claimed amount directly back to their wallet var transferSucceeded = SendNep5(userAddress, APH, amountToReturn); if (transferSucceeded == false) { Runtime.Notify("claimFail", "sendNep5 Transfer failed", userAddress, APH, amountToReturn); return(false); } Runtime.Notify("claimed", userAddress, amountToReturn); return(true); }
private static BigInteger GetAvailableToClaim(Contribution contribution, BigInteger totalFeesCollected, ContributionSums contributionSums) { BigInteger feesCollectedDuringCommitment = totalFeesCollected - contribution.FeesCollectedSnapshot; BigInteger feesToClaim; object[] notifyArgs; if (feesCollectedDuringCommitment <= 0) { notifyArgs = new object[] { "GetAvailableToClaim() No fees collected" }; feesToClaim = 0; goto ExitGetAvailableToClaim; } BigInteger contributionFeeUnitWeight = contribution.UnitsContributed * feesCollectedDuringCommitment; BigInteger feeUnitsDuringCommitment = contributionSums.TotalFeeUnits - contribution.FeeUnitsSnapshot; if (feeUnitsDuringCommitment <= 0) { notifyArgs = new object[] { "GetAvailableToClaim() No Fee Units available." }; feesToClaim = 0; goto ExitGetAvailableToClaim; } feesToClaim = feesCollectedDuringCommitment * contributionFeeUnitWeight / feeUnitsDuringCommitment; notifyArgs = new object[] { "calcAvailableToClaim", contribution.UserAddress, feesCollectedDuringCommitment, contributionFeeUnitWeight, feeUnitsDuringCommitment, feesToClaim }; ExitGetAvailableToClaim: Runtime.Notify(notifyArgs); return(feesToClaim); }
private static bool Commit(object[] args, byte[] sender) { if (args.Length != 1) { // Runtime.Notify("Commit() requires 1 param", args.Length); return(false); } object[] notifyArgs; if (VerifyOwner()) { notifyArgs = new object[] { null, "Owner can't commit", sender }; goto CommitErrorNotify; } BigInteger quantity = (BigInteger)args[0]; Contribution contribution = GetContribution(sender); if (contribution.UnitsContributed > 0) { notifyArgs = new object[] { null, "Already committed. Claim first.", sender, contribution.UnitsContributed }; goto CommitErrorNotify; } BigInteger totalFeesCollected = BigIntegerFrom8Bytes( Storage.Get(Storage.CurrentContext, FEES_POOL_KEY).Range(0, 8)); ContributionSums contributionSums = GetContributionSums(); // Update total fee units if fees have been collected since the last applied fee snapshot UpdateTotalFeeUnits(totalFeesCollected, contributionSums); // Reduce APH balance if (quantity <= 0 || ReduceBalanceOf(APH, sender, quantity) == false) { notifyArgs = new object[] { null, "Insufficient balance.", sender, quantity, GetUserValue(sender, APH).AsBigInteger() }; goto CommitErrorNotify; } // Add to the total units contributed to the pool contributionSums.TotalUnitsContributed += quantity; byte[] sumsBytes = ContributionSumsToBytes(contributionSums); Storage.Put(Storage.CurrentContext, CURRENT_CONTRIBUTIONS_KEY, sumsBytes); //Runtime.Notify("putContributionSums", sumsBytes); // Save contribution record contribution.UnitsContributed = quantity; contribution.ContributionHeight = Blockchain.GetHeight(); contribution.CompoundHeight = contribution.ContributionHeight; contribution.FeesCollectedSnapshot = totalFeesCollected; contribution.FeeUnitsSnapshot = contributionSums.TotalFeeUnits; PutContribution(contribution); Runtime.Notify("contributed", sender, contribution.UnitsContributed, contribution.ContributionHeight, contribution.FeesCollectedSnapshot, contribution.FeeUnitsSnapshot); return(true); CommitErrorNotify: notifyArgs[0] = "commitError"; Runtime.Notify(notifyArgs); return(false); }