/// <summary> /// set a vesting schedule, as defined in the whitepaper, for tokens purchased during the presale /// </summary> /// <param name="address"></param> /// <param name="tokenBalance"></param> /// <returns></returns> public static bool SetVestingPeriodForAddress(byte[] address, BigInteger tokensPurchased) { if (!ICOTemplate.UseTokenVestingPeriod()) { return(false); } if (address.Length != 20) { return(false); } object[] vestingOne = ICOTemplate.VestingBracketOne(); object[] vestingTwo = ICOTemplate.VestingBracketTwo(); BigInteger bracketOneThreshold = (BigInteger)vestingOne[0] * NEP5.factor; BigInteger bracketTwoThreshold = (BigInteger)vestingTwo[0] * NEP5.factor; BigInteger currentAvailableBalance = 0; // how many tokens will be immediately available to the owner uint currentTimestamp = Helpers.GetContractInitTime(); uint bracketOneReleaseDate = (uint)vestingOne[1] + currentTimestamp; uint bracketTwoReleaseDate = (uint)vestingTwo[1] + currentTimestamp; StorageMap vestingData = Storage.CurrentContext.CreateMap(StorageKeys.VestedTokenPrefix()); if (tokensPurchased > bracketTwoThreshold) { // user has purchased enough tokens to fall under the second vesting period restriction // calculate the difference between the bracketOne and bracketTwo thresholds to calculate how much should be released after bracketOne lapses BigInteger bracketOneReleaseAmount = bracketTwoThreshold - bracketOneThreshold; // the remainder will be released after the bracket two release date BigInteger bracketTwoReleaseAmount = tokensPurchased - bracketOneReleaseAmount - bracketOneThreshold; object[] lockoutTimes = new object[] { bracketOneReleaseDate, bracketOneReleaseAmount, bracketTwoReleaseDate, bracketTwoReleaseAmount }; vestingData.Put(address, lockoutTimes.Serialize()); } else { // user has purchased enough tokens to fall under the first vesting period restriction // calculate the difference between amount purchased and bracketOne threshold to calculate how much should be released after the bracketOne lapses BigInteger bracketOneReleaseAmount = tokensPurchased - bracketOneThreshold; object[] lockoutTimes = new object[] { bracketOneReleaseDate, bracketOneReleaseAmount }; vestingData.Put(address, lockoutTimes.Serialize()); } // ensure the total amount purchased is saved Helpers.SetBalanceOf(address, tokensPurchased); Helpers.SetBalanceOfVestedAmount(address, tokensPurchased); return(true); }
/// <summary> /// the core teams token allocation follow a linear quarterly maturation over 18 months beginning after 6 months /// </summary> /// <returns></returns> public static object[] GetCoreTeamVestingSchedule() { // calculate the allocation given to each team member object[] founderKeys = ICOContract.MoonlightFounderKeys(); BigInteger founderTokenAllocation = ((ICOContract.TokenMaxSupply * (BigInteger)ICOContract.MoonlightFoundersAllocationPercentage()) / 100) * NEP5.factor; BigInteger individualAllocation = founderTokenAllocation / founderKeys.Length; uint ContractInitTime = Helpers.GetContractInitTime(); // determine vesting schedule details for core teams token allocation // there will be 7 releases, one each quarter ending 2 years from contract init int numberOfTokenReleases = 7; BigInteger tokensPerRelease = individualAllocation / numberOfTokenReleases; object[] vestingPeriod = new object[14]; object[] founderReleaseSchedule = ICOContract.MoonlightFoundersAllocationReleaseSchedule(); uint initialReleaseDate = ContractInitTime + (uint)founderReleaseSchedule[0]; uint releaseFrequency = (uint)founderReleaseSchedule[1]; BigInteger tokensReleased = tokensPerRelease; // this is not the nicest way to populate the vesting schedule array, but it is much cheaper (in terms of processing/gas price) than looping vestingPeriod[0] = initialReleaseDate; vestingPeriod[1] = tokensPerRelease; // 3 months later release another batch of tokens tokensReleased += tokensPerRelease; vestingPeriod[2] = initialReleaseDate + (releaseFrequency * 1); vestingPeriod[3] = tokensPerRelease; // 3 months later release another batch of tokens tokensReleased += tokensPerRelease; vestingPeriod[4] = initialReleaseDate + (releaseFrequency * 2); vestingPeriod[5] = tokensPerRelease; // 3 months later release another batch of tokens tokensReleased += tokensPerRelease; vestingPeriod[6] = initialReleaseDate + (releaseFrequency * 3); vestingPeriod[7] = tokensPerRelease; // 3 months later release another batch of tokens tokensReleased += tokensPerRelease; vestingPeriod[8] = initialReleaseDate + (releaseFrequency * 4); vestingPeriod[9] = tokensPerRelease; // 3 months later release another batch of tokens tokensReleased += tokensPerRelease; vestingPeriod[10] = initialReleaseDate + (releaseFrequency * 5); vestingPeriod[11] = tokensPerRelease; // 3 months later release the last of the tokens vestingPeriod[12] = initialReleaseDate + (releaseFrequency * 6); vestingPeriod[13] = individualAllocation - tokensReleased; /* * Runtime.Notify("VestingSchedule", Helpers.SerializeArray(vestingPeriod)); * a serialised copy of this array ends up with values such as (dates subject to change): * 0e * 04 292cf05b 5bf02c29 1542466601 Saturday, November 17, 2018 2:56:41 PM * 07 6ddb810adb0301 0103db0a81db6d 285714285714285 * 04 0979685c 5c687909 1550350601 Saturday, February 16, 2019 8:56:41 PM * 07 dab60315b60702 0207b61503b6da 571428571428570 * 04 e9c5e05c 5ce0c5e9 1558234601 Sunday, May 19, 2019 2:56:41 AM * 07 4792851f910b03 030b911f859247 857142857142855 * 04 c912595d 5d5912c9 1566118601 Sunday, August 18, 2019 8:56:41 AM * 07 b46d072a6c0f04 040f6c2a076db4 1142857142857140 * 04 a95fd15d 5dd15fa9 1574002601 Sunday, November 17, 2019 2:56:41 PM * 07 21498934471305 05134734894921 1428571428571425 * 04 89ac495e 5e49ac89 1581886601 Sunday, February 16, 2020 8:56:41 PM * 07 8e240b3f221706 0617223f0b248e 1714285714285710 * 04 69f9c15e 5ec1f969 1589770601 Monday, May 18, 2020 2:56:41 AM * 07 00008d49fd1a07 071afd498d0000 2000000000000000 */ return(vestingPeriod); }
/// <summary> /// set a vesting schedule, as defined in the whitepaper, for tokens purchased during the presale /// </summary> /// <param name="address"></param> /// <param name="tokenBalance"></param> /// <returns></returns> public static bool SetVestingPeriodForAddress(byte[] address, string allocationType, BigInteger tokensPurchased) { if (allocationType != "incentive" && allocationType != "privateSale" && allocationType != "company") { return(false); } if (address.Length != 20) { return(false); } BigInteger currentAvailableBalance = 0; // how many tokens will be immediately available to the owner uint contractInitTime = Helpers.GetContractInitTime(); uint currentTimestamp = Helpers.GetBlockTimestamp(); StorageMap vestingData = Storage.CurrentContext.CreateMap(StorageKeys.VestedTokenPrefix()); uint initialReleaseDate = 0; uint releaseFrequency = 0; object[] vestingObj = new object[0]; if (allocationType == "incentive") { vestingObj = ICOTemplate.VestingIncentive(); initialReleaseDate = (uint)vestingObj[0] + contractInitTime; releaseFrequency = (uint)vestingObj[1]; } else if (allocationType == "privateSale") { vestingObj = ICOTemplate.VestingPrivateSale(); initialReleaseDate = contractInitTime; releaseFrequency = (uint)vestingObj[0]; } else if (allocationType == "company") { vestingObj = ICOTemplate.VestingCompany(); initialReleaseDate = (uint)vestingObj[0] + contractInitTime; releaseFrequency = (uint)vestingObj[0]; } object[] releasePeriod = new object[4]; releasePeriod[0] = initialReleaseDate; releasePeriod[1] = initialReleaseDate + releaseFrequency; releasePeriod[2] = initialReleaseDate + (releaseFrequency * 2); releasePeriod[3] = initialReleaseDate + (releaseFrequency * 3); // calculate how much should be released BigInteger releaseAmount = tokensPurchased * ICOTemplate.DistributionPercentage() / 100; object[] lockoutTimes = new object[] { releasePeriod[0], releaseAmount, releasePeriod[1], releaseAmount, releasePeriod[2], releaseAmount, releasePeriod[3], releaseAmount }; vestingData.Put(address, lockoutTimes.Serialize()); // ensure the total amount purchased is saved Helpers.SetBalanceOf(address, tokensPurchased); Helpers.SetBalanceOfVestedAmount(address, tokensPurchased, allocationType); transfer(null, address, tokensPurchased); return(true); }