internal bool MintTokens(string symbol, StorageContext storage, BalanceSheet balances, SupplySheet supply, Address target, BigInteger amount) { if (!TokenExists(symbol)) { return(false); } var tokenInfo = GetTokenInfo(symbol); if (!tokenInfo.Flags.HasFlag(TokenFlags.Fungible)) { return(false); } if (amount <= 0) { return(false); } if (tokenInfo.IsCapped) { if (!supply.Mint(amount)) { return(false); } } if (!balances.Add(storage, target, amount)) { return(false); } EditTokenSupply(symbol, amount); return(true); }
public void Unstake(Address address) { Runtime.Expect(IsValidator(address), "validator failed"); Runtime.Expect(IsWitness(address), "witness failed"); var entry = _entryMap.Get <Address, ValidatorInfo>(address); var diff = Timestamp.Now - entry.timestamp; var days = diff / 86400; // convert seconds to days Runtime.Expect(days >= 30, "waiting period required"); var stakeAmount = entry.stake; var token = Runtime.Nexus.GetTokenInfo(Nexus.StakingTokenSymbol); var balances = new BalanceSheet(token.Symbol); var balance = balances.Get(this.Storage, Runtime.Chain.Address); Runtime.Expect(balance >= stakeAmount, "not enough balance"); Runtime.Expect(balances.Subtract(this.Storage, Runtime.Chain.Address, stakeAmount), "balance subtract failed"); Runtime.Expect(balances.Add(this.Storage, address, stakeAmount), "balance add failed"); _entryMap.Remove(address); _entryList.Remove(address); Runtime.Notify(EventKind.TokenUnstake, address, new TokenEventData() { chainAddress = Runtime.Chain.Address, symbol = token.Symbol, value = stakeAmount }); }
internal bool MintTokens(string symbol, StorageContext storage, Chain chain, Address target, BigInteger amount) { if (!TokenExists(symbol)) { return(false); } var tokenInfo = GetTokenInfo(symbol); if (!tokenInfo.Flags.HasFlag(TokenFlags.Fungible)) { return(false); } if (amount <= 0) { return(false); } var supply = new SupplySheet(symbol, chain, this); if (!supply.Mint(storage, amount, tokenInfo.MaxSupply)) { return(false); } var balances = new BalanceSheet(symbol); if (!balances.Add(storage, target, amount)) { return(false); } return(true); }
public void Stake(Address from, BigInteger stakeAmount) { Runtime.Expect(stakeAmount >= MinimumValidStake, "invalid amount"); Runtime.Expect(IsWitness(from), "witness failed"); var stakeBalances = new BalanceSheet(Nexus.StakingTokenSymbol); var balance = stakeBalances.Get(this.Storage, from); var currentStake = _stakes.Get <Address, EnergyAction>(from); var newStake = stakeAmount + currentStake.totalAmount; Runtime.Expect(balance >= stakeAmount, "not enough balance"); Runtime.Expect(stakeBalances.Subtract(this.Storage, from, stakeAmount), "balance subtract failed"); Runtime.Expect(stakeBalances.Add(this.Storage, Runtime.Chain.Address, stakeAmount), "balance add failed"); var entry = new EnergyAction() { unclaimedPartials = stakeAmount + GetLastAction(from).unclaimedPartials, totalAmount = newStake, timestamp = this.Runtime.Time, }; _stakes.Set(from, entry); var logEntry = new VotingLogEntry() { timestamp = this.Runtime.Time, amount = stakeAmount }; var votingLogbook = _voteHistory.Get <Address, StorageList>(from); votingLogbook.Add(logEntry); var masterAccountThreshold = GetMasterThreshold(); if (Runtime.Nexus.GenesisAddress != from && newStake >= masterAccountThreshold && !IsMaster(from)) { var nextClaim = GetMasterClaimDate(2); _mastersList.Add(new EnergyMaster() { address = from, claimDate = nextClaim }); Runtime.Notify(EventKind.RolePromote, from, new RoleEventData() { role = "master", date = nextClaim }); } Runtime.Notify(EventKind.TokenStake, from, new TokenEventData() { chainAddress = Runtime.Chain.Address, symbol = Nexus.StakingTokenSymbol, value = stakeAmount }); }
internal bool MintTokens(RuntimeVM runtimeVM, string symbol, Address target, BigInteger amount) { if (!TokenExists(symbol)) { return(false); } var tokenInfo = GetTokenInfo(symbol); if (!tokenInfo.Flags.HasFlag(TokenFlags.Fungible)) { return(false); } if (amount <= 0) { return(false); } var supply = new SupplySheet(symbol, runtimeVM.Chain, this); if (!supply.Mint(runtimeVM.ChangeSet, amount, tokenInfo.MaxSupply)) { return(false); } var balances = new BalanceSheet(symbol); if (!balances.Add(runtimeVM.ChangeSet, target, amount)) { return(false); } var tokenTriggerResult = SmartContract.InvokeTrigger(runtimeVM, tokenInfo.Script, TokenContract.TriggerMint, target, amount); if (!tokenTriggerResult) { return(false); } var accountScript = this.LookUpAddressScript(target); var accountTriggerResult = SmartContract.InvokeTrigger(runtimeVM, accountScript, AccountContract.TriggerMint, target, amount); if (!accountTriggerResult) { return(false); } return(true); }
internal StorageMap _allowanceTargets; //<Address, Address> public void AllowGas(Address from, Address to, BigInteger price, BigInteger limit) { if (Runtime.readOnlyMode) { return; } Runtime.Expect(IsWitness(from), "invalid witness"); Runtime.Expect(from != to, "invalid gas target"); Runtime.Expect(Runtime.Chain.Address != to, "invalid gas target"); Runtime.Expect(price > 0, "price must be positive amount"); Runtime.Expect(limit > 0, "limit must be positive amount"); //var token = this.Runtime.Nexus.FuelToken; //Runtime.Expect(token != null, "invalid token"); //Runtime.Expect(token.Flags.HasFlag(TokenFlags.Fungible), "must be fungible token"); var balances = new BalanceSheet(Nexus.FuelTokenSymbol); var maxAmount = price * limit; var balance = balances.Get(this.Storage, from); Runtime.Expect(balance >= maxAmount, "not enough gas in address"); Runtime.Expect(balances.Subtract(this.Storage, from, maxAmount), "gas escrow withdraw failed"); Runtime.Expect(balances.Add(this.Storage, Runtime.Chain.Address, maxAmount), "gas escrow deposit failed"); var allowance = _allowanceMap.ContainsKey(from) ? _allowanceMap.Get <Address, BigInteger>(from) : 0; Runtime.Expect(allowance == 0, "unexpected pending allowance"); allowance += maxAmount; _allowanceMap.Set(from, allowance); _allowanceTargets.Set(from, to); Runtime.Notify(EventKind.GasEscrow, from, new GasEventData() { address = Runtime.Chain.Address, price = price, amount = limit }); }
public void TakePrivate(Address to, string symbol, uint queueID, RingSignature signature) { Runtime.Expect(Runtime.Nexus.TokenExists(symbol), "invalid token"); var tokenInfo = this.Runtime.Nexus.GetTokenInfo(symbol); Runtime.Expect(tokenInfo.Flags.HasFlag(TokenFlags.Fungible), "token must be fungible"); var queue = FindQueue(symbol, queueID); Runtime.Expect(queue.ID > 0, "invalid queue"); Runtime.Expect(queue.ID == queueID, "mismatching queue"); Runtime.Expect(queue.addresses.Count() == queue.size, "queue not full yet"); var addresses = queue.addresses.All <Address>(); foreach (var address in addresses) { Runtime.Expect(address != to, "cant send to anyone already in the queue"); } var msg = this.Runtime.Transaction.ToByteArray(false); Runtime.Expect(signature.Verify(msg, addresses), "ring signature failed"); var signatures = queue.signatures.All <Signature>(); foreach (RingSignature otherSignature in signatures) { Runtime.Expect(!signature.IsLinked(otherSignature), "ring signature already linked"); } queue.signatures.Add(signature); // TODO this is wrong var balances = new BalanceSheet(symbol); balances.Add(this.Storage, to, TransferAmount); }
internal bool TransferTokens(string symbol, StorageContext storage, Chain chain, Address source, Address destination, BigInteger amount) { if (!TokenExists(symbol)) { return(false); } var tokenInfo = GetTokenInfo(symbol); if (!tokenInfo.Flags.HasFlag(TokenFlags.Transferable)) { throw new Exception("Not transferable"); } if (!tokenInfo.Flags.HasFlag(TokenFlags.Fungible)) { throw new Exception("Should be fungible"); } if (amount <= 0) { return(false); } var balances = new BalanceSheet(symbol); if (!balances.Subtract(storage, source, amount)) { return(false); } if (!balances.Add(storage, destination, amount)) { return(false); } return(true); }
public void Stake(Address address) { Runtime.Expect(IsWitness(address), "witness failed"); var count = _entryList.Count(); var max = GetMaxValidators(); Runtime.Expect(count < max, "no open validators spots"); var stakeAmount = GetRequiredStake(); var token = Runtime.Nexus.GetTokenInfo(Nexus.StakingTokenSymbol); var balances = new BalanceSheet(token.Symbol); var balance = balances.Get(this.Storage, address); Runtime.Expect(balance >= stakeAmount, "not enough balance"); Runtime.Expect(balances.Subtract(this.Storage, address, stakeAmount), "balance subtract failed"); Runtime.Expect(balances.Add(this.Storage, Runtime.Chain.Address, stakeAmount), "balance add failed"); _entryList.Add(address); var entry = new ValidatorInfo() { address = address, stake = stakeAmount, timestamp = Runtime.Time, slashes = 0 }; _entryMap.Set(address, entry); Runtime.Notify(EventKind.TokenStake, address, new TokenEventData() { chainAddress = Runtime.Chain.Address, symbol = token.Symbol, value = stakeAmount }); }
public void SpendGas(Address from) { if (Runtime.readOnlyMode) { return; } Runtime.Expect(IsWitness(from), "invalid witness"); Runtime.Expect(_allowanceMap.ContainsKey(from), "no gas allowance found"); var availableAmount = _allowanceMap.Get <Address, BigInteger>(from); var spentGas = Runtime.UsedGas; var requiredAmount = spentGas * Runtime.GasPrice; Runtime.Expect(availableAmount >= requiredAmount, "gas allowance is not enough"); /*var token = this.Runtime.Nexus.FuelToken; * Runtime.Expect(token != null, "invalid token"); * Runtime.Expect(token.Flags.HasFlag(TokenFlags.Fungible), "must be fungible token"); */ var balances = new BalanceSheet(Nexus.FuelTokenSymbol); var leftoverAmount = availableAmount - requiredAmount; var targetAddress = _allowanceTargets.Get <Address, Address>(from); BigInteger targetGas; if (targetAddress != Address.Null) { targetGas = spentGas / 2; // 50% for dapps } else { targetGas = 0; } // return unused gas to transaction creator if (leftoverAmount > 0) { Runtime.Expect(balances.Subtract(this.Storage, Runtime.Chain.Address, leftoverAmount), "gas leftover deposit failed"); Runtime.Expect(balances.Add(this.Storage, from, leftoverAmount), "gas leftover withdraw failed"); } if (targetGas > 0) { var targetPayment = targetGas * Runtime.GasPrice; Runtime.Expect(balances.Subtract(this.Storage, Runtime.Chain.Address, targetPayment), "gas target withdraw failed"); Runtime.Expect(balances.Add(this.Storage, targetAddress, targetPayment), "gas target deposit failed"); spentGas -= targetGas; } _allowanceMap.Remove(from); _allowanceTargets.Remove(from); if (targetGas > 0) { Runtime.Notify(EventKind.GasPayment, targetAddress, new GasEventData() { address = from, price = Runtime.GasPrice, amount = targetGas }); } Runtime.Notify(EventKind.GasPayment, Runtime.Chain.Address, new GasEventData() { address = from, price = Runtime.GasPrice, amount = spentGas }); }
internal bool TransferTokens(RuntimeVM runtimeVM, string symbol, Address source, Address destination, BigInteger amount) { if (!TokenExists(symbol)) { return(false); } var tokenInfo = GetTokenInfo(symbol); if (!tokenInfo.Flags.HasFlag(TokenFlags.Transferable)) { throw new Exception("Not transferable"); } if (!tokenInfo.Flags.HasFlag(TokenFlags.Fungible)) { throw new Exception("Should be fungible"); } if (amount <= 0) { return(false); } var balances = new BalanceSheet(symbol); if (!balances.Subtract(runtimeVM.ChangeSet, source, amount)) { return(false); } if (!balances.Add(runtimeVM.ChangeSet, destination, amount)) { return(false); } var tokenTriggerResult = SmartContract.InvokeTrigger(runtimeVM, tokenInfo.Script, TokenContract.TriggerSend, source, amount); if (!tokenTriggerResult) { return(false); } tokenTriggerResult = SmartContract.InvokeTrigger(runtimeVM, tokenInfo.Script, TokenContract.TriggerReceive, destination, amount); if (!tokenTriggerResult) { return(false); } var accountScript = this.LookUpAddressScript(source); var accountTriggerResult = SmartContract.InvokeTrigger(runtimeVM, accountScript, AccountContract.TriggerSend, source, amount); if (!accountTriggerResult) { return(false); } accountScript = this.LookUpAddressScript(destination); accountTriggerResult = SmartContract.InvokeTrigger(runtimeVM, accountScript, AccountContract.TriggerSend, destination, amount); if (!accountTriggerResult) { return(false); } return(true); }
public void CloseBlock(Block block, StorageChangeSetContext storage) { var rootStorage = this.IsRoot ? storage : Nexus.RootStorage; if (block.Height > 1) { var prevBlock = GetBlockByHash(block.PreviousHash); if (prevBlock.Validator != block.Validator) { block.Notify(new Event(EventKind.ValidatorSwitch, block.Validator, "block", Serialization.Serialize(prevBlock))); } } var balance = new BalanceSheet(DomainSettings.FuelTokenSymbol); var blockAddress = Address.FromHash("block"); var totalAvailable = balance.Get(storage, blockAddress); var targets = new List <Address>(); if (Nexus.HasGenesis) { var validators = Nexus.GetValidators(); var totalValidators = Nexus.GetPrimaryValidatorCount(); for (int i = 0; i < totalValidators; i++) { var validator = validators[i]; if (validator.type != ValidatorType.Primary) { continue; } targets.Add(validator.address); } } else if (totalAvailable > 0) { targets.Add(Nexus.GetGenesisAddress(rootStorage)); } if (targets.Count > 0) { if (!balance.Subtract(storage, blockAddress, totalAvailable)) { throw new BlockGenerationException("could not subtract balance from block address"); } var amountPerValidator = totalAvailable / targets.Count; var leftOvers = totalAvailable - (amountPerValidator * targets.Count); foreach (var address in targets) { BigInteger amount = amountPerValidator; if (address == block.Validator) { amount += leftOvers; } // TODO this should use triggers when available... if (!balance.Add(storage, address, amount)) { throw new BlockGenerationException($"could not add balance to {address}"); } var eventData = Serialization.Serialize(new TokenEventData(DomainSettings.FuelTokenSymbol, amount, this.Name)); block.Notify(new Event(EventKind.TokenClaim, address, "block", eventData)); } } }
public BigInteger Unstake(Address from, BigInteger unstakeAmount) { Runtime.Expect(IsWitness(from), "witness failed"); Runtime.Expect(unstakeAmount >= MinimumValidStake, "invalid amount"); if (!_stakes.ContainsKey <Address>(from)) { return(0); } var stake = _stakes.Get <Address, EnergyAction>(from); if (stake.timestamp.Value == 0) // failsafe, should never happen { return(0); } Runtime.Expect(Runtime.Time >= stake.timestamp, "Negative time diff"); var diff = Runtime.Time - stake.timestamp; var days = diff / 86400; // convert seconds to days Runtime.Expect(days >= 1, "waiting period required"); var token = Runtime.Nexus.GetTokenInfo(Nexus.StakingTokenSymbol); var balances = new BalanceSheet(token.Symbol); var balance = balances.Get(this.Storage, Runtime.Chain.Address); Runtime.Expect(balance >= unstakeAmount, "not enough balance"); var availableStake = stake.totalAmount; availableStake -= GetStorageStake(from); Runtime.Expect(availableStake >= unstakeAmount, "tried to unstake more than what was staked"); //if this is a partial unstake if (availableStake - unstakeAmount > 0) { Runtime.Expect(availableStake - unstakeAmount >= MinimumValidStake, "leftover stake would be below minimum staking amount"); } Runtime.Expect(balances.Subtract(this.Storage, Runtime.Chain.Address, unstakeAmount), "balance subtract failed"); Runtime.Expect(balances.Add(this.Storage, from, unstakeAmount), "balance add failed"); stake.totalAmount -= unstakeAmount; var unclaimedPartials = GetLastAction(from).unclaimedPartials; if (stake.totalAmount == 0 && unclaimedPartials == 0) { _stakes.Remove(from); _voteHistory.Remove(from); } else { var entry = new EnergyAction() { unclaimedPartials = unclaimedPartials, totalAmount = stake.totalAmount, timestamp = this.Runtime.Time, }; _stakes.Set(from, entry); RemoveVotingPower(from, unstakeAmount); } if (stake.totalAmount < MasterAccountThreshold) { var count = _mastersList.Count(); var index = -1; for (int i = 0; i < count; i++) { var master = _mastersList.Get <EnergyMaster>(i); if (master.address == from) { index = i; break; } } if (index >= 0) { var penalizationDate = GetMasterClaimDateFromReference(1, _mastersList.Get <EnergyMaster>(index).claimDate); _mastersList.RemoveAt <EnergyMaster>(index); Runtime.Notify(EventKind.RoleDemote, from, new RoleEventData() { role = "master", date = penalizationDate }); } } Runtime.Notify(EventKind.TokenUnstake, from, new TokenEventData() { chainAddress = Runtime.Chain.Address, symbol = token.Symbol, value = unstakeAmount }); return(unstakeAmount); }