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); }
public static GoldBalanceState GetGoldBalanceState( this IAccountStateDelta states, Address address, Currency currency ) => new GoldBalanceState(address, states.GetBalance(address, currency));
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); }
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); }
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); }
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; }