Exemple #1
0
        public void Claim(Address from, Address stakeAddress)
        {
            Runtime.Expect(IsWitness(from), "witness failed");

            var unclaimedAmount = GetUnclaimed(stakeAddress);

            Runtime.Expect(unclaimedAmount > 0, "nothing unclaimed");

            var fuelAmount = unclaimedAmount;

            // distribute to proxy list
            var list  = _proxyStakersMap.Get <Address, StorageList>(stakeAddress);
            var count = list.Count();

            // if the transaction comes from someone other than the stake owner, must be registred in proxy list
            if (from != stakeAddress)
            {
                bool found = false;
                for (int i = 0; i < count; i++)
                {
                    var proxy = list.Get <EnergyProxy>(i);
                    if (proxy.address == from)
                    {
                        found = true;
                        break;
                    }
                }
                Runtime.Expect(found, "invalid permissions");
            }

            BigInteger sum             = 0;
            BigInteger availableAmount = fuelAmount;

            for (int i = 0; i < count; i++)
            {
                var proxy = list.Get <EnergyProxy>(i);
                sum += proxy.percentage;

                var proxyAmount = (fuelAmount * proxy.percentage) / 100;
                if (proxyAmount > 0)
                {
                    Runtime.Expect(availableAmount >= proxyAmount, "unsuficient amount for proxy distribution");
                    Runtime.Expect(Runtime.Nexus.MintTokens(Nexus.FuelTokenSymbol, this.Storage, Runtime.Chain, proxy.address, proxyAmount), "proxy fuel minting failed");
                    availableAmount -= proxyAmount;
                }
            }

            Runtime.Expect(availableAmount >= 0, "unsuficient leftovers");
            Runtime.Expect(Runtime.Nexus.MintTokens(Nexus.FuelTokenSymbol, this.Storage, Runtime.Chain, stakeAddress, availableAmount), "fuel minting failed");

            // NOTE here we set the full staked amount instead of claimed amount, to avoid infinite claims loophole
            var stake = _stakes.Get <Address, EnergyAction>(stakeAddress);

            if (stake.totalAmount == 0 && GetLastAction(stakeAddress).unclaimedPartials == 0)
            {
                _stakes.Remove(from);
            }

            var action = new EnergyAction()
            {
                unclaimedPartials = 0,
                totalAmount       = stake.totalAmount,
                timestamp         = Runtime.Time
            };

            _claims.Set <Address, EnergyAction>(stakeAddress, action);

            Runtime.Notify(EventKind.TokenClaim, from, new TokenEventData()
            {
                chainAddress = Runtime.Chain.Address, symbol = Nexus.StakingTokenSymbol, value = unclaimedAmount
            });
            Runtime.Notify(EventKind.TokenMint, stakeAddress, new TokenEventData()
            {
                chainAddress = Runtime.Chain.Address, symbol = Nexus.FuelTokenSymbol, value = fuelAmount
            });
        }
Exemple #2
0
        public BigInteger Unstake(Address from, BigInteger amount)
        {
            Runtime.Expect(IsWitness(from), "witness failed");

            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);
            }

            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 >= amount, "not enough balance");

            Runtime.Expect(stake.totalAmount >= amount, "tried to unstake more than what was staked");

            Runtime.Expect(balances.Subtract(this.Storage, Runtime.Chain.Address, amount), "balance subtract failed");
            Runtime.Expect(balances.Add(this.Storage, from, amount), "balance add failed");

            stake.totalAmount -= amount;

            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, amount);
            }

            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.MasterDemote, from, penalizationDate);
                }
            }

            Runtime.Notify(EventKind.TokenUnstake, from, new TokenEventData()
            {
                chainAddress = Runtime.Chain.Address, symbol = token.Symbol, value = amount
            });

            return(amount);
        }