diff --git a/ICOTemplate/ICOTemplate.cs b/ICOTemplate/ICOTemplate.cs index 09fa416..4d3c6bc 100644 --- a/ICOTemplate/ICOTemplate.cs +++ b/ICOTemplate/ICOTemplate.cs @@ -187,7 +187,7 @@ public class ICOTemplate : Framework.SmartContract /// list NEPs supported by this contract /// /// - public static string SupportedStandards() => "{\"NEP-5\", \"NEP-10\"}"; + public static string[] SupportedStandards() => new string[] { "NEP-5", "NEP-10" }; /// /// should whitelisting of TransferFrom transfer/transferFrom methods be checked @@ -209,11 +209,14 @@ public static object Main(string operation, params object[] args) { if (Runtime.Trigger == TriggerType.Application) { - //Only allow InitSmartContract if contract not initialized and not calling whitelist/KYC operations - if(!Helpers.ContractInitialised() && ((operation != "admin" && (string) args[0] != "InitSmartContract") && operation != "AddAddress" && operation != "RevokeAddress" && operation != "GetGroupNumber" && operation != "crowdsale_status")) + + // test if a nep5 method is being invoked + foreach (string nepMethod in NEP5.GetNEP5Methods()) { - Runtime.Log("Smart Contract not Initialised"); - return false; + if (nepMethod == operation) + { + return NEP5.HandleNEP5Operation(operation, args, ExecutionEngine.CallingScriptHash, ExecutionEngine.EntryScriptHash); + } } if (operation == "admin" && Helpers.VerifyIsAdminAccount()) @@ -230,24 +233,6 @@ public static object Main(string operation, params object[] args) return false; } - // test if a nep5 method is being invoked - foreach (string nepMethod in NEP5.GetNEP5Methods()) - { - if (nepMethod == operation) - { - return NEP5.HandleNEP5Operation(operation, args, ExecutionEngine.CallingScriptHash, ExecutionEngine.EntryScriptHash); - } - } - - // test if a kyc method is being invoked - foreach (string kycMethod in KYC.GetKYCMethods()) - { - if (kycMethod == operation) - { - return KYC.HandleKYCOperation(operation, args); - } - } - // test if a helper/misc method is being invoked foreach (string helperMethod in Helpers.GetHelperMethods()) { @@ -257,16 +242,6 @@ public static object Main(string operation, params object[] args) } } - //If MintTokensEth operation - if(operation == "MintTokensEth") - { - // Method can only be called by the ETH contributions listener account - if (Helpers.VerifyWitness(ICOTemplate.EthContributionListenerKey) && Helpers.RequireArgumentLength(args,3)) - { - return EthSale.MintTokensEth((string)args[0], (byte[])args[1], (ulong)args[2]); - } - } - } else if (Runtime.Trigger == TriggerType.Verification) { @@ -274,12 +249,8 @@ public static object Main(string operation, params object[] args) { return true; } - - // test if this transaction is allowed - object[] transactionData = Helpers.GetTransactionAndSaleData(); - return TokenSale.CanUserParticipateInSale(transactionData); } - + return false; } diff --git a/ICOTemplate/ICOTemplate.csproj b/ICOTemplate/ICOTemplate.csproj index f96e6ac..c4bd5ef 100644 --- a/ICOTemplate/ICOTemplate.csproj +++ b/ICOTemplate/ICOTemplate.csproj @@ -32,8 +32,8 @@ 4 - - ..\packages\Neo.SmartContract.Framework.2.7.3\lib\net40\Neo.SmartContract.Framework.dll + + ..\packages\Neo.SmartContract.Framework.2.9.3.1\lib\net40\Neo.SmartContract.Framework.dll @@ -47,9 +47,7 @@ - - diff --git a/ICOTemplate/Token/Administration.cs b/ICOTemplate/Token/Administration.cs index b58d3c4..648ef7e 100644 --- a/ICOTemplate/Token/Administration.cs +++ b/ICOTemplate/Token/Administration.cs @@ -16,7 +16,6 @@ public class Administration : Framework.SmartContract "LockPrivateSaleAllocation", "ContractMigrate", "EnableTransferFromWhitelisting", - "InitSmartContract", "UpdateAdminAddress", "WhitelistTransferFromAdd", "WhitelistTransferFromRemove", @@ -72,8 +71,6 @@ public static object HandleAdministrationOperation(string operation, params obje return false; } return ContractMigrate(args); - case "InitSmartContract": - return InitSmartContract(); case "LockPrivateSaleAllocation": return LockPrivateSaleAllocation(); case "UpdateAdminAddress": @@ -153,51 +150,11 @@ public static bool LockPrivateSaleAllocation() /// public static bool ContractMigrate(object[] args) { - // Contract Migrate(byte[] script, byte[] parameter_list, byte return_type, bool need_storage, string name, string version, string author, string email, string description) - Contract.Migrate((byte[])args[1], (byte[])args[2], (byte)args[3], (bool)args[4], (string)args[5], (string)args[6], (string)args[7], (string)args[8], (string)args[9]); + // Contract Migrate(byte[] script, byte[] parameter_list, byte return_type, ContractPropertyState (using storage), string name, string version, string author, string email, string description) + Contract.Migrate((byte[])args[1], (byte[])args[2], (byte)args[3], (ContractPropertyState)args[4], (string)args[5], (string)args[6], (string)args[7], (string)args[8], (string)args[9]); return true; } - - /// - /// initialise the smart contract for use - /// - /// - public static bool InitSmartContract() - { - if (Helpers.ContractInitialised()) - { - // contract can only be initialised once - Runtime.Log("InitSmartContract() contract already initialised"); - return false; - } - - - uint ContractInitTime = Helpers.GetBlockTimestamp(); - Storage.Put(Storage.CurrentContext, StorageKeys.ContractInitTime(), ContractInitTime); - - // assign pre-allocated tokens to the NosProjectKey() (10,000,000 tokens) - BigInteger immediateProjectAllocationValue = ICOTemplate.ImmediateCompanyReserve() * NEP5.factor; - - - Helpers.SetBalanceOf(ICOTemplate.NosProjectKey, immediateProjectAllocationValue); - transfer(null, ICOTemplate.NosProjectKey, immediateProjectAllocationValue); - - // token allocated to private sale & vested reserves & incentives - BigInteger presaleAllocationMaxValue = ICOTemplate.LockedTokenAllocationAmount() * NEP5.factor; - - // update the total supply to reflect the project allocated tokens - BigInteger totalSupply = immediateProjectAllocationValue + presaleAllocationMaxValue; - Helpers.SetTotalSupply(totalSupply); - - UpdateAdminAddress(ICOTemplate.InitialAdminAccount); - - EnableTransferFromWhitelisting(ICOTemplate.WhitelistTransferFromListings()); - - Runtime.Log("InitSmartContract() contract initialisation complete"); - return true; - } - - + /// /// allow the contract administrator to update the admin address /// diff --git a/ICOTemplate/Token/EthSale.cs b/ICOTemplate/Token/EthSale.cs deleted file mode 100644 index 9c96e6d..0000000 --- a/ICOTemplate/Token/EthSale.cs +++ /dev/null @@ -1,121 +0,0 @@ -using Neo.SmartContract.Framework; -using Neo.SmartContract.Framework.Services.Neo; -using System; -using System.ComponentModel; -using System.Numerics; - -namespace Neo.SmartContract -{ - public class EthSale : Framework.SmartContract - { - - [DisplayName("transfer")] - public static event Action transfer; - - [DisplayName("refundEth")] - public static event Action refundEth; - - /// - /// MintTokensEth is called when a the ETH contribution listener server triggers an Ether receive event - /// - /// - public static bool MintTokensEth(string ethAddress, byte[] neoAddress, ulong ethReceived) - { - - object[] transactionData = Helpers.GetEthTransactionAndSaleData(ethReceived, ethAddress, neoAddress); - Transaction tx = (Transaction)transactionData[0]; - byte[] sender = (byte[])transactionData[1]; - byte[] receiver = (byte[])transactionData[2]; - BigInteger whiteListGroupNumber = (BigInteger)transactionData[5]; - BigInteger crowdsaleAvailableAmount = (BigInteger)transactionData[6]; - BigInteger groupMaximumContribution = (BigInteger)transactionData[7]; - BigInteger totalTokensPurchased = (BigInteger)transactionData[8] * NEP5.factor; - BigInteger totalContributionBalance = (BigInteger)transactionData[9]; - - if (!CanETHUserParticipateInSale(transactionData)) - { - refundEth(ethAddress, ethReceived); - Runtime.Notify("MintTokensEth() CanUserParticipate failed", false); - return false; - } - - if (Helpers.GetBlockTimestamp() >= ICOTemplate.PublicSaleEndTime()) - { - refundEth(ethAddress, ethReceived); - Runtime.Notify("MintTokensEth() failed. Token Sale is closed.", false); - return false; - } - - byte[] lastTransactionHash = Storage.Get(Storage.CurrentContext, StorageKeys.MintTokensEthLastTX()); - if (lastTransactionHash == tx.Hash) - { - // ensure that minTokens doesnt process the same transaction more than once - Runtime.Notify("MintTokensEth() not processing duplicate tx.Hash", tx.Hash); - return false; - } - - BigInteger tokenTotalSupply = NEP5.TotalSupply(); - - Storage.Put(Storage.CurrentContext, StorageKeys.MintTokensEthLastTX(), tx.Hash); - Runtime.Notify("MintTokensEth() receivedETH", ethReceived); - - BigInteger senderAmountSubjectToVesting = TokenSale.SubjectToVestingPeriod(sender); - BigInteger newTokenBalance = NEP5.BalanceOf(sender) + totalTokensPurchased + senderAmountSubjectToVesting; - - Helpers.SetBalanceOf(sender, newTokenBalance); - Helpers.SetBalanceOfSaleContribution(sender, totalContributionBalance); - Helpers.SetTotalSupply(totalTokensPurchased); - - transfer(null, sender, totalTokensPurchased); - return true; - } - - /// - /// determine if ETH user can participate in the token sale - /// - /// - /// - public static bool CanETHUserParticipateInSale(object[] transactionData) - { - Transaction tx = (Transaction)transactionData[0]; - byte[] sender = (byte[])transactionData[1]; - byte[] receiver = (byte[])transactionData[2]; - string ethAddress = (string)transactionData[3]; - ulong receivedETH = (ulong)transactionData[4]; - BigInteger whiteListGroupNumber = (BigInteger)transactionData[5]; - BigInteger crowdsaleAvailableAmount = (BigInteger)transactionData[6]; - BigInteger groupMaximumContribution = (BigInteger)transactionData[7]; - BigInteger totalTokensPurchased = (BigInteger)transactionData[8]; - BigInteger totalContributionBalance = (BigInteger)transactionData[9]; - - if (whiteListGroupNumber <= 0) - { - Runtime.Notify("CanUserParticipate() sender is not whitelisted", sender); - return false; - } - - if (!KYC.GroupParticipationIsUnlocked((int)whiteListGroupNumber)) - { - Runtime.Notify("CanUserParticipate() sender cannot participate yet", sender); - return false; - } - - if (crowdsaleAvailableAmount <= 0) - { - // total supply has been exhausted - Runtime.Notify("CanUserParticipate() crowdsaleAvailableAmount is <= 0", crowdsaleAvailableAmount); - return false; - } - - if (totalContributionBalance > groupMaximumContribution) - { - // don't allow this purchase exceed the group cap - Runtime.Notify("CanUserParticipate() senders purchase in ETH will exceed maxContribution cap", sender, totalContributionBalance, groupMaximumContribution); - return false; - } - - return true; - } - - } -} \ No newline at end of file diff --git a/ICOTemplate/Token/Helpers.cs b/ICOTemplate/Token/Helpers.cs index b1ba5cc..4c308c3 100644 --- a/ICOTemplate/Token/Helpers.cs +++ b/ICOTemplate/Token/Helpers.cs @@ -98,170 +98,7 @@ public static uint GetContractInitTime() { return (uint)Storage.Get(Storage.CurrentContext, StorageKeys.ContractInitTime()).AsBigInteger(); } - - /// - /// retrieve information for the received transaction - /// - /// object[] { - /// (Transaction)tx, (byte[])sender, (byte)receiver, ulong receivedNEO, ulong receivedGAS, - /// (BigInteger)whiteListGroupNumber, (BigInteger)crowdsaleAvailableAmount, (BigInteger)groupMaximumContribution - /// (BigInteger)totalTokensPurchased, (BigInteger)neoRemainingAfterPurchase, (BigInteger)gasRemainingAfterPurchase - /// (BigInteger)totalContributionBalance - /// } - /// - public static object[] GetTransactionAndSaleData() - { - Transaction tx = (Transaction)ExecutionEngine.ScriptContainer; - TransactionOutput[] inputs = tx.GetReferences(); - TransactionOutput reference = inputs[0]; - TransactionOutput[] outputs = tx.GetOutputs(); - byte[] sender = reference.ScriptHash; - byte[] receiver = ExecutionEngine.ExecutingScriptHash; - ulong receivedNEO = 0; - ulong receivedGAS = 0; - - foreach (var input in inputs) - { - // ensure that the provided inputs are valid - if (input.ScriptHash == receiver) - { - throw new System.Exception(); - } - } - - foreach (TransactionOutput output in outputs) - { - if (output.ScriptHash == receiver) - { - // only add funds to total received value if receiver is the recipient of the output - ulong receivedValue = (ulong)output.Value; - Runtime.Notify("GetTransactionData() Received Deposit type", receiver, reference.AssetId); - if (reference.AssetId == NEP5.NEO) - { - receivedNEO += receivedValue; - } - else if (reference.AssetId == NEP5.GAS) - { - receivedGAS += receivedValue; - } - } - } - - BigInteger whiteListGroupNumber = KYC.GetWhitelistGroupNumber(sender); - BigInteger crowdsaleAvailableAmount = NEP5.CrowdsaleAvailableAmount(); - BigInteger groupMaximumContribution = KYC.GetGroupMaxContribution(whiteListGroupNumber) * NEP5.factor; - - BigInteger totalTokensPurchased = 0; - BigInteger neoRemainingAfterPurchase = 0; - BigInteger gasRemainingAfterPurchase = 0; - BigInteger runningCrowdsaleAmount = crowdsaleAvailableAmount; - - if (ICOTemplate.ICOAllowsNEO() && receivedNEO > 0) - { - BigInteger neoTokenValue = receivedNEO * ICOTemplate.ICONeoToTokenExchangeRate(); - if (neoTokenValue > runningCrowdsaleAmount) - { - // the user is trying to purchase more tokens than are available - // figure out how much NOS can be purchased without exceeding the cap - neoRemainingAfterPurchase = (neoTokenValue - runningCrowdsaleAmount) / (ICOTemplate.ICONeoToTokenExchangeRate()); - totalTokensPurchased = runningCrowdsaleAmount; - } - else - { - // there is enough NOS left for this purchase to complete - totalTokensPurchased = neoTokenValue; - } - // ensure amountAvailable now reflects number of tokens purchased with NEO - runningCrowdsaleAmount -= totalTokensPurchased; - } - - if (ICOTemplate.ICOAllowsGAS() && receivedGAS > 0) - { - BigInteger gasTokenValue = receivedGAS * ICOTemplate.ICOGasToTokenExchangeRate(); - if (gasTokenValue > runningCrowdsaleAmount) - { - // the user is trying to purchase more tokens than are available - // figure out how much NOS can be purchased without exceeding the cap - gasRemainingAfterPurchase = (gasTokenValue - runningCrowdsaleAmount) / (ICOTemplate.ICOGasToTokenExchangeRate()); - totalTokensPurchased = totalTokensPurchased + runningCrowdsaleAmount; - } - else - { - totalTokensPurchased = totalTokensPurchased + gasTokenValue; - } - } - - BigInteger totalContributionBalance = BalanceOfSaleContribution(sender) + totalTokensPurchased; - - return new object[] { - tx, // neo transaction object - sender, // who initiated the transfer - receiver, // who the assets were sent to - receivedNEO, // how many neo were transferred - receivedGAS, // how many gas were transferred - whiteListGroupNumber, // what whitelist group is the sender in - crowdsaleAvailableAmount, // how many tokens are left to be purchased - groupMaximumContribution, // how many tokens can members of this whitelist group purchase - totalTokensPurchased, // the total number of tokens purchased in this transaction - neoRemainingAfterPurchase, // how much neo is left after purchase of tokens - gasRemainingAfterPurchase, // how much gas is left after purchase of tokens - totalContributionBalance // the total amount of tokens sender has purchased during public sale - }; - } - - /// - /// retrieve information for the received transaction - /// - /// object[] { - /// (Transaction)tx, (byte[])sender, (byte)receiver, ulong receivedNEO, ulong receivedGAS, - /// (BigInteger)whiteListGroupNumber, (BigInteger)crowdsaleAvailableAmount, (BigInteger)groupMaximumContribution - /// (BigInteger)totalTokensPurchased, (BigInteger)neoRemainingAfterPurchase, (BigInteger)gasRemainingAfterPurchase - /// (BigInteger)totalContributionBalance - /// } - /// - public static object[] GetEthTransactionAndSaleData(ulong receivedETH, string ethAddress, byte[] neoAddress) - { - Transaction tx = (Transaction)ExecutionEngine.ScriptContainer; - byte[] sender = neoAddress; - byte[] receiver = neoAddress; - - // only add funds to total received value if receiver is the recipient of the output - Runtime.Notify("GetEthTransactionData() Received ETH Deposit type", receiver); - - BigInteger whiteListGroupNumber = KYC.GetWhitelistGroupNumber(sender); - BigInteger crowdsaleAvailableAmount = NEP5.CrowdsaleAvailableAmount(); - BigInteger groupMaximumContribution = KYC.GetGroupMaxContribution(whiteListGroupNumber) * NEP5.factor; - - BigInteger totalTokensPurchased = 0; - - //ETH minimum must be 0.1 eth - if (ICOTemplate.ICOAllowsETH() && receivedETH >= ICOTemplate.EthMinimumContribution()) - { - //Get the amount of tokens in exchange for contributed ETH. receivedETH is with 18 decimals so divide by 1000000000000000000. - BigInteger ethTokenValue = receivedETH * ICOTemplate.ICOEthToTokenExchangeRate() / 1000000000000000000; - - // there is enough NOS left for this purchase to complete - totalTokensPurchased = ethTokenValue; - - // ensure amountAvailable now reflects number of tokens purchased with ETH - } - - BigInteger totalContributionBalance = BalanceOfSaleContribution(sender) + (totalTokensPurchased * NEP5.factor); - - return new object[] { - tx, // neo transaction object - sender, // who initiated the transfer - receiver, // who the assets were sent to - ethAddress, // ETH address of contributor - receivedETH, // how many neo were transferred - whiteListGroupNumber, // what whitelist group is the sender in - crowdsaleAvailableAmount, // how many tokens are left to be purchased - groupMaximumContribution, // how many tokens can members of this whitelist group purchase - totalTokensPurchased, // the total number of tokens purchased in this transaction - totalContributionBalance // the total amount of tokens sender has purchased during public sale - }; - } - + /// /// test if a contract address is a whitelisted TransferFrom /// diff --git a/ICOTemplate/Token/KYC.cs b/ICOTemplate/Token/KYC.cs deleted file mode 100644 index 57449c0..0000000 --- a/ICOTemplate/Token/KYC.cs +++ /dev/null @@ -1,250 +0,0 @@ -using Neo.SmartContract.Framework; -using Neo.SmartContract.Framework.Services.Neo; -using System.Numerics; - -namespace Neo.SmartContract -{ - public class KYC : Framework.SmartContract - { - public static string[] GetKYCMethods() => new string[] { - "AddAddress", - "crowdsale_status", - "GetBlockHeight", - "GetGroupMaxContribution", - "GetGroupNumber", - "GetGroupUnlockTime", - "GroupParticipationIsUnlocked", - "RevokeAddress", - }; - - public static object HandleKYCOperation(string operation, params object[] args) - { - // neo-compiler doesn't support switch blocks with too many case statements due to c# compiler optimisations - // * IL_0004 Call System.UInt32 ::ComputeStringHash(System.String) ---> System.Exception: not supported on neovm now. - // therefore, extra if statements required for more than 6 operations - if (operation == "crowdsale_status") - { - // test if an address is whitelisted - if (!Helpers.RequireArgumentLength(args, 1)) - { - return false; - } - return AddressIsWhitelisted((byte[])args[0]); - } - else if (operation == "GetGroupNumber") - { - // allow people to check which group they have been assigned to during the whitelist process - if (!Helpers.RequireArgumentLength(args, 1)) - { - return false; - } - return GetWhitelistGroupNumber((byte[])args[0]); - } - else if (operation == "GroupParticipationIsUnlocked") - { - // allow people to check if their group is unlocked (bool) - if (!Helpers.RequireArgumentLength(args, 1)) - { - return false; - } - return GroupParticipationIsUnlocked((int)args[0]); - } else if (operation == "GetBlockHeight") - { - // expose a method to retrieve current block height - return Blockchain.GetHeight(); - } - - switch (operation) - { - case "AddAddress": - // add an address to the kyc whitelist - if (!Helpers.RequireArgumentLength(args, 2)) - { - return false; - } - return AddAddress((byte[])args[0], (int)args[1]); - case "GetGroupMaxContribution": - // get the maximum amount of NOS that can be purchased for group - if (!Helpers.RequireArgumentLength(args, 1)) - { - return false; - } - return GetGroupMaxContribution((BigInteger)args[0]); - case "GetGroupUnlockTime": - // allow people to check the block height their group will be unlocked (uint) - if (!Helpers.RequireArgumentLength(args, 1)) - { - return false; - } - return GetGroupUnlockTime((BigInteger)args[0]); - case "RevokeAddress": - // remove an address to the kyc whitelist - if (!Helpers.RequireArgumentLength(args, 1)) - { - return false; - } - return RevokeAddress((byte[])args[0]); - - } - - return false; - } - - /// - /// add an address to the kyc whitelist - /// - /// - public static bool AddAddress(byte[] address, int groupNumber) - { - if (address.Length != 20 || groupNumber <= 0 || groupNumber > 4) - { - return false; - } - - if (Helpers.VerifyWitness(ICOTemplate.KycMiddlewareKey)) - { - StorageMap kycWhitelist = Storage.CurrentContext.CreateMap(StorageKeys.KYCWhitelistPrefix()); - kycWhitelist.Put(address, groupNumber); - return true; - } - return false; - } - - /// - /// determine if the given address is whitelisted by testing if group number > 0 - /// - /// - /// - public static bool AddressIsWhitelisted(byte[] address) - { - if (address.Length != 20) - { - return false; - } - - BigInteger whitelisted = GetWhitelistGroupNumber(address); - return whitelisted > 0; - } - - /// - /// get the maximum number of NOS that can be purchased by groupNumber during the public sale - /// - /// - /// - public static BigInteger GetGroupMaxContribution(BigInteger groupNumber) - { - BigInteger maxContribution = 0; - uint latestTimeStamp = Helpers.GetBlockTimestamp(); - uint publicSaleMaxContribution = (uint)ICOTemplate.MaximumContributionAmount(); - uint publicSaleEndTime = (uint)ICOTemplate.PublicSaleEndTime(); - - //If latest block timestamp is larger than presale start and smaller than presale end: check presale tier contributions. - if (latestTimeStamp >= (uint)ICOTemplate.PresaleStartTime() && latestTimeStamp <= (uint)ICOTemplate.PresaleEndTime()) - { - //Presale has not ended. Only presale can participate. - if (groupNumber == 1) - { - //Pre-sale tier 1. - maxContribution = (uint)ICOTemplate.PresaleTierOne(); - } - else if (groupNumber == 2) - { - //Pre-sale tier 2. - maxContribution = (uint)ICOTemplate.PresaleTierTwo(); - } - else if (groupNumber == 3) - { - //Pre-sale tier 3. - maxContribution = (uint)ICOTemplate.PresaleTierThree(); - } - else if(groupNumber == 4) - { - //Tier 4 - maxContribution = (uint)ICOTemplate.PresaleTierFour(); - } - } - //Otherwise we're in the public sale; get the publicSaleMaxContribution - //publicSaleMaxContribution returns the max contribution based on the presale phase using Helpers.GetPublicSaleMaxContribution() - else if (groupNumber > 0 && groupNumber <= 4 && latestTimeStamp >= (uint)ICOTemplate.PublicSaleStartTime() && latestTimeStamp <= publicSaleEndTime) - { - maxContribution = publicSaleMaxContribution; - } - - return maxContribution; - } - - /// - /// helper method to retrieve the stored group unlock block height - /// - /// - /// - public static uint GetGroupUnlockTime(BigInteger groupNumber) - { - BigInteger unlockTime = 0; - - if (groupNumber <= 0 || groupNumber > 4) - { - return 0; - } - else if (groupNumber > 0 && groupNumber <= 4) - { - unlockTime = (uint)ICOTemplate.PresaleStartTime(); - } - return (uint)unlockTime; - } - - /// - /// retrieve the group number the whitelisted address is in - /// - /// - /// - public static BigInteger GetWhitelistGroupNumber(byte[] address) - { - if (address.Length != 20) - { - return 0; - } - - StorageMap kycWhitelist = Storage.CurrentContext.CreateMap(StorageKeys.KYCWhitelistPrefix()); - return kycWhitelist.Get(address).AsBigInteger(); - } - - /// - /// determine if groupNumber is eligible to participate in public sale yet - /// - /// - /// - public static bool GroupParticipationIsUnlocked(int groupNumber) - { - if (groupNumber <= 0) - { - return false; - } - - uint unlockBlockTime = GetGroupUnlockTime(groupNumber); - return unlockBlockTime > 0 && unlockBlockTime <= Helpers.GetBlockTimestamp(); - } - - /// - /// remove an address from the whitelist - /// - /// - public static bool RevokeAddress(byte[] address) - { - if (address.Length != 20) - { - return false; - } - - if (Helpers.VerifyWitness(ICOTemplate.KycMiddlewareKey)) - { - StorageMap kycWhitelist = Storage.CurrentContext.CreateMap(StorageKeys.KYCWhitelistPrefix()); - kycWhitelist.Delete(address); - return true; - } - return false; - } - - - } -} diff --git a/ICOTemplate/Token/NEP5.cs b/ICOTemplate/Token/NEP5.cs index 5bc1d88..7a98d12 100644 --- a/ICOTemplate/Token/NEP5.cs +++ b/ICOTemplate/Token/NEP5.cs @@ -1,6 +1,5 @@ using Neo.SmartContract.Framework; using Neo.SmartContract.Framework.Services.Neo; -using Neo.SmartContract.Framework.Services.System; using System; using System.ComponentModel; using System.Numerics; @@ -9,10 +8,6 @@ namespace Neo.SmartContract { public class NEP5 : Framework.SmartContract { - // define the global system assets NEO/GAS - public static readonly byte[] NEO = { 155, 124, 255, 218, 166, 116, 190, 174, 15, 147, 14, 190, 96, 133, 175, 144, 147, 229, 254, 86, 179, 74, 92, 34, 12, 205, 207, 110, 252, 51, 111, 197 }; - public static readonly byte[] GAS = { 231, 45, 40, 105, 121, 238, 108, 177, 183, 230, 93, 253, 223, 178, 227, 132, 16, 11, 141, 20, 142, 119, 88, 222, 66, 228, 22, 139, 113, 121, 44, 96 }; - /// /// NEP5.1 definition: number of decimals for this token - probably best to leave this as-is /// @@ -34,8 +29,7 @@ public class NEP5 : Framework.SmartContract /// public static string[] GetNEP5Methods() => new string[] { "name", "symbol", "decimals", "totalSupply", "balanceOf", - "transfer", "transferFrom", "approve", "allowance", - "crowdsale_available_amount", "mintTokens" + "transfer", "transferFrom", "approve", "allowance" }; [DisplayName("transfer")] @@ -129,16 +123,6 @@ public static object HandleNEP5Operation(string operation, object[] args, byte[] return Allowance((byte[])args[0], (byte[])args[1]); } - // check how many tokens left for purchase - if (operation == "crowdsale_available_amount") - { - return CrowdsaleAvailableAmount(); - } - - if (operation == "mintTokens") - { - return TokenSale.MintTokens(); - } return false; } @@ -264,12 +248,6 @@ public static BigInteger TotalSupply() /// public static bool Transfer(byte[] from, byte[] to, BigInteger amount, byte[] caller, byte[] entry) { - if(Helpers.GetBlockTimestamp() < ICOTemplate.PublicSaleEndTime()) - { - Runtime.Log("Transfer() not available before ICOTemplate.PublicSaleEndTime()"); - return false; - } - if (caller != entry && !Helpers.IsContractWhitelistedTransferFrom(caller)) { from = caller; @@ -334,12 +312,6 @@ public static bool Transfer(byte[] from, byte[] to, BigInteger amount, byte[] ca /// public static bool TransferFrom(byte[] originator, byte[] from, byte[] to, BigInteger amount, byte[] caller, byte[] entry) { - if (Helpers.GetBlockTimestamp() < ICOTemplate.PublicSaleEndTime()) - { - Runtime.Log("TransferFrom() not available before ICOTemplate.PublicSaleEndTime()"); - return false; - } - if (caller != entry && !Helpers.IsContractWhitelistedTransferFrom(caller)) { originator = caller; @@ -389,7 +361,7 @@ public static bool TransferFrom(byte[] originator, byte[] from, byte[] to, BigIn BigInteger newBalance = fromBalance - amount; Helpers.SetBalanceOf(from, newBalance + senderAmountSubjectToVesting); // remove balance from originating account Helpers.SetBalanceOf(to, recipientBalance + recipientAmountSubjectToVesting + amount); // set new balance for destination account - Helpers.SetAllowanceAmount(from.Concat(originator), approvedTransferAmount - amount); // deduct transferred amount from allowance + Helpers.SetAllowanceAmount(from.Concat(to), approvedTransferAmount - amount); // deduct transferred amount from allowance transfer(from, to, amount); return true; diff --git a/ICOTemplate/Token/TokenSale.cs b/ICOTemplate/Token/TokenSale.cs index 5fbc546..811a3e4 100644 --- a/ICOTemplate/Token/TokenSale.cs +++ b/ICOTemplate/Token/TokenSale.cs @@ -9,126 +9,10 @@ namespace Neo.SmartContract { public class TokenSale : Framework.SmartContract { - [DisplayName("refund")] - public static event Action refund; - - - [DisplayName("transfer")] public static event Action transfer; - - /// - /// determine if user can participate in the token sale yet - /// - /// - /// - public static bool CanUserParticipateInSale(object[] transactionData) - { - Transaction tx = (Transaction)transactionData[0]; - byte[] sender = (byte[])transactionData[1]; - byte[] receiver = (byte[])transactionData[2]; - ulong receivedNEO = (ulong)transactionData[3]; - ulong receivedGAS = (ulong)transactionData[4]; - BigInteger whiteListGroupNumber = (BigInteger)transactionData[5]; - BigInteger crowdsaleAvailableAmount = (BigInteger)transactionData[6]; - BigInteger groupMaximumContribution = (BigInteger)transactionData[7]; - BigInteger totalTokensPurchased = (BigInteger)transactionData[8]; - BigInteger neoRemainingAfterPurchase = (BigInteger)transactionData[9]; - BigInteger gasRemainingAfterPurchase = (BigInteger)transactionData[10]; - BigInteger totalContributionBalance = (BigInteger)transactionData[11]; - - if (whiteListGroupNumber <= 0) - { - Runtime.Notify("CanUserParticipate() sender is not whitelisted", sender); - return false; - } - - if (!KYC.GroupParticipationIsUnlocked((int)whiteListGroupNumber)) - { - Runtime.Notify("CanUserParticipate() sender cannot participate yet", sender); - return false; - } - - if (crowdsaleAvailableAmount <= 0) - { - // total supply has been exhausted - Runtime.Notify("CanUserParticipate() crowdsaleAvailableAmount is <= 0", crowdsaleAvailableAmount); - return false; - } - - if (totalContributionBalance > groupMaximumContribution) - { - // don't allow this purchase exceed the group cap - Runtime.Notify("CanUserParticipate() senders purchase will exceed maxContribution cap", sender, totalContributionBalance, groupMaximumContribution); - refund(sender, receivedNEO, receivedGAS); - return false; - } - - return true; - } - + - - /// - /// mint tokens is called when a user wishes to purchase tokens - /// - /// - public static bool MintTokens() - { - object[] transactionData = Helpers.GetTransactionAndSaleData(); - Transaction tx = (Transaction)transactionData[0]; - byte[] sender = (byte[])transactionData[1]; - byte[] receiver = (byte[])transactionData[2]; - ulong receivedNEO = (ulong)transactionData[3]; - ulong receivedGAS = (ulong)transactionData[4]; - BigInteger whiteListGroupNumber = (BigInteger)transactionData[5]; - BigInteger crowdsaleAvailableAmount = (BigInteger)transactionData[6]; - BigInteger groupMaximumContribution = (BigInteger)transactionData[7]; - BigInteger totalTokensPurchased = (BigInteger)transactionData[8]; - BigInteger neoRemainingAfterPurchase = (BigInteger)transactionData[9]; - BigInteger gasRemainingAfterPurchase = (BigInteger)transactionData[10]; - BigInteger totalContributionBalance = (BigInteger)transactionData[11]; - - if (Helpers.GetBlockTimestamp() >= ICOTemplate.PublicSaleEndTime()) - { - Runtime.Notify("MintTokens() failed. Token Sale is closed.", false); - return false; - } - - if (!CanUserParticipateInSale(transactionData)) - { - Runtime.Notify("MintTokens() CanUserParticipate failed", false); - return false; - } - - byte[] lastTransactionHash = Storage.Get(Storage.CurrentContext, StorageKeys.MintTokensLastTX()); - if (lastTransactionHash == tx.Hash) - { - // ensure that minTokens doesnt process the same transaction more than once - Runtime.Notify("MintTokens() not processing duplicate tx.Hash", tx.Hash); - return false; - } - - Storage.Put(Storage.CurrentContext, StorageKeys.MintTokensLastTX(), tx.Hash); - Runtime.Notify("MintTokens() receivedNEO / receivedGAS", receivedNEO, receivedGAS); - - if (neoRemainingAfterPurchase > 0 || gasRemainingAfterPurchase > 0) - { - // this purchase would have exceed the allowed max supply so we spent what we could and will refund the remainder - refund(sender, neoRemainingAfterPurchase, gasRemainingAfterPurchase); - } - - BigInteger senderAmountSubjectToVesting = SubjectToVestingPeriod(sender); - BigInteger newTokenBalance = NEP5.BalanceOf(sender) + totalTokensPurchased + senderAmountSubjectToVesting; - - Helpers.SetBalanceOf(sender, newTokenBalance); - Helpers.SetBalanceOfSaleContribution(sender, totalContributionBalance); - Helpers.SetTotalSupply(totalTokensPurchased); - - transfer(null, sender, totalTokensPurchased); - return true; - } - /// /// set a vesting schedule, as defined in the whitepaper, for tokens purchased during the presale /// diff --git a/ICOTemplate/packages.config b/ICOTemplate/packages.config index eb70bff..93b0079 100644 --- a/ICOTemplate/packages.config +++ b/ICOTemplate/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file diff --git a/packages/Neo.SmartContract.Framework.2.7.3/.signature.p7s b/packages/Neo.SmartContract.Framework.2.7.3/.signature.p7s new file mode 100644 index 0000000..1a647b1 Binary files /dev/null and b/packages/Neo.SmartContract.Framework.2.7.3/.signature.p7s differ diff --git a/packages/Neo.SmartContract.Framework.2.7.3/Neo.SmartContract.Framework.2.7.3.nupkg b/packages/Neo.SmartContract.Framework.2.7.3/Neo.SmartContract.Framework.2.7.3.nupkg new file mode 100644 index 0000000..d0c2b59 Binary files /dev/null and b/packages/Neo.SmartContract.Framework.2.7.3/Neo.SmartContract.Framework.2.7.3.nupkg differ diff --git a/packages/Neo.SmartContract.Framework.2.7.3/lib/net40/Neo.SmartContract.Framework.dll b/packages/Neo.SmartContract.Framework.2.7.3/lib/net40/Neo.SmartContract.Framework.dll new file mode 100644 index 0000000..a68d531 Binary files /dev/null and b/packages/Neo.SmartContract.Framework.2.7.3/lib/net40/Neo.SmartContract.Framework.dll differ diff --git a/packages/Neo.SmartContract.Framework.2.7.3/lib/netstandard1.6/Neo.SmartContract.Framework.dll b/packages/Neo.SmartContract.Framework.2.7.3/lib/netstandard1.6/Neo.SmartContract.Framework.dll new file mode 100644 index 0000000..ea6c1c3 Binary files /dev/null and b/packages/Neo.SmartContract.Framework.2.7.3/lib/netstandard1.6/Neo.SmartContract.Framework.dll differ diff --git a/packages/Neo.SmartContract.Framework.2.9.3.1/.signature.p7s b/packages/Neo.SmartContract.Framework.2.9.3.1/.signature.p7s new file mode 100644 index 0000000..8b20d32 Binary files /dev/null and b/packages/Neo.SmartContract.Framework.2.9.3.1/.signature.p7s differ diff --git a/packages/Neo.SmartContract.Framework.2.9.3.1/Neo.SmartContract.Framework.2.9.3.1.nupkg b/packages/Neo.SmartContract.Framework.2.9.3.1/Neo.SmartContract.Framework.2.9.3.1.nupkg new file mode 100644 index 0000000..ef9de24 Binary files /dev/null and b/packages/Neo.SmartContract.Framework.2.9.3.1/Neo.SmartContract.Framework.2.9.3.1.nupkg differ diff --git a/packages/Neo.SmartContract.Framework.2.9.3.1/lib/net40/Neo.SmartContract.Framework.dll b/packages/Neo.SmartContract.Framework.2.9.3.1/lib/net40/Neo.SmartContract.Framework.dll new file mode 100644 index 0000000..8dea8d6 Binary files /dev/null and b/packages/Neo.SmartContract.Framework.2.9.3.1/lib/net40/Neo.SmartContract.Framework.dll differ diff --git a/packages/Neo.SmartContract.Framework.2.9.3.1/lib/netstandard1.6/Neo.SmartContract.Framework.dll b/packages/Neo.SmartContract.Framework.2.9.3.1/lib/netstandard1.6/Neo.SmartContract.Framework.dll new file mode 100644 index 0000000..f30ac97 Binary files /dev/null and b/packages/Neo.SmartContract.Framework.2.9.3.1/lib/netstandard1.6/Neo.SmartContract.Framework.dll differ