///<remarks> /// END user configurable fields ///</remarks> /// <summary> /// the entry point for smart contract execution /// </summary> /// <param name="operation">string to determine execution operation performed</param> /// <param name="args">optional arguments, context specific depending on operation</param> /// <returns></returns> public static object Main(string operation, params object[] args) { if (Runtime.Trigger == TriggerType.Application) { // 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()) { if (helperMethod == operation) { return(Helpers.HandleHelperOperation(operation, args)); } } if (operation == "admin" && Helpers.VerifyIsAdminAccount()) { // allow access to administration methods string adminOperation = (string)args[0]; foreach (string adminMethod in Administration.GetAdministrationMethods()) { if (adminMethod == adminOperation) { return(Administration.HandleAdministrationOperation(adminOperation, args)); } } return(false); } } else if (Runtime.Trigger == TriggerType.Verification) { if (Helpers.VerifyIsAdminAccount()) { return(true); } // test if this transaction is allowed object[] transactionData = Helpers.GetTransactionAndSaleData(); return(TokenSale.CanUserParticipateInSale(transactionData)); } return(false); }
/// <summary> /// determine if user can participate in the token sale yet /// </summary> /// <param name="sender"></param> /// <returns></returns> 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); }
/// <summary> /// retrieve information for the received transaction /// </summary> /// <returns>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 /// } /// </returns> 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 }); }
/// <summary> /// retrieve information for the received transaction /// </summary> /// <returns>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 /// } /// </returns> 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 }); }
///<remarks> /// END user configurable fields ///</remarks> /// <summary> /// the entry point for smart contract execution /// </summary> /// <param name="operation">string to determine execution operation performed</param> /// <param name="args">optional arguments, context specific depending on operation</param> /// <returns></returns> 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")) { Runtime.Log("Smart Contract not Initialised"); return false; } if (operation == "admin" && Helpers.VerifyIsAdminAccount()) { // allow access to administration methods string adminOperation = (string)args[0]; foreach (string adminMethod in Administration.GetAdministrationMethods()) { if (adminMethod == adminOperation) { return Administration.HandleAdministrationOperation(adminOperation, 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()) { if (helperMethod == operation) { return Helpers.HandleHelperOperation(operation, 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) { if (Helpers.VerifyIsAdminAccount()) { return true; } // test if this transaction is allowed object[] transactionData = Helpers.GetTransactionAndSaleData(); return TokenSale.CanUserParticipateInSale(transactionData); } return false; }