public void ExecuteThrowNotEnoughFungibleAssetValueException()
        {
            var previousWeeklyArenaState = _initialState.GetWeeklyArenaState(_weeklyArenaAddress);
            var arenaInfo = previousWeeklyArenaState.GetArenaInfo(_avatar1Address);

            previousWeeklyArenaState.Update(new ArenaInfo(arenaInfo));

            var previousState = _initialState.SetState(
                _weeklyArenaAddress,
                previousWeeklyArenaState.Serialize());

            var goldCurrency           = new Currency("NCG", 2, Addresses.GoldCurrency);
            var previousAgentGoldState = _initialState.GetBalance(
                _agent1Address,
                goldCurrency);

            if (previousAgentGoldState.Sign > 0)
            {
                previousState = _initialState.TransferAsset(
                    _agent1Address,
                    Addresses.GoldCurrency,
                    previousAgentGoldState);
            }

            var action = new RankingBattle
            {
                AvatarAddress      = _avatar1Address,
                EnemyAddress       = _avatar2Address,
                WeeklyArenaAddress = _weeklyArenaAddress,
                costumeIds         = new List <int>(),
                equipmentIds       = new List <Guid>(),
                consumableIds      = new List <Guid>(),
            };

            Assert.Throws <NotEnoughFungibleAssetValueException>(() =>
            {
                action.Execute(new ActionContext()
                {
                    PreviousStates = previousState,
                    Signer         = _agent1Address,
                    Random         = new TestRandom(),
                    Rehearsal      = false,
                });
            });
        }
        public void Execute(int rewardLevel, int prevRewardLevel, int collectionLevel)
        {
            Address collectionAddress = MonsterCollectionState0.DeriveAddress(_signer, 0);
            List <MonsterCollectionRewardSheet.RewardInfo> rewards = _tableSheets.MonsterCollectionRewardSheet[1].Rewards;
            MonsterCollectionState0 monsterCollectionState         = new MonsterCollectionState0(collectionAddress, 1, 0, _tableSheets.MonsterCollectionRewardSheet);

            for (int i = 0; i < prevRewardLevel; i++)
            {
                int level = i + 1;
                MonsterCollectionResult result = new MonsterCollectionResult(Guid.NewGuid(), _avatarAddress, rewards);
                monsterCollectionState.UpdateRewardMap(level, result, i * MonsterCollectionState0.RewardInterval);
            }

            List <MonsterCollectionRewardSheet.RewardInfo> collectionRewards = _tableSheets.MonsterCollectionRewardSheet[collectionLevel].Rewards;

            monsterCollectionState.Update(collectionLevel, rewardLevel, _tableSheets.MonsterCollectionRewardSheet);
            for (long i = rewardLevel; i < 4; i++)
            {
                Assert.Equal(collectionRewards, monsterCollectionState.RewardLevelMap[i + 1]);
            }

            Dictionary <int, int> rewardExpectedMap = new Dictionary <int, int>();

            foreach (var(key, value) in monsterCollectionState.RewardLevelMap)
            {
                if (monsterCollectionState.RewardMap.ContainsKey(key) || key > rewardLevel)
                {
                    continue;
                }

                foreach (var info in value)
                {
                    if (rewardExpectedMap.ContainsKey(info.ItemId))
                    {
                        rewardExpectedMap[info.ItemId] += info.Quantity;
                    }
                    else
                    {
                        rewardExpectedMap[info.ItemId] = info.Quantity;
                    }
                }
            }

            AvatarState prevAvatarState = _state.GetAvatarState(_avatarAddress);

            Assert.Empty(prevAvatarState.mailBox);

            Currency currency        = _state.GetGoldCurrency();
            int      collectionRound = _state.GetAgentState(_signer).MonsterCollectionRound;

            _state = _state
                     .SetState(collectionAddress, monsterCollectionState.Serialize());

            FungibleAssetValue balance = 0 * currency;

            if (rewardLevel == 4)
            {
                foreach (var row in _tableSheets.MonsterCollectionSheet)
                {
                    if (row.Level <= collectionLevel)
                    {
                        balance += row.RequiredGold * currency;
                    }
                }

                collectionRound += 1;
                _state           = _state
                                   .MintAsset(collectionAddress, balance);
            }

            Assert.Equal(prevRewardLevel, monsterCollectionState.RewardLevel);
            Assert.Equal(0, _state.GetAgentState(_signer).MonsterCollectionRound);

            ClaimMonsterCollectionReward0 action = new ClaimMonsterCollectionReward0
            {
                avatarAddress   = _avatarAddress,
                collectionRound = 0,
            };

            IAccountStateDelta nextState = action.Execute(new ActionContext
            {
                PreviousStates = _state,
                Signer         = _signer,
                BlockIndex     = rewardLevel * MonsterCollectionState0.RewardInterval,
                Random         = new TestRandom(),
            });

            MonsterCollectionState0 nextMonsterCollectionState = new MonsterCollectionState0((Dictionary)nextState.GetState(collectionAddress));

            Assert.Equal(rewardLevel, nextMonsterCollectionState.RewardLevel);

            AvatarState nextAvatarState = nextState.GetAvatarState(_avatarAddress);

            foreach (var(itemId, qty) in rewardExpectedMap)
            {
                Assert.True(nextAvatarState.inventory.HasItem(itemId, qty));
            }

            Assert.Equal(rewardLevel - prevRewardLevel, nextAvatarState.mailBox.Count);
            Assert.All(nextAvatarState.mailBox, mail =>
            {
                Assert.IsType <MonsterCollectionMail>(mail);
                MonsterCollectionMail monsterCollectionMail = (MonsterCollectionMail)mail;
                Assert.IsType <MonsterCollectionResult>(monsterCollectionMail.attachment);
                MonsterCollectionResult result = (MonsterCollectionResult)monsterCollectionMail.attachment;
                Assert.Equal(result.id, mail.id);
            });

            for (int i = 0; i < nextMonsterCollectionState.RewardLevel; i++)
            {
                int level = i + 1;
                List <MonsterCollectionRewardSheet.RewardInfo> rewardInfos = _tableSheets.MonsterCollectionRewardSheet[collectionLevel].Rewards;
                Assert.Contains(level, nextMonsterCollectionState.RewardMap.Keys);
                Assert.Equal(_avatarAddress, nextMonsterCollectionState.RewardMap[level].avatarAddress);
            }

            Assert.Equal(0 * currency, nextState.GetBalance(collectionAddress, currency));
            Assert.Equal(balance, nextState.GetBalance(_signer, currency));
            Assert.Equal(collectionRound, nextState.GetAgentState(_signer).MonsterCollectionRound);
            Assert.Equal(nextMonsterCollectionState.End, rewardLevel == 4);
        }
