/// <summary> /// allow allocation of presale purchases by contract administrator. this allows the moonlight team to allocate the 25% of LX tokens sold in the private presale. /// as we accepted ETH in addition to NEO&GAS, using a mintTokens method here is not practical. /// 1. this method will not allow the presale allocation to exceed the defined amount /// 2. this method is permanently disabled once the method `LockPresaleAllocation` has been called. /// 3. the state of the `LockPresaleAllocation` can be determined by the public using the method `IsPresaleAllocationLocked` (returns timestamp that lock was put in place) /// </summary> /// <param name="address"></param> /// <param name="amountPurchased"></param> /// <returns></returns> public static bool AllocatePresalePurchase(byte[] address, BigInteger amountPurchased) { bool presaleLocked = Storage.Get(Storage.CurrentContext, StorageKeys.PresaleAllocationLocked()).AsBigInteger() > 0; if (presaleLocked) { Runtime.Notify("AllocatePresalePurchase() presaleLocked, can't allocate"); return(false); } BigInteger presaleAllocationMaxValue = ((ICOContract.TokenMaxSupply * (BigInteger)ICOContract.PresaleAllocationPercentage()) / 100) * 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("AllocatePresalePurchase() purchase will exceed presale max allocation"); return(false); } TokenSale.SetVestingPeriodForAddress(address, amountPurchased); Storage.Put(Storage.CurrentContext, StorageKeys.PresaleAllocatedValue(), presaleAllocatedValue + amountPurchased); Runtime.Notify("AllocatePresalePurchase() tokens allocated", address, amountPurchased); return(true); }
/// <summary> /// Claims unsold tokens /// </summary> /// <returns></returns> public static bool ClaimUnsoldTokens() { bool UnsoldTokensClaimed = Storage.Get(Storage.CurrentContext, StorageKeys.UnsoldTokensClaimed()).AsString() == "1"; //This method can only be executed by the admin account, after the public sale, and can only be called once (use UnsoldTokensClaimed() storage item) if (Helpers.GetBlockTimestamp() >= ICOTemplate.PublicSaleEndTime() && UnsoldTokensClaimed == false && Helpers.VerifyIsAdminAccount()) { byte[] address = ICOTemplate.AdditionalCompanyTokenFund; //Get amount remaining BigInteger amountRemaining = NEP5.CrowdsaleAvailableAmount(); //Add vested amount to account TokenSale.SetVestingPeriodForAddress(address, "company", amountRemaining); //Set total supply Helpers.SetTotalSupply(amountRemaining); //Set the UnsoldTokensClaimed() storage item so ClaimUnsoldTokens() cannot be called again Storage.Put(Storage.CurrentContext, StorageKeys.UnsoldTokensClaimed(), "1"); transfer(null, address, amountRemaining); Runtime.Notify("ClaimUnsoldTokens() tokens allocated", address, amountRemaining); return(true); } return(false); }
///<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> /// transfer an amount from the "from" account to the "to" acount if the "spender" has been approved to transfer the requested amount. /// </summary> /// <param name="spender"></param> /// <param name="from"></param> /// <param name="to"></param> /// <param name="amount"></param> /// <param name="caller"></param> /// <param name="entry"></param> /// <returns></returns> public static bool TransferFrom(byte[] spender, byte[] from, byte[] to, BigInteger amount, byte[] caller, byte[] entry) { if (caller != entry && !Helpers.IsContractWhitelistedDEX(caller)) { spender = caller; } if (spender.Length != 20 || from.Length != 20 || to.Length != 20) { Runtime.Log("TransferFrom() (originator|from|to).Length != 20"); return(false); } if (amount < 0 || from == to) { Runtime.Log("TransferFrom() invalid transfer amount must be >= 0"); throw new Exception(); } BigInteger approvedTransferAmount = Allowance(from, spender); // how many tokens is the spender authorised to transfer from the "from" account if (!Runtime.CheckWitness(spender)) { // ensure transaction is signed properly by the request originator Runtime.Log("TransferFrom() CheckWitness failed"); return(false); } //At this point, vesting is complete for all but founders and project. //Check if vesting applies and resolve. BigInteger recipientAmountSubjectToVesting = 0; BigInteger senderAmountSubjectToVesting = 0; // reduce gas usage by only checking vesting criteria if the from address is a project or founders key if (Helpers.IsProjectKey(from)) { senderAmountSubjectToVesting = TokenSale.SubjectToVestingPeriod(from); } BigInteger fromBalance = BalanceOfRaw(from); // retrieve balance of authorised account if (approvedTransferAmount < amount || fromBalance - senderAmountSubjectToVesting < amount) { // don't transfer if funds not available Runtime.Notify("TransferFrom() (authorisedAmount|fromBalance) < transferValue", approvedTransferAmount, fromBalance, amount); return(false); } BigInteger recipientBalance = BalanceOfRaw(to); BigInteger newBalance = fromBalance - amount; Helpers.SetBalanceOf(from, newBalance); // remove balance from originating account Helpers.SetBalanceOf(to, recipientBalance + amount); // set new balance for destination account Helpers.SetAllowanceAmount(from.Concat(spender), approvedTransferAmount - amount); // deduct transferred amount from allowance transfer(from, to, amount); return(true); }
/// <summary> /// transfer an amount from the "from" account to the "to" acount if the "spender" has been approved to transfer the requested amount. /// </summary> /// <param name="spender"></param> /// <param name="from"></param> /// <param name="to"></param> /// <param name="amount"></param> /// <param name="caller"></param> /// <param name="entry"></param> /// <returns></returns> public static bool TransferFrom(byte[] spender, byte[] from, byte[] to, BigInteger amount, byte[] caller, byte[] entry) { if (caller != entry && !Helpers.IsContractWhitelistedDEX(caller)) { spender = caller; } if (spender.Length != 20 || from.Length != 20 || to.Length != 20) { Runtime.Log("TransferFrom() (spender|from|to).Length != 20"); return(false); } if (amount < 0) { Runtime.Log("TransferFrom() invalid transfer amount must be >= 0"); throw new Exception(); } BigInteger approvedTransferAmount = Allowance(from, spender); // how many tokens is this address authorised to transfer BigInteger fromBalance = BalanceOf(from); // retrieve balance of authorised account if (approvedTransferAmount < amount || fromBalance < amount) { // don't transfer if funds not available Runtime.Notify("TransferFrom() (authorisedAmount|fromBalance) < transferValue", approvedTransferAmount, fromBalance, amount); return(false); } BigInteger senderAmountSubjectToVesting = TokenSale.SubjectToVestingPeriod(from); if (amount == 0 || from == to || fromBalance - senderAmountSubjectToVesting < amount) { // don't accept a meaningless value Runtime.Log("TransferFrom() empty transfer amount or from==to"); transfer(from, to, amount); return(true); // as per nep5 standard - return true when amount is 0 or from == to } if (!Runtime.CheckWitness(spender)) { // ensure transaction is signed properly by the spender Runtime.Log("TransferFrom() CheckWitness failed"); return(false); } BigInteger recipientBalance = BalanceOf(to); BigInteger recipientAmountSubjectToVesting = TokenSale.SubjectToVestingPeriod(to); 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(spender), approvedTransferAmount - amount); // deduct transferred amount from allowance transfer(from, to, amount); return(true); }
/// <summary> /// NEP5: Transfer tokens from one account to another /// </summary> /// <param name="from">sender address</param> /// <param name="to">recipient address</param> /// <param name="amount">number of tokens to transfer</param> /// <param name="caller"></param> /// <param name="entry"></param> /// <returns></returns> public static bool Transfer(byte[] from, byte[] to, BigInteger amount, byte[] caller, byte[] entry) { if (caller != entry && !Helpers.IsContractWhitelistedDEX(caller)) { from = caller; } if (from.Length != 20 || to.Length != 20) { Runtime.Log("Transfer() (from|to).Length != 20"); return(false); } if (amount < 0 || from == to) { Runtime.Log("Transfer() invalid transfer amount must be >= 0"); throw new Exception(); } if (!Runtime.CheckWitness(from)) { // ensure transaction is signed properly by the owner of the tokens Runtime.Log("Transfer() CheckWitness failed"); return(false); } //At this point, vesting is complete for all but founders and project. //Check if vesting applies and resolve. BigInteger recipientAmountSubjectToVesting = 0; BigInteger senderAmountSubjectToVesting = 0; // reduce gas usage by only checking vesting criteria if the from address is a project or founders key if (Helpers.IsProjectKey(from)) { senderAmountSubjectToVesting = TokenSale.SubjectToVestingPeriod(from); } BigInteger fromBalance = BalanceOfRaw(from); // retrieve balance of originating account if (fromBalance - senderAmountSubjectToVesting < amount) { Runtime.Log("Transfer() fromBalance < transferValue"); // don't transfer if funds not available return(false); } BigInteger recipientBalance = BalanceOfRaw(to); BigInteger newBalance = fromBalance - amount; Helpers.SetBalanceOf(from, newBalance); // remove balance from originating account Helpers.SetBalanceOf(to, recipientBalance + amount); // set new balance for destination account transfer(from, to, amount); return(true); }
public static bool Transfer(byte[] from, byte[] to, BigInteger amount, byte[] caller, byte[] entry) { if (caller != entry && !Helpers.IsContractWhitelistedDEX(caller)) { from = caller; } if (from.Length != 20 || to.Length != 20) { Runtime.Log("Transfer() (from|to).Length != 20"); return(false); } if (amount < 0) { Runtime.Log("Transfer() invalid transfer amount must be >= 0"); throw new Exception(); } BigInteger fromBalance = BalanceOf(from); // retrieve balance of originating account if (fromBalance < amount) { Runtime.Log("Transfer() fromBalance < transferValue"); // don't transfer if funds not available return(false); } if (amount == 0 || from == to) { // don't accept a meaningless value Runtime.Log("Transfer() empty transfer amount or from==to"); transfer(from, to, amount); return(true); // as per nep5 standard - return true when amount is 0 or from == to } if (!Runtime.CheckWitness(from)) { // ensure transaction is signed properly by the owner of the tokens Runtime.Log("Transfer() CheckWitness failed"); return(false); } BigInteger recipientBalance = BalanceOf(to); BigInteger recipientAmountSubjectToVesting = TokenSale.SubjectToVestingPeriod(to); BigInteger senderAmountSubjectToVesting = TokenSale.SubjectToVestingPeriod(from); 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 transfer(from, to, amount); return(true); }
/// <summary> /// MintTokensEth is called when a the ETH contribution listener server triggers an Ether receive event /// </summary> /// <returns></returns> 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); }
public static BigInteger BalanceOf(byte[] account) { if (account.Length != 20) { Runtime.Log("BalanceOf() invalid address supplied"); return(0); } StorageMap balances = Storage.CurrentContext.CreateMap(StorageKeys.BalancePrefix()); BigInteger amountSubjectToVesting = TokenSale.SubjectToVestingPeriod(account); BigInteger userBalance = balances.Get(account).AsBigInteger() - amountSubjectToVesting; if (userBalance < 0) { userBalance = 0; } return(userBalance.AsByteArray().Concat(new byte[] { }).AsBigInteger()); }
/// <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); }
///<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; }
public static object HandleNEP5Operation(string operation, object[] args, byte[] caller, byte[] entry) { //{ "name", "symbol", "decimals", "totalSupply", "balanceOf", "transfer", "transferFrom", "approve", "allowance" }; if (operation == "name") { // the name of the token return(DIVE.DIVE.name()); } if (operation == "symbol") { // the symbol of the token return(DIVE.DIVE.symbol()); } if (operation == "decimals") { // decimals to determine fractions of tokens return(DIVE.DIVE.decimals()); } if (operation == "totalSupply") { // the total number of tokens minted return(TotalSupply()); } if (operation == "balanceOf") { // retreive the balance of an address if (!Helpers.RequireArgumentLength(args, 1)) { // BalanceOf() requires at least 1 argument - the address to check the balance of return(false); } return(BalanceOf((byte[])args[0])); } if (operation == "transfer") { // transfer tokens from one address to another if (!Helpers.RequireArgumentLength(args, 3)) { // Transfer() requires 3 arguments: from, to, amount return(false); } return(Transfer((byte[])args[0], (byte[])args[1], (BigInteger)args[2], caller, entry)); } if (operation == "transferFrom") { // transfer tokens from one address to another if (!Helpers.RequireArgumentLength(args, 4)) { // TransferFrom() requires 4 arguments: originator, from, to, amount return(false); } return(TransferFrom((byte[])args[0], (byte[])args[1], (byte[])args[2], (BigInteger)args[3], caller, entry)); } if (operation == "approve") { // approve a third party to transfer tokens from one address to another if (!Helpers.RequireArgumentLength(args, 3)) { // Approve() requires 3 arguments: originator, to, amount return(false); } return(Approve((byte[])args[0], (byte[])args[1], (BigInteger)args[2], caller, entry)); } if (operation == "allowance") { // retreive the authorised balance of an address if (!Helpers.RequireArgumentLength(args, 2)) { // Allowance() requires 2 arguments: from, to return(false); } 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); }
public static object HandleAdministrationOperation(string operation, params object[] args) { if (operation == "WhitelistTransferFromRemove") { if (!Helpers.RequireArgumentLength(args, 2)) { return(false); } return(WhitelistTransferFromRemove((byte[])args[1])); } else if (operation == "WhitelistTransferFromAdd") { if (!Helpers.RequireArgumentLength(args, 2)) { return(false); } return(WhitelistTransferFromAdd((byte[])args[1])); } else if (operation == "EnableTransferFromWhitelisting") { if (!Helpers.RequireArgumentLength(args, 2)) { return(false); } EnableTransferFromWhitelisting((bool)args[1]); } else if (operation == "ClaimUnsoldTokens") { if (!Helpers.RequireArgumentLength(args, 1)) { return(false); } TokenSale.ClaimUnsoldTokens(); } switch (operation) { case "AllocatePrivateSalePurchase": if (!Helpers.RequireArgumentLength(args, 4)) { return(false); } return(AllocatePrivateSalePurchase((byte[])args[1], (string)args[2], (BigInteger)args[3])); case "ContractMigrate": if (!Helpers.RequireArgumentLength(args, 10)) { return(false); } return(ContractMigrate(args)); case "InitSmartContract": return(InitSmartContract()); case "LockPrivateSaleAllocation": return(LockPrivateSaleAllocation()); case "UpdateAdminAddress": if (!Helpers.RequireArgumentLength(args, 2)) { return(false); } return(UpdateAdminAddress((byte[])args[1])); } return(false); }