public void Serialization() { var shopState = new ShopState(); var agentAddress = new PrivateKey().ToAddress(); var avatarAddress = new PrivateKey().ToAddress(); var productId = Guid.NewGuid(); var weaponRow = new EquipmentItemSheet.Row(); weaponRow.Set(new[] { "10100000", "Weapon", "0", "Normal", "0", "ATK", "1", "2", "10100000", }); var itemUsable = new Weapon( weaponRow, Guid.NewGuid(), 0); var price = new FungibleAssetValue(new Currency("NCG", 2, minter: null)); var shopItem = new ShopItem( agentAddress, avatarAddress, productId, price, itemUsable); shopState.Register(shopItem); var serialized = (Dictionary)shopState.Serialize(); shopState = new ShopState(serialized); Assert.Equal(1, shopState.Products.Count); Assert.Contains(productId, shopState.Products); Assert.Equal(shopItem, shopState.Products[productId]); }
public Sell0Test(ITestOutputHelper outputHelper) { Log.Logger = new LoggerConfiguration() .MinimumLevel.Verbose() .WriteTo.TestOutput(outputHelper) .CreateLogger(); _initialState = new State(); var sheets = TableSheetsImporter.ImportSheets(); foreach (var(key, value) in sheets) { _initialState = _initialState .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); } _tableSheets = new TableSheets(sheets); _currency = new Currency("NCG", 2, minters: null); var goldCurrencyState = new GoldCurrencyState(_currency); var shopState = new ShopState(); _agentAddress = new PrivateKey().ToAddress(); var agentState = new AgentState(_agentAddress); _avatarAddress = new PrivateKey().ToAddress(); var rankingMapAddress = new PrivateKey().ToAddress(); _avatarState = new AvatarState( _avatarAddress, _agentAddress, 0, _tableSheets.GetAvatarSheets(), new GameConfigState(), rankingMapAddress) { worldInformation = new WorldInformation( 0, _tableSheets.WorldSheet, GameConfig.RequireClearedStageLevel.ActionsInShop), }; agentState.avatarAddresses[0] = _avatarAddress; var equipment = ItemFactory.CreateItemUsable( _tableSheets.EquipmentItemSheet.First, Guid.NewGuid(), 0); _avatarState.inventory.AddItem2(equipment); _initialState = _initialState .SetState(GoldCurrencyState.Address, goldCurrencyState.Serialize()) .SetState(Addresses.Shop, shopState.Serialize()) .SetState(_agentAddress, agentState.Serialize()) .SetState(_avatarAddress, _avatarState.Serialize()); }
public BuyMultipleTest(ITestOutputHelper outputHelper) { Log.Logger = new LoggerConfiguration() .MinimumLevel.Verbose() .WriteTo.TestOutput(outputHelper) .CreateLogger(); _initialState = new State(); var sheets = TableSheetsImporter.ImportSheets(); foreach (var(key, value) in sheets) { _initialState = _initialState .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); } _tableSheets = new TableSheets(sheets); var currency = new Currency("NCG", 2, minters: null); _goldCurrencyState = new GoldCurrencyState(currency); _sellerAgentStateMap = new Dictionary <AvatarState, AgentState>(); _buyerAgentAddress = new PrivateKey().ToAddress(); var buyerAgentState = new AgentState(_buyerAgentAddress); _buyerAvatarAddress = new PrivateKey().ToAddress(); var rankingMapAddress = new PrivateKey().ToAddress(); _buyerAvatarState = new AvatarState( _buyerAvatarAddress, _buyerAgentAddress, 0, _tableSheets.GetAvatarSheets(), new GameConfigState(), rankingMapAddress) { worldInformation = new WorldInformation( 0, _tableSheets.WorldSheet, GameConfig.RequireClearedStageLevel.ActionsInShop), }; buyerAgentState.avatarAddresses[0] = _buyerAvatarAddress; var shopState = new ShopState(); _initialState = _initialState .SetState(GoldCurrencyState.Address, _goldCurrencyState.Serialize()) .SetState(Addresses.Shop, shopState.Serialize()) .SetState(_buyerAgentAddress, buyerAgentState.Serialize()) .SetState(_buyerAvatarAddress, _buyerAvatarState.Serialize()) .MintAsset(_buyerAgentAddress, _goldCurrencyState.Currency * 100); }
public override IAccountStateDelta Execute(IActionContext context) { IActionContext ctx = context; var states = ctx.PreviousStates; var weeklyArenaState = new WeeklyArenaState(0); if (ctx.Rehearsal) { states = states.SetState(RankingState.Address, MarkChanged); states = states.SetState(ShopState.Address, MarkChanged); states = states.SetState(TableSheetsState.Address, MarkChanged); states = states.SetState(weeklyArenaState.address, MarkChanged); states = states.SetState(GameConfigState.Address, MarkChanged); states = states.SetState(RedeemCodeState.Address, MarkChanged); states = states.SetState(AdminState.Address, MarkChanged); states = states.SetState(ActivatedAccountsState.Address, MarkChanged); states = states.SetState(GoldCurrencyState.Address, MarkChanged); states = states.SetState(Addresses.GoldDistribution, MarkChanged); return(states); } if (ctx.BlockIndex != 0) { return(states); } states = states .SetState(weeklyArenaState.address, weeklyArenaState.Serialize()) .SetState(RankingState.Address, RankingState.Serialize()) .SetState(ShopState.Address, ShopState.Serialize()) .SetState(TableSheetsState.Address, TableSheetsState.Serialize()) .SetState(GameConfigState.Address, GameConfigState.Serialize()) .SetState(RedeemCodeState.Address, RedeemCodeState.Serialize()) .SetState(AdminState.Address, AdminAddressState.Serialize()) .SetState(ActivatedAccountsState.Address, ActivatedAccountsState.Serialize()) .SetState(GoldCurrencyState.Address, GoldCurrencyState.Serialize()) .SetState(Addresses.GoldDistribution, GoldDistributions.Select(v => v.Serialize()).Serialize()); states = states.MintAsset(GoldCurrencyState.Address, GoldCurrencyState.Currency, 1000000000); return(states); }
public InitializeStates( RankingState rankingState, ShopState shopState, Dictionary <string, string> tableSheets, GameConfigState gameConfigState, RedeemCodeState redeemCodeState, AdminState adminAddressState, ActivatedAccountsState activatedAccountsState, GoldCurrencyState goldCurrencyState, GoldDistribution[] goldDistributions, PendingActivationState[] pendingActivationStates, AuthorizedMinersState authorizedMinersState = null, CreditsState creditsState = null) { Ranking = (Bencodex.Types.Dictionary)rankingState.Serialize(); Shop = (Bencodex.Types.Dictionary)shopState.Serialize(); TableSheets = tableSheets; GameConfig = (Bencodex.Types.Dictionary)gameConfigState.Serialize(); RedeemCode = (Bencodex.Types.Dictionary)redeemCodeState.Serialize(); AdminAddress = (Bencodex.Types.Dictionary)adminAddressState.Serialize(); ActivatedAccounts = (Bencodex.Types.Dictionary)activatedAccountsState.Serialize(); GoldCurrency = (Bencodex.Types.Dictionary)goldCurrencyState.Serialize(); GoldDistributions = new Bencodex.Types.List( goldDistributions.Select(d => d.Serialize()).Cast <Bencodex.Types.IValue>() ); PendingActivations = new Bencodex.Types.List(pendingActivationStates.Select(p => p.Serialize())); if (!(authorizedMinersState is null)) { AuthorizedMiners = (Bencodex.Types.Dictionary)authorizedMinersState.Serialize(); } if (!(creditsState is null)) { Credits = (Bencodex.Types.Dictionary)creditsState.Serialize(); } }
public Buy3Test(ITestOutputHelper outputHelper) { Log.Logger = new LoggerConfiguration() .MinimumLevel.Verbose() .WriteTo.TestOutput(outputHelper) .CreateLogger(); _initialState = new State(); var sheets = TableSheetsImporter.ImportSheets(); foreach (var(key, value) in sheets) { _initialState = _initialState .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); } _tableSheets = new TableSheets(sheets); var currency = new Currency("NCG", 2, minters: null); _goldCurrencyState = new GoldCurrencyState(currency); _sellerAgentAddress = new PrivateKey().ToAddress(); var sellerAgentState = new AgentState(_sellerAgentAddress); _sellerAvatarAddress = new PrivateKey().ToAddress(); var rankingMapAddress = new PrivateKey().ToAddress(); var sellerAvatarState = new AvatarState( _sellerAvatarAddress, _sellerAgentAddress, 0, _tableSheets.GetAvatarSheets(), new GameConfigState(), rankingMapAddress) { worldInformation = new WorldInformation( 0, _tableSheets.WorldSheet, GameConfig.RequireClearedStageLevel.ActionsInShop), }; sellerAgentState.avatarAddresses[0] = _sellerAvatarAddress; _buyerAgentAddress = new PrivateKey().ToAddress(); var buyerAgentState = new AgentState(_buyerAgentAddress); _buyerAvatarAddress = new PrivateKey().ToAddress(); _buyerAvatarState = new AvatarState( _buyerAvatarAddress, _buyerAgentAddress, 0, _tableSheets.GetAvatarSheets(), new GameConfigState(), rankingMapAddress) { worldInformation = new WorldInformation( 0, _tableSheets.WorldSheet, GameConfig.RequireClearedStageLevel.ActionsInShop), }; buyerAgentState.avatarAddresses[0] = _buyerAvatarAddress; var equipment = ItemFactory.CreateItemUsable( _tableSheets.EquipmentItemSheet.First, Guid.NewGuid(), 0); var consumable = ItemFactory.CreateItemUsable( _tableSheets.ConsumableItemSheet.First, Guid.NewGuid(), 0); var costume = ItemFactory.CreateCostume( _tableSheets.CostumeItemSheet.First, Guid.NewGuid()); var shopState = new ShopState(); shopState.Register(new ShopItem( _sellerAgentAddress, _sellerAvatarAddress, Guid.NewGuid(), new FungibleAssetValue(_goldCurrencyState.Currency, ProductPrice, 0), equipment)); shopState.Register(new ShopItem( _sellerAgentAddress, _sellerAvatarAddress, Guid.NewGuid(), new FungibleAssetValue(_goldCurrencyState.Currency, ProductPrice, 0), consumable)); shopState.Register(new ShopItem( _sellerAgentAddress, _sellerAvatarAddress, Guid.NewGuid(), new FungibleAssetValue(_goldCurrencyState.Currency, ProductPrice, 0), costume)); _initialState = _initialState .SetState(GoldCurrencyState.Address, _goldCurrencyState.Serialize()) .SetState(Addresses.Shop, shopState.Serialize()) .SetState(_sellerAgentAddress, sellerAgentState.Serialize()) .SetState(_sellerAvatarAddress, sellerAvatarState.Serialize()) .SetState(_buyerAgentAddress, buyerAgentState.Serialize()) .SetState(_buyerAvatarAddress, _buyerAvatarState.Serialize()) .MintAsset(_buyerAgentAddress, shopState.Products .Select(pair => pair.Value.Price) .Aggregate((totalPrice, next) => totalPrice + next)); }
public override IAccountStateDelta Execute(IActionContext context) { IActionContext ctx = context; var states = ctx.PreviousStates; if (ctx.Rehearsal) { states = states.SetState(ShopState.Address, MarkChanged); states = states.SetState(sellerAvatarAddress, MarkChanged); return(states.SetState(ctx.Signer, MarkChanged)); } var sw = new Stopwatch(); sw.Start(); var started = DateTimeOffset.UtcNow; Log.Debug("Sell exec started."); if (price < 0) { return(LogError(context, "Aborted as the price is less than zero: {Price}.", price)); } if (!states.TryGetAgentAvatarStates(ctx.Signer, sellerAvatarAddress, out AgentState agentState, out AvatarState avatarState)) { return(LogError(context, "Aborted as the avatar state of the signer was failed to load.")); } sw.Stop(); Log.Debug("Sell Get AgentAvatarStates: {Elapsed}", sw.Elapsed); sw.Restart(); if (!avatarState.worldInformation.TryGetUnlockedWorldByStageClearedBlockIndex(out var world)) { return(LogError(context, "Aborted as the WorldInformation was failed to load.")); } if (world.StageClearedId < GameConfig.RequireClearedStageLevel.ActionsInShop) { // 스테이지 클리어 부족 에러. return(LogError( context, "Aborted as the signer is not cleared the minimum stage level required to sell items yet: {ClearedLevel} < {RequiredLevel}.", world.StageClearedId, GameConfig.RequireClearedStageLevel.ActionsInShop )); } if (!states.TryGetState(ShopState.Address, out Bencodex.Types.Dictionary d)) { return(LogError(context, "Aborted as the shop state was failed to load.")); } var shopState = new ShopState(d); sw.Stop(); Log.Debug("Sell Get ShopState: {Elapsed}", sw.Elapsed); sw.Restart(); Log.Debug("Execute Sell; seller: {SellerAvatarAddress}", sellerAvatarAddress); // 인벤토리에서 판매할 아이템을 선택하고 수량을 조절한다. if (!avatarState.inventory.TryGetNonFungibleItem(itemId, out ItemUsable nonFungibleItem)) { return(LogError( context, "Aborted as the NonFungibleItem ({@Item}) was failed to load from avatar's inventory.", itemId )); } if (nonFungibleItem.RequiredBlockIndex > context.BlockIndex) { // 필요 블럭 인덱스 불충분 에러. return(LogError( context, "Aborted as the equipment to enhance ({@Item}) is not available yet; it will be available at the block #{RequiredBlockIndex}.", itemId, nonFungibleItem.RequiredBlockIndex )); } avatarState.inventory.RemoveNonFungibleItem(nonFungibleItem); if (nonFungibleItem is Equipment equipment) { equipment.equipped = false; } // 상점에 아이템을 등록한다. shopState.Register(ctx.Signer, new ShopItem( sellerAvatarAddress, productId, nonFungibleItem, price )); sw.Stop(); Log.Debug("Sell Get Register Item: {Elapsed}", sw.Elapsed); sw.Restart(); avatarState.updatedAt = DateTimeOffset.UtcNow; avatarState.blockIndex = ctx.BlockIndex; states = states.SetState(sellerAvatarAddress, avatarState.Serialize()); sw.Stop(); Log.Debug("Sell Set AvatarState: {Elapsed}", sw.Elapsed); sw.Restart(); states = states.SetState(ShopState.Address, shopState.Serialize()); sw.Stop(); var ended = DateTimeOffset.UtcNow; Log.Debug("Sell Set ShopState: {Elapsed}", sw.Elapsed); Log.Debug("Sell Total Executed Time: {Elapsed}", ended - started); return(states); }
public void Execute(bool isAdmin, bool expire, Type exc) { var adminAddress = new Address("399bddF9F7B6d902ea27037B907B2486C9910730"); var adminState = new AdminState(adminAddress, 100); var states = new State().SetState(Addresses.Admin, adminState.Serialize()); var signer = isAdmin ? adminAddress : default; var blockIndex = expire ? 200 : 100; var action = new MigrationLegacyShop(); var avatarAddress = new Address(action.AvatarAddressesHex.First()); if (exc is null) { var agentState = new AgentState(adminAddress); var avatarState = new AvatarState( avatarAddress, adminAddress, 0, _tableSheets.GetAvatarSheets(), new GameConfigState(), default); agentState.avatarAddresses[0] = avatarAddress; var shopState = new ShopState(); var itemSubTypes = new[] { ItemSubType.Weapon, ItemSubType.FullCostume }; var random = new TestRandom(); var itemIds = new List<Guid>(); foreach (var itemSubType in itemSubTypes) { var item = (ITradableItem)ItemFactory.CreateItem(_tableSheets.ItemSheet.Values.First(r => r.ItemSubType == itemSubType), random); var shopItem = new ShopItem( adminAddress, avatarAddress, Guid.NewGuid(), new FungibleAssetValue(new Currency("NCG", 2, minter: null), 100, 0), item); shopState.Register(shopItem); itemIds.Add(item.TradableId); } states = states .SetState(Addresses.Shop, shopState.Serialize()) .SetState(adminAddress, agentState.Serialize()) .SetState(avatarAddress, avatarState.Serialize()); var nextState = action.Execute(new ActionContext { BlockIndex = blockIndex, PreviousStates = states, Signer = signer, }); var nextShopState = nextState.GetShopState(); Assert.Empty(nextShopState.Products); var nextAvatarState = nextState.GetAvatarState(avatarAddress); Assert.All(itemIds, id => nextAvatarState.inventory.HasNonFungibleItem(id)); } else { Assert.Throws(exc, () => action.Execute(new ActionContext { BlockIndex = blockIndex, PreviousStates = states, Signer = signer, })); } }
public override IAccountStateDelta Execute(IActionContext context) { IActionContext ctx = context; var states = ctx.PreviousStates; if (ctx.Rehearsal) { states = states .SetState(buyerAvatarAddress, MarkChanged) .SetState(ctx.Signer, MarkChanged) .SetState(sellerAvatarAddress, MarkChanged) .MarkBalanceChanged(GoldCurrencyMock, ctx.Signer, sellerAgentAddress); return(states.SetState(ShopState.Address, MarkChanged)); } if (ctx.Signer.Equals(sellerAgentAddress)) { return(LogError(context, "Aborted as the signer is the seller.")); } var sw = new Stopwatch(); sw.Start(); var started = DateTimeOffset.UtcNow; Log.Debug("Buy exec started."); if (!states.TryGetAgentAvatarStates(ctx.Signer, buyerAvatarAddress, out var buyerAgentState, out var buyerAvatarState)) { return(LogError(context, "Aborted as the avatar state of the buyer was failed to load.")); } sw.Stop(); Log.Debug("Buy Get Buyer AgentAvatarStates: {Elapsed}", sw.Elapsed); sw.Restart(); if (!buyerAvatarState.worldInformation.TryGetUnlockedWorldByStageClearedBlockIndex(out var world)) { return(LogError(context, "Aborted as the WorldInformation was failed to load.")); } if (world.StageClearedId < GameConfig.RequireClearedStageLevel.ActionsInShop) { // 스테이지 클리어 부족 에러. return(LogError( context, "Aborted as the signer is not cleared the minimum stage level required to use shop yet: {ClearedLevel} < {RequiredLevel}.", world.StageClearedId, GameConfig.RequireClearedStageLevel.ActionsInShop )); } if (!states.TryGetState(ShopState.Address, out Bencodex.Types.Dictionary d)) { return(LogError(context, "Aborted as the shop state was failed to load.")); } var shopState = new ShopState(d); sw.Stop(); Log.Debug("Buy Get ShopState: {Elapsed}", sw.Elapsed); sw.Restart(); Log.Debug("Execute Buy; buyer: {Buyer} seller: {Seller}", buyerAvatarAddress, sellerAvatarAddress); // 상점에서 구매할 아이템을 찾는다. if (!shopState.TryGet(sellerAgentAddress, productId, out var outPair)) { return(LogError( context, "Aborted as the shop item ({ProductId}) was failed to get from the seller agent ({SellerAgent}).", productId, sellerAgentAddress )); } sw.Stop(); Log.Debug($"Buy Get Item: {sw.Elapsed}"); sw.Restart(); if (!states.TryGetAgentAvatarStates(sellerAgentAddress, sellerAvatarAddress, out var sellerAgentState, out var sellerAvatarState)) { return(LogError( context, "Aborted as the seller agent/avatar was filed to load from {SellerAgent}/{SellerAvatar}.", sellerAgentAddress, sellerAvatarAddress )); } sw.Stop(); Log.Debug($"Buy Get Seller AgentAvatarStates: {sw.Elapsed}"); sw.Restart(); // 돈은 있냐? BigInteger buyerBalance = states.GetBalance(context.Signer, states.GetGoldCurrency()); if (buyerBalance < outPair.Value.Price) { return(LogError( context, "Aborted as the buyer ({Buyer}) has no sufficient gold: {BuyerBalance} < {ItemPrice}", ctx.Signer, buyerBalance, outPair.Value.Price )); } var taxedPrice = (BigInteger)decimal.Round((decimal)outPair.Value.Price * 0.92m); // 구매자의 돈을 판매자에게 송금한다. states = states.TransferAsset(context.Signer, sellerAgentAddress, states.GetGoldCurrency(), outPair.Value.Price); // 상점에서 구매할 아이템을 제거한다. if (!shopState.Unregister(sellerAgentAddress, outPair.Value)) { return(LogError( context, "Aborted as the item ({@ProductId}) was failed to unregister from seller ({Seller})'s inventory.", outPair.Value.ProductId, sellerAgentAddress )); } // 구매자, 판매자에게 결과 메일 전송 buyerResult = new BuyerResult { shopItem = outPair.Value, itemUsable = outPair.Value.ItemUsable }; var buyerMail = new BuyerMail(buyerResult, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(), ctx.BlockIndex); buyerResult.id = buyerMail.id; sellerResult = new SellerResult { shopItem = outPair.Value, itemUsable = outPair.Value.ItemUsable, gold = taxedPrice }; var sellerMail = new SellerMail(sellerResult, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(), ctx.BlockIndex); sellerResult.id = sellerMail.id; buyerAvatarState.Update(buyerMail); buyerAvatarState.UpdateFromAddItem(buyerResult.itemUsable, false); sellerAvatarState.Update(sellerMail); // 퀘스트 업데이트 buyerAvatarState.questList.UpdateTradeQuest(TradeType.Buy, outPair.Value.Price); sellerAvatarState.questList.UpdateTradeQuest(TradeType.Sell, outPair.Value.Price); var timestamp = DateTimeOffset.UtcNow; buyerAvatarState.updatedAt = timestamp; buyerAvatarState.blockIndex = ctx.BlockIndex; sellerAvatarState.updatedAt = timestamp; sellerAvatarState.blockIndex = ctx.BlockIndex; buyerAvatarState.UpdateQuestRewards(ctx); sellerAvatarState.UpdateQuestRewards(ctx); states = states.SetState(sellerAvatarAddress, sellerAvatarState.Serialize()); sw.Stop(); Log.Debug("Buy Set Seller AvatarState: {Elapsed}", sw.Elapsed); sw.Restart(); states = states.SetState(buyerAvatarAddress, buyerAvatarState.Serialize()); sw.Stop(); Log.Debug("Buy Set Buyer AvatarState: {Elapsed}", sw.Elapsed); sw.Restart(); states = states.SetState(ShopState.Address, shopState.Serialize()); sw.Stop(); var ended = DateTimeOffset.UtcNow; Log.Debug("Buy Set ShopState: {Elapsed}", sw.Elapsed); Log.Debug("Buy Total Executed Time: {Elapsed}", ended - started); return(states.SetState(ctx.Signer, buyerAgentState.Serialize())); }
public override IAccountStateDelta Execute(IActionContext context) { IActionContext ctx = context; var states = ctx.PreviousStates; if (ctx.Rehearsal) { states = states.SetState(ShopState.Address, MarkChanged); return(states.SetState(sellerAvatarAddress, MarkChanged)); } var sw = new Stopwatch(); sw.Start(); var started = DateTimeOffset.UtcNow; Log.Debug($"Sell Cancel exec started."); if (!states.TryGetAgentAvatarStates(ctx.Signer, sellerAvatarAddress, out _, out var avatarState)) { return(states); } sw.Stop(); Log.Debug($"Sell Cancel Get AgentAvatarStates: {sw.Elapsed}"); sw.Restart(); if (!avatarState.worldInformation.TryGetUnlockedWorldByStageClearedBlockIndex( out var world)) { return(states); } if (world.StageClearedId < GameConfig.RequireClearedStageLevel.ActionsInShop) { // 스테이지 클리어 부족 에러. return(states); } if (!states.TryGetState(ShopState.Address, out Bencodex.Types.Dictionary d)) { return(states); } var shopState = new ShopState(d); sw.Stop(); Log.Debug($"Sell Cancel Get ShopState: {sw.Elapsed}"); sw.Restart(); // 상점에서 아이템을 빼온다. if (!shopState.TryUnregister(ctx.Signer, productId, out var outUnregisteredItem)) { return(states); } sw.Stop(); Log.Debug($"Sell Cancel Get Unregister Item: {sw.Elapsed}"); sw.Restart(); //9c-beta 브랜치에서는 블록 인덱스도 확인 해야함 (이전 블록 유효성 보장) if (outUnregisteredItem.SellerAvatarAddress != sellerAvatarAddress) { Log.Error("Invalid Avatar Address"); return(states); } // 메일에 아이템을 넣는다. result = new Result { shopItem = outUnregisteredItem, itemUsable = outUnregisteredItem.ItemUsable }; var mail = new SellCancelMail(result, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(), ctx.BlockIndex); result.id = mail.id; avatarState.Update(mail); avatarState.UpdateFromAddItem(result.itemUsable, true); avatarState.updatedAt = DateTimeOffset.UtcNow; avatarState.blockIndex = ctx.BlockIndex; sw.Stop(); Log.Debug($"Sell Cancel Update AvatarState: {sw.Elapsed}"); sw.Restart(); states = states.SetState(sellerAvatarAddress, avatarState.Serialize()); sw.Stop(); Log.Debug($"Sell Cancel Set AvatarState: {sw.Elapsed}"); sw.Restart(); states = states.SetState(ShopState.Address, shopState.Serialize()); sw.Stop(); var ended = DateTimeOffset.UtcNow; Log.Debug($"Sell Cancel Set ShopState: {sw.Elapsed}"); Log.Debug($"Sell Cancel Total Executed Time: {ended - started}"); return(states); }