Exemple #3
0
 public static GoldBalanceState GetGoldBalanceState(
     this IAccountStateDelta states,
     Address address,
     Currency currency
     ) => new GoldBalanceState(address, states.GetBalance(address, currency));
Exemple #4
0
        public override IAccountStateDelta Execute(IActionContext context)
        {
            IAccountStateDelta states = context.PreviousStates;
            Address            monsterCollectionAddress = MonsterCollectionState0.DeriveAddress(context.Signer, collectionRound);

            if (context.Rehearsal)
            {
                return(states
                       .SetState(monsterCollectionAddress, MarkChanged)
                       .SetState(context.Signer, MarkChanged)
                       .MarkBalanceChanged(GoldCurrencyMock, context.Signer, monsterCollectionAddress));
            }

            CheckObsolete(BlockChain.Policy.BlockPolicySource.V100080ObsoleteIndex, context);

            MonsterCollectionSheet monsterCollectionSheet = states.GetSheet <MonsterCollectionSheet>();

            AgentState agentState = states.GetAgentState(context.Signer);

            if (agentState is null)
            {
                throw new FailedLoadStateException("Aborted as the agent state failed to load.");
            }

            if (agentState.MonsterCollectionRound != collectionRound)
            {
                throw new InvalidMonsterCollectionRoundException(
                          $"Expected collection round is {agentState.MonsterCollectionRound}, but actual collection round is {collectionRound}.");
            }

            if (!monsterCollectionSheet.TryGetValue(level, out MonsterCollectionSheet.Row _))
            {
                throw new SheetRowNotFoundException(nameof(MonsterCollectionSheet), level);
            }

            Currency currency = states.GetGoldCurrency();
            // Set default gold value.
            FungibleAssetValue requiredGold = currency * 0;
            FungibleAssetValue balance      = states.GetBalance(context.Signer, states.GetGoldCurrency());

            MonsterCollectionState0 monsterCollectionState;
            int currentLevel = 1;
            MonsterCollectionRewardSheet monsterCollectionRewardSheet = states.GetSheet <MonsterCollectionRewardSheet>();

            if (states.TryGetState(monsterCollectionAddress, out Dictionary stateDict))
            {
                monsterCollectionState = new MonsterCollectionState0(stateDict);

                if (monsterCollectionState.ExpiredBlockIndex < context.BlockIndex)
                {
                    throw new MonsterCollectionExpiredException(
                              $"{monsterCollectionAddress} has already expired on {monsterCollectionState.ExpiredBlockIndex}");
                }

                if (monsterCollectionState.Level >= level)
                {
                    throw new InvalidLevelException($"The level must be greater than {monsterCollectionState.Level}.");
                }

                currentLevel = monsterCollectionState.Level + 1;
                long rewardLevel = monsterCollectionState.GetRewardLevel(context.BlockIndex);
                monsterCollectionState.Update(level, rewardLevel, monsterCollectionRewardSheet);
            }
            else
            {
                monsterCollectionState = new MonsterCollectionState0(monsterCollectionAddress, level, context.BlockIndex, monsterCollectionRewardSheet);
            }

            for (int i = currentLevel; i < level + 1; i++)
            {
                requiredGold += currency * monsterCollectionSheet[i].RequiredGold;
            }

            if (balance < requiredGold)
            {
                throw new InsufficientBalanceException(context.Signer, requiredGold,
                                                       $"There is no sufficient balance for {context.Signer}: {balance} < {requiredGold}");
            }
            states = states.TransferAsset(context.Signer, monsterCollectionAddress, requiredGold);
            states = states.SetState(monsterCollectionAddress, monsterCollectionState.Serialize());
            return(states);
        }
