/// <summary>
        /// allow allocation of presale purchases by contract administrator. this allows the nOS team to allocate the nOS tokens from the private sale, company reserve, and locked incentive reserve.
        /// This method will not allow the private allocations to exceed the defined amount
        /// the state of the `LockPrivateSaleAllocation` can be determined by the public using the method `IsPrivateSaleAllocationLocked` (returns timestamp that lock was put in place)
        /// </summary>
        /// <param name="address"></param>
        /// <param name="amountPurchased"></param>
        /// <returns></returns>
        public static bool AllocatePrivateSalePurchase(byte[] address, string allocationType, BigInteger amountPurchased)
        {
            amountPurchased = amountPurchased * NEP5.factor;

            bool privateSaleLocked = Storage.Get(Storage.CurrentContext, StorageKeys.PrivateSaleAllocationLocked()).AsBigInteger() > 0;

            if (privateSaleLocked)
            {
                Runtime.Notify("AllocatePrivateSalePurchase() privateSaleLocked, can't allocate");
                return(false);
            }

            if (allocationType != "incentive" && allocationType != "privateSale" && allocationType != "company")
            {
                return(false);
            }

            BigInteger presaleAllocationMaxValue = ICOTemplate.LockedTokenAllocationAmount() * NEP5.factor;
            BigInteger presaleAllocatedValue     = Storage.Get(Storage.CurrentContext, StorageKeys.PresaleAllocatedValue()).AsBigInteger();

            if ((presaleAllocatedValue + amountPurchased) > presaleAllocationMaxValue)
            {
                // this purchase will exceed the presale cap.. dont allow
                Runtime.Notify("AllocatePrivateSalePurchase() purchase will exceed max allocation");
                return(false);
            }

            if (!TokenSale.SetVestingPeriodForAddress(address, allocationType, amountPurchased))
            {
                Runtime.Notify("SetVestingPeriodForAddress() failed.");
                return(false);
            }

            Storage.Put(Storage.CurrentContext, StorageKeys.PresaleAllocatedValue(), presaleAllocatedValue + amountPurchased);
            transfer(null, address, amountPurchased);

            Runtime.Notify("AllocatePrivateSalePurchase() tokens allocated", address, amountPurchased, allocationType);

            return(true);
        }
        /// <summary>
        /// initialise the smart contract for use
        /// </summary>
        /// <returns></returns>
        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);
        }