Exemple #5
0
        public override IAccountStateDelta Execute(IActionContext context)
        {
            IAccountStateDelta states = context.PreviousStates;

            if (context.Rehearsal)
            {
                return(states
                       .SetState(MonsterCollectionState.DeriveAddress(context.Signer, 0), MarkChanged)
                       .SetState(MonsterCollectionState.DeriveAddress(context.Signer, 1), MarkChanged)
                       .SetState(MonsterCollectionState.DeriveAddress(context.Signer, 2), MarkChanged)
                       .SetState(MonsterCollectionState.DeriveAddress(context.Signer, 3), MarkChanged)
                       .SetState(context.Signer, MarkChanged)
                       .MarkBalanceChanged(GoldCurrencyMock, context.Signer, MonsterCollectionState.DeriveAddress(context.Signer, 0))
                       .MarkBalanceChanged(GoldCurrencyMock, context.Signer, MonsterCollectionState.DeriveAddress(context.Signer, 1))
                       .MarkBalanceChanged(GoldCurrencyMock, context.Signer, MonsterCollectionState.DeriveAddress(context.Signer, 2))
                       .MarkBalanceChanged(GoldCurrencyMock, context.Signer, MonsterCollectionState.DeriveAddress(context.Signer, 3)));
            }

            MonsterCollectionSheet monsterCollectionSheet = states.GetSheet <MonsterCollectionSheet>();

            AgentState agentState = states.GetAgentState(context.Signer);

            if (agentState is null)
            {
                throw new FailedLoadStateException("Aborted as the agent state failed to load.");
            }

            if (level < 0 || level > 0 && !monsterCollectionSheet.TryGetValue(level, out MonsterCollectionSheet.Row _))
            {
                throw new MonsterCollectionLevelException();
            }

            Currency currency = states.GetGoldCurrency();
            // Set default gold value.
            FungibleAssetValue requiredGold             = currency * 0;
            Address            monsterCollectionAddress = MonsterCollectionState.DeriveAddress(
                context.Signer,
                agentState.MonsterCollectionRound
                );

            if (states.TryGetState(monsterCollectionAddress, out Dictionary stateDict))
            {
                var existingStates = new MonsterCollectionState(stateDict);
                int previousLevel  = existingStates.Level;
                // Check collection level and required block index
                if (level < previousLevel && existingStates.IsLocked(context.BlockIndex))
                {
                    throw new RequiredBlockIndexException();
                }

                if (level == previousLevel)
                {
                    throw new MonsterCollectionLevelException();
                }

                if (existingStates.CalculateStep(context.BlockIndex) > 0)
                {
                    throw new MonsterCollectionExistingClaimableException();
                }

                // Refund holding NCG to user
                FungibleAssetValue gold = states.GetBalance(monsterCollectionAddress, currency);
                states = states.TransferAsset(monsterCollectionAddress, context.Signer, gold);
            }

            if (level == 0)
            {
                return(states.SetState(monsterCollectionAddress, new Null()));
            }

            FungibleAssetValue balance = states.GetBalance(context.Signer, currency);
            var monsterCollectionState = new MonsterCollectionState(monsterCollectionAddress, level, context.BlockIndex);

            for (int i = 0; i < level; i++)
            {
                requiredGold += currency * monsterCollectionSheet[i + 1].RequiredGold;
            }

            if (balance < requiredGold)
            {
                throw new InsufficientBalanceException(context.Signer, requiredGold,
                                                       $"There is no sufficient balance for {context.Signer}: {balance} < {requiredGold}");
            }
            states = states.TransferAsset(context.Signer, monsterCollectionAddress, requiredGold);
            states = states.SetState(monsterCollectionAddress, monsterCollectionState.Serialize());
            return(states);
        }
Exemple #6
0
        public void ExecuteInsufficientBalanceError()
        {
            var shopState = _initialState.GetShopState();

            var sellerAvatarAddress = new PrivateKey().ToAddress();
            var sellerAgentAddress  = new PrivateKey().ToAddress();

            var(avatarState, agentState) = CreateAvatarState(sellerAgentAddress, sellerAvatarAddress);

            var equipment = ItemFactory.CreateItemUsable(
                _tableSheets.EquipmentItemSheet.First,
                Guid.NewGuid(),
                1);

            shopState.Register(new ShopItem(
                                   sellerAgentAddress,
                                   sellerAvatarAddress,
                                   Guid.NewGuid(),
                                   new FungibleAssetValue(_goldCurrencyState.Currency, 1, 0),
                                   100,
                                   equipment));

            var costume = ItemFactory.CreateCostume(
                _tableSheets.CostumeItemSheet.First,
                Guid.NewGuid());

            shopState.Register(new ShopItem(
                                   sellerAgentAddress,
                                   sellerAvatarAddress,
                                   Guid.NewGuid(),
                                   new FungibleAssetValue(_goldCurrencyState.Currency, 100, 0),
                                   100,
                                   costume));

            _initialState = _initialState
                            .SetState(Addresses.Shop, shopState.Serialize());
            shopState = _initialState.GetShopState();
            Assert.NotEmpty(shopState.Products);

            var products = shopState.Products.Values
                           .Select(p => new BuyMultiple.PurchaseInfo(
                                       p.ProductId,
                                       p.SellerAgentAddress,
                                       p.SellerAvatarAddress))
                           .ToList();

            Assert.NotEmpty(products);

            var balance = _initialState.GetBalance(_buyerAgentAddress, _goldCurrencyState.Currency);

            _initialState = _initialState.BurnAsset(_buyerAgentAddress, balance);

            var action = new BuyMultiple
            {
                buyerAvatarAddress = _buyerAvatarAddress,
                purchaseInfos      = products,
            };

            action.Execute(new ActionContext()
            {
                BlockIndex     = 0,
                PreviousStates = _initialState,
                Random         = new TestRandom(),
                Signer         = _buyerAgentAddress,
            });

            var results     = action.buyerResult.purchaseResults;
            var isAllFailed = results.Any(r => r.errorCode == BuyMultiple.ERROR_CODE_INSUFFICIENT_BALANCE);

            Assert.True(isAllFailed);
        }
Exemple #7
0
        public void Execute(params ShopItemData[] productDatas)
        {
            var buyerAvatarState = _initialState.GetAvatarState(_buyerAvatarAddress);

            var goldCurrency = _initialState.GetGoldCurrency();
            var shopState    = _initialState.GetShopState();
            var buyCount     = 0;
            var itemsToBuy   = new List <BuyMultiple.PurchaseInfo>();

            foreach (var product in productDatas)
            {
                var(sellerAvatarState, sellerAgentState) =
                    CreateAvatarState(product.SellerAgentAddress, product.SellerAvatarAddress);

                INonFungibleItem nonFungibleItem;
                if (product.ItemType == ItemType.Equipment)
                {
                    var itemUsable = ItemFactory.CreateItemUsable(
                        _tableSheets.EquipmentItemSheet.First,
                        product.ItemId,
                        product.RequiredBlockIndex);
                    nonFungibleItem = itemUsable;
                }
                else
                {
                    var costume = ItemFactory.CreateCostume(_tableSheets.CostumeItemSheet.First, product.ItemId);
                    costume.Update(product.RequiredBlockIndex);
                    nonFungibleItem = costume;
                }

                // Case for backward compatibility of `Buy`
                if (product.ContainsInInventory)
                {
                    sellerAvatarState.inventory.AddItem2((ItemBase)nonFungibleItem);
                }

                var shopItemId = Guid.NewGuid();

                var shopItem = new ShopItem(
                    sellerAgentState.address,
                    sellerAvatarState.address,
                    shopItemId,
                    new FungibleAssetValue(_goldCurrencyState.Currency, product.Price, 0),
                    product.RequiredBlockIndex,
                    nonFungibleItem);
                shopState.Register(shopItem);

                if (product.Buy)
                {
                    ++buyCount;
                    var purchaseInfo = new BuyMultiple.PurchaseInfo(
                        shopItem.ProductId,
                        shopItem.SellerAgentAddress,
                        shopItem.SellerAvatarAddress);
                    itemsToBuy.Add(purchaseInfo);
                }
            }

            Assert.NotNull(shopState.Products);
            Assert.Equal(3, shopState.Products.Count);

            _initialState = _initialState
                            .SetState(_buyerAvatarAddress, buyerAvatarState.Serialize())
                            .SetState(Addresses.Shop, shopState.Serialize());

            var priceData = new PriceData(goldCurrency);

            foreach (var avatarState in _sellerAgentStateMap.Keys)
            {
                var agentState = _sellerAgentStateMap[avatarState];
                priceData.TaxedPriceSum[agentState.address] = new FungibleAssetValue(goldCurrency, 0, 0);

                _initialState = _initialState
                                .SetState(avatarState.address, avatarState.Serialize());
            }

            IAccountStateDelta previousStates = _initialState;

            var buyerGold    = previousStates.GetBalance(_buyerAgentAddress, goldCurrency);
            var priceSumData = productDatas
                               .Where(i => i.Buy)
                               .Aggregate(priceData, (priceSum, next) =>
            {
                var price         = new FungibleAssetValue(goldCurrency, next.Price, 0);
                var tax           = price.DivRem(100, out _) * Buy.TaxRate;
                var taxedPrice    = price - tax;
                priceData.TaxSum += tax;

                var prevSum = priceData.TaxedPriceSum[next.SellerAgentAddress];
                priceData.TaxedPriceSum[next.SellerAgentAddress] = prevSum + taxedPrice;
                priceData.PriceSum += price;
                return(priceData);
            });

            var buyMultipleAction = new BuyMultiple
            {
                buyerAvatarAddress = _buyerAvatarAddress,
                purchaseInfos      = itemsToBuy,
            };
            var nextState = buyMultipleAction.Execute(new ActionContext()
            {
                BlockIndex     = 1,
                PreviousStates = previousStates,
                Random         = new TestRandom(),
                Rehearsal      = false,
                Signer         = _buyerAgentAddress,
            });

            var nextShopState = nextState.GetShopState();

            Assert.Equal(productDatas.Length - buyCount, nextShopState.Products.Count);

            var nextBuyerAvatarState = nextState.GetAvatarState(_buyerAvatarAddress);

            foreach (var product in productDatas.Where(i => i.Buy))
            {
                Assert.True(
                    nextBuyerAvatarState.inventory.TryGetNonFungibleItem(
                        product.ItemId,
                        out INonFungibleItem outNonFungibleItem)
                    );
                Assert.Equal(product.RequiredBlockIndex, outNonFungibleItem.RequiredBlockIndex);
            }

            Assert.Equal(buyCount, nextBuyerAvatarState.mailBox.Count);

            goldCurrency = nextState.GetGoldCurrency();
            var nextGoldCurrencyGold = nextState.GetBalance(Addresses.GoldCurrency, goldCurrency);

            Assert.Equal(priceSumData.TaxSum, nextGoldCurrencyGold);

            foreach (var product in productDatas)
            {
                var nextSellerGold = nextState.GetBalance(product.SellerAgentAddress, goldCurrency);
                Assert.Equal(priceSumData.TaxedPriceSum[product.SellerAgentAddress], nextSellerGold);
            }

            var nextBuyerGold = nextState.GetBalance(_buyerAgentAddress, goldCurrency);

            Assert.Equal(buyerGold - priceSumData.PriceSum, nextBuyerGold);

            previousStates = nextState;
        }