From ad1e1a4066ae86d1149500b8d47f5b592e073afb Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Tue, 8 Oct 2024 18:26:18 +0900 Subject: [PATCH 001/136] introduce Synthesize action --- Lib9c/Action/Synthesize.cs | 258 +++++++++++++++++++++++++++++++++++++ 1 file changed, 258 insertions(+) create mode 100644 Lib9c/Action/Synthesize.cs diff --git a/Lib9c/Action/Synthesize.cs b/Lib9c/Action/Synthesize.cs new file mode 100644 index 0000000000..424a8e94ca --- /dev/null +++ b/Lib9c/Action/Synthesize.cs @@ -0,0 +1,258 @@ +#nullable enable + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using Bencodex.Types; +using Libplanet.Action; +using Libplanet.Action.State; +using Libplanet.Crypto; +using Nekoyume.Extensions; +using Nekoyume.Model.Item; +using Nekoyume.Model.State; +using Nekoyume.Module; +using Nekoyume.TableData; + +namespace Nekoyume.Action +{ + [Serializable] + [ActionType(TypeIdentifier)] + public class Synthesize : GameAction + { + private const string TypeIdentifier = "synthesize"; + + private const string MaterialsKey = "m"; + private const string AvatarAddressKey = "a"; + private const string ItemSubTypeKey = "s"; + + private static readonly ItemType[] InvalidItemType = + { + ItemType.Costume, + ItemType.Equipment, + }; + + private static readonly ItemSubType[] InvalidItemSubType = + { + ItemSubType.FullCostume, + ItemSubType.Title, + ItemSubType.Title, + ItemSubType.Title, + }; + +#region Fields + public List MaterialIds = new(); + public Address AvatarAddress; + // ItemSubType만 체크하면 ItemType은 자동으로 체크된다고 가정 + public int ItemSubTypeValue; +#endregion Fields + + // TODO: 세부 기획 정해지면 그에 맞게 테스트 코드 우선 작성 + public override IWorld Execute(IActionContext context) + { + context.UseGas(1); + var states = context.PreviousState; + + // Collect addresses + var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress); + + // Validate avatar + var agentState = states.GetAgentState(context.Signer); + if (agentState is null) + { + throw new FailedLoadStateException( + $"{addressesHex} Aborted as the agent state of the signer was failed to load." + ); + } + + var avatarState = states.GetAvatarState(AvatarAddress, true, false, false); + if (avatarState is null || !avatarState.agentAddress.Equals(context.Signer)) + { + throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); + } + + // TODO - Use Sheets + + // TODO - TransferAsset (NCG) + + // Select id to equipment + var materialEquipments = new List(); + var materialCostumes = new List(); + foreach (var materialId in MaterialIds) + { + var materialEquipment = GetEquipmentFromId(materialId, avatarState, context, addressesHex); + var materialCostume = GetCostumeFromId(materialId, avatarState, addressesHex); + if (materialEquipment == null || materialCostume == null) + { + throw new InvalidMaterialException( + $"{addressesHex} Aborted as the material item is not a valid item type." + ); + } + + materialEquipments.Add(materialEquipment); + materialCostumes.Add(materialCostume); + } + + // Unequip items + foreach (var materialEquipment in materialEquipments) + { + materialEquipment.Unequip(); + } + foreach (var materialEquipment in materialCostumes) + { + materialEquipment.Unequip(); + } + + // Remove materials + foreach (var materialId in MaterialIds) + { + avatarState.inventory.RemoveNonFungibleItem(materialId); + } + + // clone random item + avatarState.inventory.AddNonFungibleItem(GetSynthesizedItem(context)); + + return states.SetAvatarState(AvatarAddress, avatarState); + } + + // TODO: Use Sheet + private ItemBase GetSynthesizedItem(IActionContext context) + { + // TODO: 기획 상세에 따라 구현, 현재는 임의 아이템 생성 + switch ((ItemSubType)ItemSubTypeValue) + { + case ItemSubType.FullCostume: + return GetRandomFullCostume(context); + case ItemSubType.Title: + return GetRandomTitle(context); + case ItemSubType.Aura: + //return GetRandomAura(context); + case ItemSubType.Grimoire: + //return GetRandomGrimoire(context); + default: + throw new ArgumentOutOfRangeException(); + } + } + + private ItemBase GetRandomFullCostume(IActionContext context) + { + return GetRandomCostume(context, 40100000, 30); + } + + private ItemBase GetRandomTitle(IActionContext context) + { + return GetRandomCostume(context, 49900001, 21); + } + + // TODO: 시트 로드 최적화 + private ItemBase GetRandomCostume(IActionContext context, int keyBase, int keyUpperBound) + { + var random = context.GetRandom(); + var randomId = keyBase + random.Next(0, keyUpperBound); + + Dictionary sheets = context.PreviousState.GetSheets(sheetTypes: new[] + { + typeof(CostumeItemSheet), + }); + var sheet = sheets.GetSheet(); + if (!sheet.TryGetValue(randomId, out var costumeRow)) + { + throw new SheetRowNotFoundException( + $"Aborted as the costume row ({randomId}) was failed to load.", randomId + ); + } + + return ItemFactory.CreateCostume(costumeRow, Guid.NewGuid()); + } + + private Equipment? GetEquipmentFromId(Guid materialId, AvatarState avatarState, IActionContext context, string addressesHex) + { + if (!avatarState.inventory.TryGetNonFungibleItem(materialId, out Equipment materialEquipment)) + { + return null; + } + + if (materialEquipment.RequiredBlockIndex > context.BlockIndex) + { + throw new RequiredBlockIndexException( + $"{addressesHex} Aborted as the material ({materialId}) is not available yet;" + + $" it will be available at the block #{materialEquipment.RequiredBlockIndex}." + ); + } + + // Validate item type + if (InvalidItemType.Contains(materialEquipment.ItemType)) + { + throw new InvalidMaterialException( + $"{addressesHex} Aborted as the material item is not a valid item type: {materialEquipment.ItemType}." + ); + } + + if (InvalidItemSubType.Contains(materialEquipment.ItemSubType)) + { + throw new InvalidMaterialException( + $"{addressesHex} Aborted as the material item is not a valid item sub type: {materialEquipment.ItemSubType}." + ); + } + + var itemSubType = (ItemSubType)ItemSubTypeValue; + if (materialEquipment.ItemSubType != itemSubType) + { + throw new InvalidMaterialException( + $"{addressesHex} Aborted as the material item is not a {itemSubType}, but {materialEquipment.ItemSubType}." + ); + } + + return materialEquipment; + } + + private Costume? GetCostumeFromId(Guid materialId, AvatarState avatarState, string addressesHex) + { + if (!avatarState.inventory.TryGetNonFungibleItem(materialId, out Costume costumeItem)) + { + return null; + } + + // Validate item type + if (InvalidItemType.Contains(costumeItem.ItemType)) + { + throw new InvalidMaterialException( + $"{addressesHex} Aborted as the material item is not a valid item type: {costumeItem.ItemType}." + ); + } + + if (InvalidItemSubType.Contains(costumeItem.ItemSubType)) + { + throw new InvalidMaterialException( + $"{addressesHex} Aborted as the material item is not a valid item sub type: {costumeItem.ItemSubType}." + ); + } + + var itemSubType = (ItemSubType)ItemSubTypeValue; + if (costumeItem.ItemSubType != itemSubType) + { + throw new InvalidMaterialException( + $"{addressesHex} Aborted as the material item is not a {itemSubType}, but {costumeItem.ItemSubType}." + ); + } + + return costumeItem; + } + + protected override IImmutableDictionary PlainValueInternal => + new Dictionary + { + [MaterialsKey] = new List(MaterialIds.OrderBy(i => i).Select(i => i.Serialize())), + [AvatarAddressKey] = AvatarAddress.Serialize(), + [ItemSubTypeKey] = (Integer)ItemSubTypeValue, + } + .ToImmutableDictionary(); + + protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) + { + MaterialIds = plainValue[MaterialsKey].ToList(StateExtensions.ToGuid); + AvatarAddress = plainValue[AvatarAddressKey].ToAddress(); + ItemSubTypeValue= (Integer)plainValue[ItemSubTypeKey]; + } + } +} From afc310fa33804cc01cf33d99271ac4a674661d19 Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Tue, 8 Oct 2024 18:27:56 +0900 Subject: [PATCH 002/136] remove blank --- Lib9c/Action/Synthesize.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib9c/Action/Synthesize.cs b/Lib9c/Action/Synthesize.cs index 424a8e94ca..0b7db1dd4a 100644 --- a/Lib9c/Action/Synthesize.cs +++ b/Lib9c/Action/Synthesize.cs @@ -42,7 +42,7 @@ public class Synthesize : GameAction #region Fields public List MaterialIds = new(); - public Address AvatarAddress; + public Address AvatarAddress; // ItemSubType만 체크하면 ItemType은 자동으로 체크된다고 가정 public int ItemSubTypeValue; #endregion Fields From d5d22868472fbd3d93abaf49f30d4b4ef6876380 Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Tue, 8 Oct 2024 18:32:06 +0900 Subject: [PATCH 003/136] fix item null check --- Lib9c/Action/Synthesize.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Lib9c/Action/Synthesize.cs b/Lib9c/Action/Synthesize.cs index 0b7db1dd4a..0af7400d49 100644 --- a/Lib9c/Action/Synthesize.cs +++ b/Lib9c/Action/Synthesize.cs @@ -82,15 +82,22 @@ public override IWorld Execute(IActionContext context) { var materialEquipment = GetEquipmentFromId(materialId, avatarState, context, addressesHex); var materialCostume = GetCostumeFromId(materialId, avatarState, addressesHex); - if (materialEquipment == null || materialCostume == null) + if (materialEquipment == null && materialCostume == null) { throw new InvalidMaterialException( $"{addressesHex} Aborted as the material item is not a valid item type." ); } - materialEquipments.Add(materialEquipment); - materialCostumes.Add(materialCostume); + if (materialEquipment != null) + { + materialEquipments.Add(materialEquipment); + } + + if (materialCostume != null) + { + materialCostumes.Add(materialCostume); + } } // Unequip items From f2e3ad548f69a62b131d4b455883df6b43a6d59b Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Tue, 8 Oct 2024 18:32:21 +0900 Subject: [PATCH 004/136] add sheet info in exception log --- Lib9c/Action/Synthesize.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib9c/Action/Synthesize.cs b/Lib9c/Action/Synthesize.cs index 0af7400d49..3ce6ac9201 100644 --- a/Lib9c/Action/Synthesize.cs +++ b/Lib9c/Action/Synthesize.cs @@ -165,7 +165,7 @@ private ItemBase GetRandomCostume(IActionContext context, int keyBase, int keyUp if (!sheet.TryGetValue(randomId, out var costumeRow)) { throw new SheetRowNotFoundException( - $"Aborted as the costume row ({randomId}) was failed to load.", randomId + $"Aborted as the costume row ({randomId}) was failed to load in {nameof(CostumeItemSheet)}", randomId ); } From 494d1a67ef7f782f7b4b2a965cc89821259ace50 Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Tue, 8 Oct 2024 18:32:44 +0900 Subject: [PATCH 005/136] fix item sub type --- Lib9c/Action/Synthesize.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib9c/Action/Synthesize.cs b/Lib9c/Action/Synthesize.cs index 3ce6ac9201..670eb954c2 100644 --- a/Lib9c/Action/Synthesize.cs +++ b/Lib9c/Action/Synthesize.cs @@ -36,8 +36,8 @@ public class Synthesize : GameAction { ItemSubType.FullCostume, ItemSubType.Title, - ItemSubType.Title, - ItemSubType.Title, + ItemSubType.Grimoire, + ItemSubType.Aura, }; #region Fields From abcf9852e255ca5196efa010b6e4bd1eed618c5c Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Tue, 15 Oct 2024 15:20:32 +0900 Subject: [PATCH 006/136] add grimoire to dict --- Lib9c.sln.DotSettings | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib9c.sln.DotSettings b/Lib9c.sln.DotSettings index 5334635e0e..cd03b1148a 100644 --- a/Lib9c.sln.DotSettings +++ b/Lib9c.sln.DotSettings @@ -7,6 +7,7 @@ True True True + True True True True From 8c1fe5d4ed5b22d5ed45471baa0913238b017469 Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Tue, 15 Oct 2024 16:15:58 +0900 Subject: [PATCH 007/136] add Get Random Aura and Grimoire --- Lib9c/Action/Synthesize.cs | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/Lib9c/Action/Synthesize.cs b/Lib9c/Action/Synthesize.cs index 670eb954c2..b84c119cad 100644 --- a/Lib9c/Action/Synthesize.cs +++ b/Lib9c/Action/Synthesize.cs @@ -151,7 +151,32 @@ private ItemBase GetRandomTitle(IActionContext context) return GetRandomCostume(context, 49900001, 21); } - // TODO: 시트 로드 최적화 + private ItemBase GetRandomAura(IActionContext context) + { + // TODO: 시트 로드 최적화, 테스트 코드라 냅둠 + Dictionary sheets = context.PreviousState.GetSheets(sheetTypes: new[] + { + typeof(EquipmentItemSheet), + }); + var sheet = sheets.GetSheet(); + + var row = sheet.Values.FirstOrDefault(r => r.ItemSubType == ItemSubType.Aura); + return ItemFactory.CreateItem(row, context.GetRandom()); + } + + private ItemBase GetRandomGrimoire(IActionContext context) + { + // TODO: 시트 로드 최적화, 테스트 코드라 냅둠 + Dictionary sheets = context.PreviousState.GetSheets(sheetTypes: new[] + { + typeof(EquipmentItemSheet), + }); + var sheet = sheets.GetSheet(); + + var row = sheet.Values.FirstOrDefault(r => r.ItemSubType == ItemSubType.Grimoire); + return ItemFactory.CreateItem(row, context.GetRandom()); + } + private ItemBase GetRandomCostume(IActionContext context, int keyBase, int keyUpperBound) { var random = context.GetRandom(); From 1dc6c688e226c8f4327cfaecf76626991caf1049 Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Tue, 15 Oct 2024 16:16:12 +0900 Subject: [PATCH 008/136] fix synthesize action --- Lib9c/Action/Synthesize.cs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/Lib9c/Action/Synthesize.cs b/Lib9c/Action/Synthesize.cs index b84c119cad..bf743a0647 100644 --- a/Lib9c/Action/Synthesize.cs +++ b/Lib9c/Action/Synthesize.cs @@ -26,13 +26,13 @@ public class Synthesize : GameAction private const string AvatarAddressKey = "a"; private const string ItemSubTypeKey = "s"; - private static readonly ItemType[] InvalidItemType = + private static readonly ItemType[] ValidItemType = { ItemType.Costume, ItemType.Equipment, }; - private static readonly ItemSubType[] InvalidItemSubType = + private static readonly ItemSubType[] ValidItemSubType = { ItemSubType.FullCostume, ItemSubType.Title, @@ -119,7 +119,7 @@ public override IWorld Execute(IActionContext context) // clone random item avatarState.inventory.AddNonFungibleItem(GetSynthesizedItem(context)); - return states.SetAvatarState(AvatarAddress, avatarState); + return states.SetAvatarState(AvatarAddress, avatarState, true, true, false, false); } // TODO: Use Sheet @@ -133,9 +133,9 @@ private ItemBase GetSynthesizedItem(IActionContext context) case ItemSubType.Title: return GetRandomTitle(context); case ItemSubType.Aura: - //return GetRandomAura(context); + return GetRandomAura(context); case ItemSubType.Grimoire: - //return GetRandomGrimoire(context); + return GetRandomGrimoire(context); default: throw new ArgumentOutOfRangeException(); } @@ -182,6 +182,7 @@ private ItemBase GetRandomCostume(IActionContext context, int keyBase, int keyUp var random = context.GetRandom(); var randomId = keyBase + random.Next(0, keyUpperBound); + // TODO: 시트 로드 최적화, 테스트 코드라 냅둠 Dictionary sheets = context.PreviousState.GetSheets(sheetTypes: new[] { typeof(CostumeItemSheet), @@ -194,7 +195,7 @@ private ItemBase GetRandomCostume(IActionContext context, int keyBase, int keyUp ); } - return ItemFactory.CreateCostume(costumeRow, Guid.NewGuid()); + return ItemFactory.CreateItem(costumeRow, context.GetRandom()); } private Equipment? GetEquipmentFromId(Guid materialId, AvatarState avatarState, IActionContext context, string addressesHex) @@ -213,14 +214,14 @@ private ItemBase GetRandomCostume(IActionContext context, int keyBase, int keyUp } // Validate item type - if (InvalidItemType.Contains(materialEquipment.ItemType)) + if (!ValidItemType.Contains(materialEquipment.ItemType)) { throw new InvalidMaterialException( $"{addressesHex} Aborted as the material item is not a valid item type: {materialEquipment.ItemType}." ); } - if (InvalidItemSubType.Contains(materialEquipment.ItemSubType)) + if (!ValidItemSubType.Contains(materialEquipment.ItemSubType)) { throw new InvalidMaterialException( $"{addressesHex} Aborted as the material item is not a valid item sub type: {materialEquipment.ItemSubType}." @@ -246,14 +247,14 @@ private ItemBase GetRandomCostume(IActionContext context, int keyBase, int keyUp } // Validate item type - if (InvalidItemType.Contains(costumeItem.ItemType)) + if (!ValidItemType.Contains(costumeItem.ItemType)) { throw new InvalidMaterialException( $"{addressesHex} Aborted as the material item is not a valid item type: {costumeItem.ItemType}." ); } - if (InvalidItemSubType.Contains(costumeItem.ItemSubType)) + if (!ValidItemSubType.Contains(costumeItem.ItemSubType)) { throw new InvalidMaterialException( $"{addressesHex} Aborted as the material item is not a valid item sub type: {costumeItem.ItemSubType}." From 20c8ca8124a38d5b9aa4b6eb0cf8b92683465f7f Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Tue, 15 Oct 2024 16:16:24 +0900 Subject: [PATCH 009/136] add default synthesize test --- .Lib9c.Tests/Action/SynthesizeTest.cs | 197 ++++++++++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 .Lib9c.Tests/Action/SynthesizeTest.cs diff --git a/.Lib9c.Tests/Action/SynthesizeTest.cs b/.Lib9c.Tests/Action/SynthesizeTest.cs new file mode 100644 index 0000000000..8e5e7550bb --- /dev/null +++ b/.Lib9c.Tests/Action/SynthesizeTest.cs @@ -0,0 +1,197 @@ +namespace Lib9c.Tests.Action; + +using System; +using System.Collections.Generic; +using System.Linq; +using Libplanet.Action.State; +using Libplanet.Crypto; +using Libplanet.Mocks; +using Libplanet.Types.Assets; +using Nekoyume; +using Nekoyume.Action; +using Nekoyume.Model.Item; +using Nekoyume.Model.State; +using Nekoyume.Module; +using Nekoyume.TableData; +using Xunit; + +public class SynthesizeTest +{ + private static readonly Dictionary Sheets = + TableSheetsImporter.ImportSheets(); + + private static readonly TableSheets TableSheets = new (Sheets); + + private static int _randomSeed; + +#pragma warning disable CS0618 + // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1419 + private readonly Currency _goldCurrency = Currency.Legacy("NCG", 2, null); +#pragma warning restore CS0618 + + public IWorld Init(out Address agentAddress, out Address avatarAddress, out long blockIndex) + { + agentAddress = new PrivateKey().Address; + avatarAddress = new PrivateKey().Address; + blockIndex = TableSheets.ArenaSheet.Values.First().Round + .OrderBy(x => x.StartBlockIndex) + .First() + .StartBlockIndex; + + var goldCurrencyState = new GoldCurrencyState(_goldCurrency); + var state = new World(MockUtil.MockModernWorldState) + .SetLegacyState(goldCurrencyState.address, goldCurrencyState.Serialize()) + .SetAgentState(agentAddress, new AgentState(agentAddress)); + + foreach (var (key, value) in Sheets) + state = state.SetLegacyState(Addresses.TableSheet.Derive(key), value.Serialize()); + + var gameConfigState = new GameConfigState(Sheets[nameof(GameConfigSheet)]); + var avatarState = AvatarState.Create( + avatarAddress, + agentAddress, + 0, + TableSheets.GetAvatarSheets(), + default + ); + state = state.SetAvatarState(avatarAddress, avatarState); + return state.SetLegacyState(gameConfigState.address, gameConfigState.Serialize()); + } + + [Theory] + [InlineData(new[] { ItemSubType.FullCostume, ItemSubType.FullCostume, ItemSubType.FullCostume })] + [InlineData(new[] { ItemSubType.Title, ItemSubType.Title, ItemSubType.Title })] + [InlineData(new[] { ItemSubType.Grimoire, ItemSubType.Grimoire, ItemSubType.Grimoire })] + [InlineData(new[] { ItemSubType.Aura, ItemSubType.Aura, ItemSubType.Aura })] + public void Execute(ItemSubType[] itemSubTypes) + { + var context = new ActionContext(); + var state = Init(out var agentAddress, out var avatarAddress, out var blockIndex); + (state, var items) = UpdateItemsFromSubType(itemSubTypes, state, avatarAddress); + + var action = new Synthesize() + { + AvatarAddress = avatarAddress, + ItemSubTypeValue = (int)itemSubTypes[0], + MaterialIds = items, + }; + + var ctx = new ActionContext + { + BlockIndex = blockIndex, + PreviousState = state, + RandomSeed = 0, + Signer = agentAddress, + }; + + state = action.Execute(ctx); + var inventory = state.GetInventoryV2(avatarAddress); + + // 현재 아이템 3개를 넣어 1개가 나오는 구조라 이런 형태로 체크, 추후 변경될 수 있음 + Assert.Single(inventory.Items); + Assert.True(inventory.Items.First().item.ItemSubType == itemSubTypes[0]); + } + + [Theory] + [InlineData(new[] { ItemSubType.Aura, ItemSubType.FullCostume, ItemSubType.FullCostume })] + [InlineData(new[] { ItemSubType.Title, ItemSubType.Grimoire, ItemSubType.Title })] + [InlineData(new[] { ItemSubType.Grimoire, ItemSubType.Title, ItemSubType.Grimoire })] + [InlineData(new[] { ItemSubType.Aura, ItemSubType.Aura, ItemSubType.Grimoire })] + public void ExecuteMixedSubTypes(ItemSubType[] itemSubTypes) + { + var context = new ActionContext(); + var state = Init(out var agentAddress, out var avatarAddress, out var blockIndex); + (state, var items) = UpdateItemsFromSubType(itemSubTypes, state, avatarAddress); + + var action = new Synthesize() + { + AvatarAddress = avatarAddress, + ItemSubTypeValue = (int)itemSubTypes[0], + MaterialIds = items, + }; + + var ctx = new ActionContext + { + BlockIndex = blockIndex, + PreviousState = state, + RandomSeed = 0, + Signer = agentAddress, + }; + + Assert.Throws(() => action.Execute(ctx)); + } + + private static (IWorld, List) UpdateItemsFromSubType(ItemSubType[] itemSubTypes, IWorld state, Address avatarAddress) + { + var avatarState = state.GetAvatarState(avatarAddress); + var items = new List(); + foreach (var subType in itemSubTypes) + { + var item = GetItem(subType); + avatarState.inventory.AddItem(item); + + switch (item) + { + case Costume costume: + items.Add(costume.ItemId); + break; + case ItemUsable itemUsable: + items.Add(itemUsable.ItemId); + break; + default: + throw new ArgumentException($"Unexpected item type: {item.GetType()}", nameof(item)); + } + } + + return (state.SetInventory(avatarAddress, avatarState.inventory), items); + } + + private static ItemBase GetItem(ItemSubType type) + { + switch (type) + { + case ItemSubType.FullCostume: + return GetFirstCostume(); + case ItemSubType.Title: + return GetFirstTitle(); + case ItemSubType.Aura: + return GetFirstAura(); + case ItemSubType.Grimoire: + return GetFirstGrimoire(); + default: + throw new ArgumentOutOfRangeException(nameof(type), type, null); + } + } + + private static ItemBase GetFirstCostume() + { + var row = TableSheets.CostumeItemSheet.FirstOrDefault(r => r.Value.ItemSubType == ItemSubType.FullCostume).Value; + Assert.NotNull(row); + + return ItemFactory.CreateItem(row, new TestRandom(_randomSeed++)); + } + + private static ItemBase GetFirstTitle() + { + var row = TableSheets.CostumeItemSheet.FirstOrDefault(r => r.Value.ItemSubType == ItemSubType.Title).Value; + Assert.NotNull(row); + + return ItemFactory.CreateItem(row, new TestRandom(_randomSeed++)); + } + + private static ItemBase GetFirstAura() + { + var row = TableSheets.EquipmentItemSheet.FirstOrDefault(r => r.Value.ItemSubType == ItemSubType.Aura).Value; + Assert.NotNull(row); + + return ItemFactory.CreateItem(row, new TestRandom(_randomSeed++)); + } + + private static ItemBase GetFirstGrimoire() + { + var row = TableSheets.EquipmentItemSheet.FirstOrDefault(r => r.Value.ItemSubType == ItemSubType.Grimoire).Value; + Assert.NotNull(row); + + return ItemFactory.CreateItem(row, new TestRandom(_randomSeed++)); + } +} From 3105ff2697e2c4fc0894e533dcdac4abc3eac300 Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Tue, 15 Oct 2024 16:36:06 +0900 Subject: [PATCH 010/136] add xml comment --- Lib9c/Action/Synthesize.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Lib9c/Action/Synthesize.cs b/Lib9c/Action/Synthesize.cs index bf743a0647..818a70d8a2 100644 --- a/Lib9c/Action/Synthesize.cs +++ b/Lib9c/Action/Synthesize.cs @@ -16,6 +16,10 @@ namespace Nekoyume.Action { + /// + /// Synthesize action is a type of action that synthesizes items. + /// TODO: Implement Synthesize action. + /// [Serializable] [ActionType(TypeIdentifier)] public class Synthesize : GameAction @@ -48,6 +52,11 @@ public class Synthesize : GameAction #endregion Fields // TODO: 세부 기획 정해지면 그에 맞게 테스트 코드 우선 작성 + /// + /// Execute Synthesize action, Input and . + /// Depending on the probability, you can get different items. + /// Success: Return new AvatarState with synthesized item in inventory. + /// public override IWorld Execute(IActionContext context) { context.UseGas(1); From 107910f0b169daa3cef4c1994f06b580f2dcdc7c Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Thu, 17 Oct 2024 11:33:46 +0900 Subject: [PATCH 011/136] remove ItemSubTypeValue serialize field --- .Lib9c.Tests/Action/SynthesizeTest.cs | 2 -- Lib9c/Action/Synthesize.cs | 30 ++++++++++++++------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.Lib9c.Tests/Action/SynthesizeTest.cs b/.Lib9c.Tests/Action/SynthesizeTest.cs index 8e5e7550bb..6e8a12c5a2 100644 --- a/.Lib9c.Tests/Action/SynthesizeTest.cs +++ b/.Lib9c.Tests/Action/SynthesizeTest.cs @@ -72,7 +72,6 @@ public void Execute(ItemSubType[] itemSubTypes) var action = new Synthesize() { AvatarAddress = avatarAddress, - ItemSubTypeValue = (int)itemSubTypes[0], MaterialIds = items, }; @@ -106,7 +105,6 @@ public void ExecuteMixedSubTypes(ItemSubType[] itemSubTypes) var action = new Synthesize() { AvatarAddress = avatarAddress, - ItemSubTypeValue = (int)itemSubTypes[0], MaterialIds = items, }; diff --git a/Lib9c/Action/Synthesize.cs b/Lib9c/Action/Synthesize.cs index 818a70d8a2..6af2de1799 100644 --- a/Lib9c/Action/Synthesize.cs +++ b/Lib9c/Action/Synthesize.cs @@ -28,7 +28,6 @@ public class Synthesize : GameAction private const string MaterialsKey = "m"; private const string AvatarAddressKey = "a"; - private const string ItemSubTypeKey = "s"; private static readonly ItemType[] ValidItemType = { @@ -47,8 +46,8 @@ public class Synthesize : GameAction #region Fields public List MaterialIds = new(); public Address AvatarAddress; - // ItemSubType만 체크하면 ItemType은 자동으로 체크된다고 가정 - public int ItemSubTypeValue; + + private ItemSubType? _cachedItemSubType; #endregion Fields // TODO: 세부 기획 정해지면 그에 맞게 테스트 코드 우선 작성 @@ -109,6 +108,11 @@ public override IWorld Execute(IActionContext context) } } + if (_cachedItemSubType == null) + { + throw new InvalidOperationException("ItemSubType is not set."); + } + // Unequip items foreach (var materialEquipment in materialEquipments) { @@ -126,16 +130,16 @@ public override IWorld Execute(IActionContext context) } // clone random item - avatarState.inventory.AddNonFungibleItem(GetSynthesizedItem(context)); + avatarState.inventory.AddNonFungibleItem(GetSynthesizedItem(context, _cachedItemSubType.Value)); return states.SetAvatarState(AvatarAddress, avatarState, true, true, false, false); } // TODO: Use Sheet - private ItemBase GetSynthesizedItem(IActionContext context) + private ItemBase GetSynthesizedItem(IActionContext context, ItemSubType itemSubTypeValue) { // TODO: 기획 상세에 따라 구현, 현재는 임의 아이템 생성 - switch ((ItemSubType)ItemSubTypeValue) + switch (itemSubTypeValue) { case ItemSubType.FullCostume: return GetRandomFullCostume(context); @@ -237,11 +241,11 @@ private ItemBase GetRandomCostume(IActionContext context, int keyBase, int keyUp ); } - var itemSubType = (ItemSubType)ItemSubTypeValue; - if (materialEquipment.ItemSubType != itemSubType) + _cachedItemSubType ??= materialEquipment.ItemSubType; + if (materialEquipment.ItemSubType != _cachedItemSubType) { throw new InvalidMaterialException( - $"{addressesHex} Aborted as the material item is not a {itemSubType}, but {materialEquipment.ItemSubType}." + $"{addressesHex} Aborted as the material item is not a {_cachedItemSubType}, but {materialEquipment.ItemSubType}." ); } @@ -270,11 +274,11 @@ private ItemBase GetRandomCostume(IActionContext context, int keyBase, int keyUp ); } - var itemSubType = (ItemSubType)ItemSubTypeValue; - if (costumeItem.ItemSubType != itemSubType) + _cachedItemSubType ??= costumeItem.ItemSubType; + if (costumeItem.ItemSubType != _cachedItemSubType) { throw new InvalidMaterialException( - $"{addressesHex} Aborted as the material item is not a {itemSubType}, but {costumeItem.ItemSubType}." + $"{addressesHex} Aborted as the material item is not a {_cachedItemSubType}, but {costumeItem.ItemSubType}." ); } @@ -286,7 +290,6 @@ private ItemBase GetRandomCostume(IActionContext context, int keyBase, int keyUp { [MaterialsKey] = new List(MaterialIds.OrderBy(i => i).Select(i => i.Serialize())), [AvatarAddressKey] = AvatarAddress.Serialize(), - [ItemSubTypeKey] = (Integer)ItemSubTypeValue, } .ToImmutableDictionary(); @@ -294,7 +297,6 @@ protected override void LoadPlainValueInternal(IImmutableDictionary Date: Sun, 27 Oct 2024 16:34:03 +0900 Subject: [PATCH 012/136] hack_and_slash --- .../lib9c/src/actions/hack_and_slash.ts | 76 +++++++++++++++++++ .../tests/actions/hack_and_slash.test.ts | 25 ++++++ 2 files changed, 101 insertions(+) create mode 100644 integrations/javascript/@planetarium/lib9c/src/actions/hack_and_slash.ts create mode 100644 integrations/javascript/@planetarium/lib9c/tests/actions/hack_and_slash.test.ts diff --git a/integrations/javascript/@planetarium/lib9c/src/actions/hack_and_slash.ts b/integrations/javascript/@planetarium/lib9c/src/actions/hack_and_slash.ts new file mode 100644 index 0000000000..a8cf8d548b --- /dev/null +++ b/integrations/javascript/@planetarium/lib9c/src/actions/hack_and_slash.ts @@ -0,0 +1,76 @@ +import { BencodexDictionary, Value, type Dictionary } from "@planetarium/bencodex"; +import { GameAction, type GameActionArgs } from "./common.js"; +import type { Address } from "@planetarium/account"; +import { CreateAvatarArgs } from "./create_avatar.js"; +import { RuneSlotInfo } from "../models/rune_slot_info.js"; + + +export type HackAndSlashArgs = { + costumes: Array; + equipments: Array; + foods: Array; + runeInfos: Array; + worldId: bigint; + stageId: bigint; + stageBuffId: bigint | null; + avatarAddress:Address; + totalPlayCount:bigint; + apStoneCount:bigint; +} & GameActionArgs; + +export class HackAndSlash extends GameAction { + protected readonly type_id: string = "hack_and_slash22"; + + public readonly avatarAddress: Address; + public readonly worldId: bigint; + public readonly stageId: bigint; + public readonly costumes: Array; + public readonly equipments: Array; + public readonly foods: Array; + public readonly totalPlayCount: bigint; + public readonly apStoneCount: bigint; + public readonly runeInfos: Array; + public readonly stageBuffId: bigint | null; + + + constructor({ avatarAddress, worldId, stageId, equipments, foods, totalPlayCount, apStoneCount, runeInfos, stageBuffId, costumes, id }: HackAndSlashArgs) { + super({ id }); + this.costumes = costumes; + this.avatarAddress = avatarAddress; + this.worldId = worldId; + this.stageId = stageId; + this.equipments = equipments; + this.foods = foods; + this.totalPlayCount = totalPlayCount; + this.apStoneCount = apStoneCount; + this.runeInfos = runeInfos; + this.stageBuffId = stageBuffId; + } + + protected plain_value_internal(): Dictionary { + var data = new BencodexDictionary([ + ["avatarAddress", this.avatarAddress.toBytes()], + ["equipments", this.equipments.map((equipment) => equipment)], + ["costumes", this.costumes.map((costume) => costume)], + ["foods", this.foods.map((food) => food)], + ["r", this.runeInfos.map((rune) => rune.serialize())], + ["worldId", this.worldId.toString()], + ["stageId", this.stageId.toString()], + ["totalPlayCount", this.totalPlayCount.toString()], + ["apStoneCount", this.apStoneCount.toString()] + ]); + + if (this.stageBuffId !== null) { + (data as any)["stageBuffId"] = this.stageBuffId.toString(); + } + + + return data + } + + + +} + + +//stageBuffId \ No newline at end of file diff --git a/integrations/javascript/@planetarium/lib9c/tests/actions/hack_and_slash.test.ts b/integrations/javascript/@planetarium/lib9c/tests/actions/hack_and_slash.test.ts new file mode 100644 index 0000000000..0feb3e6f88 --- /dev/null +++ b/integrations/javascript/@planetarium/lib9c/tests/actions/hack_and_slash.test.ts @@ -0,0 +1,25 @@ +import { describe } from "vitest"; +import { RuneSlotInfo, uuidToGuidBytes } from "../../src/index.js"; +import { runTests } from "./common.js"; +import { avatarAddress } from "./fixtures.js"; +import { HackAndSlash } from "../../src/actions/hack_and_slash.js"; + +describe("HackAndSlash", () => { + describe("odin", () => { + runTests("valid case", [ + new HackAndSlash({ + id: uuidToGuidBytes("ae195a5e-b43f-4c6d-8fd3-9f38311a45eb"), + avatarAddress, + worldId: BigInt(1), + stageId: BigInt(1), + stageBuffId: null, + apStoneCount:BigInt(1), + totalPlayCount: BigInt(1), + costumes: [uuidToGuidBytes("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa")], + equipments: [uuidToGuidBytes("bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb")], + foods: [uuidToGuidBytes("bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb")], + runeInfos: [new RuneSlotInfo(BigInt(1), BigInt(1))], + }), + ]); + }); +}); From 3378340cb80660050a3ebb9cebac677bc7005f2e Mon Sep 17 00:00:00 2001 From: Hyun Seungmin Date: Tue, 29 Oct 2024 19:36:29 +0900 Subject: [PATCH 013/136] get target exception types for exception serialization test with reflection --- .Lib9c.Tests/Action/ExceptionTest.cs | 84 +++++++--------------------- 1 file changed, 21 insertions(+), 63 deletions(-) diff --git a/.Lib9c.Tests/Action/ExceptionTest.cs b/.Lib9c.Tests/Action/ExceptionTest.cs index 3706d83d89..0f82552da9 100644 --- a/.Lib9c.Tests/Action/ExceptionTest.cs +++ b/.Lib9c.Tests/Action/ExceptionTest.cs @@ -1,19 +1,16 @@ namespace Lib9c.Tests.Action { using System; + using System.Collections.Generic; using System.IO; + using System.Linq; using System.Runtime.Serialization.Formatters.Binary; using Lib9c.Formatters; using Libplanet.Crypto; using MessagePack; using MessagePack.Resolvers; using Nekoyume.Action; - using Nekoyume.Action.Exceptions; - using Nekoyume.Action.Exceptions.AdventureBoss; - using Nekoyume.Action.Exceptions.Arena; - using Nekoyume.Exceptions; using Nekoyume.Model.State; - using Nekoyume.TableData; using Xunit; public class ExceptionTest @@ -28,65 +25,26 @@ public ExceptionTest() MessagePackSerializer.DefaultOptions = options; } + public static IEnumerable GetExceptions() + { + var t = typeof(Exception); + var exceptions = AppDomain.CurrentDomain.GetAssemblies() + .SelectMany(e => e.GetTypes()) + .Where(e => + e.Namespace is not null && + e.Namespace.Contains("Nekoyume") && + !e.IsAbstract && + e.IsClass && + e.IsAssignableTo(t)) + .ToArray(); + foreach (var e in exceptions) + { + yield return new object[] { e, }; + } + } + [Theory] - [InlineData(typeof(InvalidTradableIdException))] - [InlineData(typeof(AlreadyReceivedException))] - [InlineData(typeof(ArenaNotEndedException))] - [InlineData(typeof(AvatarIndexAlreadyUsedException))] - [InlineData(typeof(FailedLoadStateException))] - [InlineData(typeof(InvalidNamePatternException))] - [InlineData(typeof(CombinationSlotResultNullException))] - [InlineData(typeof(CombinationSlotUnlockException))] - [InlineData(typeof(NotEnoughMaterialException))] - [InlineData(typeof(InvalidPriceException))] - [InlineData(typeof(ItemDoesNotExistException))] - [InlineData(typeof(EquipmentLevelExceededException))] - [InlineData(typeof(DuplicateMaterialException))] - [InlineData(typeof(InvalidMaterialException))] - [InlineData(typeof(ConsumableSlotOutOfRangeException))] - [InlineData(typeof(ConsumableSlotUnlockException))] - [InlineData(typeof(InvalidItemTypeException))] - [InlineData(typeof(InvalidRedeemCodeException))] - [InlineData(typeof(DuplicateRedeemException))] - [InlineData(typeof(SheetRowValidateException))] - [InlineData(typeof(ShopItemExpiredException))] - [InlineData(typeof(InvalidMonsterCollectionRoundException))] - [InlineData(typeof(MonsterCollectionExpiredException))] - [InlineData(typeof(InvalidLevelException))] - [InlineData(typeof(ActionPointExceededException))] - [InlineData(typeof(InvalidItemCountException))] - [InlineData(typeof(DuplicateOrderIdException))] - [InlineData(typeof(OrderIdDoesNotExistException))] - [InlineData(typeof(ActionObsoletedException))] - [InlineData(typeof(FailedLoadSheetException))] - [InlineData(typeof(InvalidEquipmentException))] - [InlineData(typeof(AlreadyRecipeUnlockedException))] - [InlineData(typeof(InvalidRecipeIdException))] - [InlineData(typeof(AlreadyWorldUnlockedException))] - [InlineData(typeof(InvalidActionFieldException))] - [InlineData(typeof(NotEnoughEventDungeonTicketsException))] - [InlineData(typeof(InvalidClaimException))] - [InlineData(typeof(RequiredBlockIntervalException))] - [InlineData(typeof(ActionUnavailableException))] - [InlineData(typeof(InvalidTransferCurrencyException))] - [InlineData(typeof(InvalidCurrencyException))] - [InlineData(typeof(InvalidProductTypeException))] - [InlineData(typeof(ProductNotFoundException))] - [InlineData(typeof(AlreadyContractedException))] - [InlineData(typeof(ItemNotFoundException))] - [InlineData(typeof(NotEnoughItemException))] - [InlineData(typeof(StateNullException))] - [InlineData(typeof(AlreadyClaimedException))] - [InlineData(typeof(ClaimExpiredException))] - [InlineData(typeof(InsufficientStakingException))] - [InlineData(typeof(InvalidAdventureBossSeasonException))] - [InlineData(typeof(InvalidBountyException))] - [InlineData(typeof(MaxInvestmentCountExceededException))] - [InlineData(typeof(PreviousBountyException))] - [InlineData(typeof(SeasonInProgressException))] - [InlineData(typeof(EmptyRewardException))] - [InlineData(typeof(UnsupportedStateException))] - [InlineData(typeof(AlreadyJoinedArenaException))] + [MemberData(nameof(GetExceptions))] public void Exception_Serializable(Type excType) { if (Activator.CreateInstance(excType, "for testing") is Exception exc) From d5bb5909cea0f9578aec551257e8095c92fa8146 Mon Sep 17 00:00:00 2001 From: Hyun Seungmin Date: Tue, 29 Oct 2024 19:36:50 +0900 Subject: [PATCH 014/136] fix tests --- ...ActivatedAccountsDoesNotExistsException.cs | 4 +++ Lib9c/Action/ActivationException.cs | 6 +++- Lib9c/Action/BalanceDoesNotExistsException.cs | 14 ++++++---- Lib9c/Action/InvalidMinterException.cs | 5 +++- Lib9c/Action/InvalidSignatureException.cs | 13 ++++----- .../Action/InvalidTransferMinterException.cs | 4 +++ .../InvalidTransferRecipientException.cs | 21 +++++++------- .../Action/InvalidTransferSignerException.cs | 28 +++++++++---------- ...idTransferUnactivatedRecipientException.cs | 17 ++++++----- .../NotEnoughFungibleAssetValueException.cs | 25 +++++++++-------- Lib9c/Action/NotEnoughRankException.cs | 8 ++++-- ...PendingActivationDoesNotExistsException.cs | 19 ++++++------- Lib9c/Action/PermissionDeniedException.cs | 6 ++-- Lib9c/Action/PermissionException.cs | 6 +++- Lib9c/Action/PolicyExpiredException.cs | 7 +++-- Lib9c/Action/RankingExceededException.cs | 10 +++++-- .../TotalSupplyDoesNotExistException.cs | 14 ++++++---- Lib9c/Battle/WeightedSelector.cs | 6 +++- Lib9c/Exceptions/PetCostNotFoundException.cs | 10 +++++-- Lib9c/TableData/Exceptions.cs | 24 ++++++++-------- 20 files changed, 149 insertions(+), 98 deletions(-) diff --git a/Lib9c/Action/ActivatedAccountsDoesNotExistsException.cs b/Lib9c/Action/ActivatedAccountsDoesNotExistsException.cs index e1b867e8fd..fabe4e5ca6 100644 --- a/Lib9c/Action/ActivatedAccountsDoesNotExistsException.cs +++ b/Lib9c/Action/ActivatedAccountsDoesNotExistsException.cs @@ -10,6 +10,10 @@ public ActivatedAccountsDoesNotExistsException() { } + public ActivatedAccountsDoesNotExistsException(string message) : base(message) + { + } + public ActivatedAccountsDoesNotExistsException( SerializationInfo info, StreamingContext context ) : base(info, context) diff --git a/Lib9c/Action/ActivationException.cs b/Lib9c/Action/ActivationException.cs index 2dc8f5c8ce..d5c5e854b7 100644 --- a/Lib9c/Action/ActivationException.cs +++ b/Lib9c/Action/ActivationException.cs @@ -8,7 +8,11 @@ public abstract class ActivationException : Exception protected ActivationException() { } - + + protected ActivationException(string message) : base(message) + { + } + protected ActivationException(SerializationInfo info, StreamingContext context) : base(info, context) { diff --git a/Lib9c/Action/BalanceDoesNotExistsException.cs b/Lib9c/Action/BalanceDoesNotExistsException.cs index fa7f9ee682..3499dcdc83 100644 --- a/Lib9c/Action/BalanceDoesNotExistsException.cs +++ b/Lib9c/Action/BalanceDoesNotExistsException.cs @@ -8,17 +8,24 @@ namespace Nekoyume.Action [Serializable] public class BalanceDoesNotExistsException : Exception { + public Address Address { get; } + public Currency Currency { get; } + public BalanceDoesNotExistsException(Address address, Currency currency) { Address = address; Currency = currency; } + public BalanceDoesNotExistsException(string message) : base(message) + { + } + protected BalanceDoesNotExistsException(SerializationInfo info, StreamingContext context) : base(info, context) { - Address = (Address) info.GetValue(nameof(Address), typeof(Address)); - Currency = (Currency) info.GetValue(nameof(Currency), typeof(Currency)); + Address = (Address)info.GetValue(nameof(Address), typeof(Address)); + Currency = (Currency)info.GetValue(nameof(Currency), typeof(Currency)); } public override void GetObjectData(SerializationInfo info, StreamingContext context) @@ -27,8 +34,5 @@ public override void GetObjectData(SerializationInfo info, StreamingContext cont info.AddValue(nameof(Address), Address); info.AddValue(nameof(Currency), Currency); } - - public Address Address { get; } - public Currency Currency { get; } } } diff --git a/Lib9c/Action/InvalidMinterException.cs b/Lib9c/Action/InvalidMinterException.cs index cf70e66784..d6d0362b4f 100644 --- a/Lib9c/Action/InvalidMinterException.cs +++ b/Lib9c/Action/InvalidMinterException.cs @@ -16,12 +16,15 @@ public InvalidMinterException() { } - public InvalidMinterException(Address signer) { _signer = signer; } + public InvalidMinterException(string message) : base(message) + { + } + protected InvalidMinterException(SerializationInfo info, StreamingContext context) : base(info, context) { diff --git a/Lib9c/Action/InvalidSignatureException.cs b/Lib9c/Action/InvalidSignatureException.cs index 5bdabd57be..82f766bf65 100644 --- a/Lib9c/Action/InvalidSignatureException.cs +++ b/Lib9c/Action/InvalidSignatureException.cs @@ -17,9 +17,11 @@ public InvalidSignatureException(PendingActivationState pending, byte[] signatur Signature = signature; } - public InvalidSignatureException( - SerializationInfo info, StreamingContext context - ) : base(info, context) + public InvalidSignatureException(string message) : base(message) + { + } + + public InvalidSignatureException(SerializationInfo info, StreamingContext context) : base(info, context) { byte[] rawPending = (byte[])info.GetValue(nameof(Pending), typeof(byte[])); Pending = new PendingActivationState( @@ -28,10 +30,7 @@ public InvalidSignatureException( Signature = (byte[])info.GetValue(nameof(Signature), typeof(byte[])); } - public override void GetObjectData( - SerializationInfo info, - StreamingContext context - ) + public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); info.AddValue(nameof(Pending), new Codec().Encode(Pending.Serialize())); diff --git a/Lib9c/Action/InvalidTransferMinterException.cs b/Lib9c/Action/InvalidTransferMinterException.cs index abba2881d3..c0c1618e1a 100644 --- a/Lib9c/Action/InvalidTransferMinterException.cs +++ b/Lib9c/Action/InvalidTransferMinterException.cs @@ -21,6 +21,10 @@ public InvalidTransferMinterException(IEnumerable
minters, Address send Recipient = recipient; } + public InvalidTransferMinterException(string message) : base(message) + { + } + protected InvalidTransferMinterException(SerializationInfo info, StreamingContext context) : base(info, context) { diff --git a/Lib9c/Action/InvalidTransferRecipientException.cs b/Lib9c/Action/InvalidTransferRecipientException.cs index a2a97e65e4..5abfb5ff8f 100644 --- a/Lib9c/Action/InvalidTransferRecipientException.cs +++ b/Lib9c/Action/InvalidTransferRecipientException.cs @@ -7,17 +7,22 @@ namespace Nekoyume.Action [Serializable] public class InvalidTransferRecipientException : Exception { - public InvalidTransferRecipientException( - Address sender, - Address recipient) + public Address Sender { get; } + + public Address Recipient { get; } + + public InvalidTransferRecipientException(Address sender, Address recipient) { Sender = sender; Recipient = recipient; } - public InvalidTransferRecipientException( - SerializationInfo info, StreamingContext context - ) : base(info, context) + public InvalidTransferRecipientException(string message) : base(message) + { + } + + public InvalidTransferRecipientException(SerializationInfo info, StreamingContext context) : + base(info, context) { Sender = (Address)info.GetValue(nameof(Sender), typeof(Address)); Recipient = (Address)info.GetValue(nameof(Recipient), typeof(Address)); @@ -29,9 +34,5 @@ public override void GetObjectData(SerializationInfo info, StreamingContext cont info.AddValue(nameof(Sender), Sender); info.AddValue(nameof(Recipient), Recipient); } - - public Address Sender { get; } - - public Address Recipient { get; } } } diff --git a/Lib9c/Action/InvalidTransferSignerException.cs b/Lib9c/Action/InvalidTransferSignerException.cs index 1309a11bbb..eb674fcd4a 100644 --- a/Lib9c/Action/InvalidTransferSignerException.cs +++ b/Lib9c/Action/InvalidTransferSignerException.cs @@ -7,19 +7,25 @@ namespace Nekoyume.Action [Serializable] public class InvalidTransferSignerException : Exception { - public InvalidTransferSignerException( - Address txSigner, - Address sender, - Address recipient) + public Address TxSigner { get; } + + public Address Sender { get; } + + public Address Recipient { get; } + + public InvalidTransferSignerException(Address txSigner, Address sender, Address recipient) { TxSigner = txSigner; Sender = sender; Recipient = recipient; } - - public InvalidTransferSignerException( - SerializationInfo info, StreamingContext context - ) : base(info, context) + + public InvalidTransferSignerException(string message) : base(message) + { + } + + public InvalidTransferSignerException(SerializationInfo info, StreamingContext context) : + base(info, context) { TxSigner = (Address)info.GetValue(nameof(TxSigner), typeof(Address)); Sender = (Address)info.GetValue(nameof(Sender), typeof(Address)); @@ -33,11 +39,5 @@ public override void GetObjectData(SerializationInfo info, StreamingContext cont info.AddValue(nameof(Sender), Sender); info.AddValue(nameof(Recipient), Recipient); } - - public Address TxSigner { get; } - - public Address Sender { get; } - - public Address Recipient { get; } } } diff --git a/Lib9c/Action/InvalidTransferUnactivatedRecipientException.cs b/Lib9c/Action/InvalidTransferUnactivatedRecipientException.cs index 84a8238505..3bf450286f 100644 --- a/Lib9c/Action/InvalidTransferUnactivatedRecipientException.cs +++ b/Lib9c/Action/InvalidTransferUnactivatedRecipientException.cs @@ -7,6 +7,10 @@ namespace Nekoyume.Action [Serializable] public class InvalidTransferUnactivatedRecipientException : Exception { + public Address Sender { get; } + + public Address Recipient { get; } + public InvalidTransferUnactivatedRecipientException( Address sender, Address recipient) @@ -15,9 +19,12 @@ public InvalidTransferUnactivatedRecipientException( Recipient = recipient; } - public InvalidTransferUnactivatedRecipientException( - SerializationInfo info, StreamingContext context - ) : base(info, context) + public InvalidTransferUnactivatedRecipientException(string message) : base(message) + { + } + + public InvalidTransferUnactivatedRecipientException(SerializationInfo info, StreamingContext context) : + base(info, context) { Sender = (Address)info.GetValue(nameof(Sender), typeof(Address)); Recipient = (Address)info.GetValue(nameof(Recipient), typeof(Address)); @@ -29,9 +36,5 @@ public override void GetObjectData(SerializationInfo info, StreamingContext cont info.AddValue(nameof(Sender), Sender); info.AddValue(nameof(Recipient), Recipient); } - - public Address Sender { get; } - - public Address Recipient { get; } } } diff --git a/Lib9c/Action/NotEnoughFungibleAssetValueException.cs b/Lib9c/Action/NotEnoughFungibleAssetValueException.cs index b94c22dba8..cbcba38674 100644 --- a/Lib9c/Action/NotEnoughFungibleAssetValueException.cs +++ b/Lib9c/Action/NotEnoughFungibleAssetValueException.cs @@ -9,9 +9,11 @@ namespace Nekoyume.Action [Serializable] public class NotEnoughFungibleAssetValueException : Exception { - public NotEnoughFungibleAssetValueException( - string message, - Exception innerException = null) : + public NotEnoughFungibleAssetValueException(string message) : base(message) + { + } + + public NotEnoughFungibleAssetValueException(string message, Exception innerException) : base(message, innerException) { } @@ -20,8 +22,8 @@ public NotEnoughFungibleAssetValueException( string addressesHex, string require, string current, - Exception innerException = null) - : this( + Exception innerException = null) : + this( $"{addressesHex}Aborted as the signer's balance is" + $" insufficient to pay entrance fee/stake: {current} < {require}.", innerException) @@ -33,8 +35,8 @@ public NotEnoughFungibleAssetValueException( string addressesHex, string require, string current, - Exception innerException = null) - : this( + Exception innerException = null) : + this( $"[{actionType}][{addressesHex}] Aborted as the signer's balance is" + $" insufficient to pay entrance fee/stake: {current} < {require}.", innerException) @@ -91,8 +93,8 @@ public NotEnoughFungibleAssetValueException( string actionType, string addressesHex, BigInteger require, - BigInteger current) - : this( + BigInteger current) : + this( actionType, addressesHex, require.ToString(CultureInfo.InvariantCulture), @@ -100,9 +102,8 @@ public NotEnoughFungibleAssetValueException( { } - public NotEnoughFungibleAssetValueException( - SerializationInfo info, - StreamingContext context) : base(info, context) + public NotEnoughFungibleAssetValueException(SerializationInfo info, StreamingContext context) : + base(info, context) { } } diff --git a/Lib9c/Action/NotEnoughRankException.cs b/Lib9c/Action/NotEnoughRankException.cs index cef42b469b..cf15d7069b 100644 --- a/Lib9c/Action/NotEnoughRankException.cs +++ b/Lib9c/Action/NotEnoughRankException.cs @@ -10,8 +10,12 @@ public NotEnoughRankException() { } - public NotEnoughRankException(SerializationInfo info, StreamingContext context) : base( - info, context) + public NotEnoughRankException(string message) : base(message) + { + } + + public NotEnoughRankException(SerializationInfo info, StreamingContext context) : + base(info, context) { } } diff --git a/Lib9c/Action/PendingActivationDoesNotExistsException.cs b/Lib9c/Action/PendingActivationDoesNotExistsException.cs index 66c7fd6631..3c57471728 100644 --- a/Lib9c/Action/PendingActivationDoesNotExistsException.cs +++ b/Lib9c/Action/PendingActivationDoesNotExistsException.cs @@ -14,20 +14,17 @@ public PendingActivationDoesNotExistsException(Address pendingAddress) PendingAddress = pendingAddress; } - public PendingActivationDoesNotExistsException( - SerializationInfo info, StreamingContext context - ) : base(info, context) + public PendingActivationDoesNotExistsException(string message) : base(message) { - PendingAddress = (Address)info.GetValue( - nameof(PendingAddress), - typeof(Address) - ); } - public override void GetObjectData( - SerializationInfo info, - StreamingContext context - ) + public PendingActivationDoesNotExistsException(SerializationInfo info, StreamingContext context) : + base(info, context) + { + PendingAddress = (Address)info.GetValue(nameof(PendingAddress), typeof(Address)); + } + + public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); info.AddValue(nameof(PendingAddress), PendingAddress); diff --git a/Lib9c/Action/PermissionDeniedException.cs b/Lib9c/Action/PermissionDeniedException.cs index ad76ef4e81..f76c8b46fd 100644 --- a/Lib9c/Action/PermissionDeniedException.cs +++ b/Lib9c/Action/PermissionDeniedException.cs @@ -11,12 +11,14 @@ public class PermissionDeniedException : AdminPermissionException { public Address Signer { get; } - public PermissionDeniedException(AdminState policy, Address signer) - : base(policy) + public PermissionDeniedException(AdminState policy, Address signer) : base(policy) { Signer = signer; } + public PermissionDeniedException(string message) : base(message) + { + } public PermissionDeniedException(SerializationInfo info, StreamingContext context) : base(info, context) { diff --git a/Lib9c/Action/PermissionException.cs b/Lib9c/Action/PermissionException.cs index 6fa965246d..12ceb6830d 100644 --- a/Lib9c/Action/PermissionException.cs +++ b/Lib9c/Action/PermissionException.cs @@ -10,11 +10,15 @@ public abstract class AdminPermissionException : Exception { public AdminState Policy { get; private set; } - public AdminPermissionException(AdminState policy) + protected AdminPermissionException(AdminState policy) { Policy = policy; } + protected AdminPermissionException(string message) : base(message) + { + } + protected AdminPermissionException(SerializationInfo info, StreamingContext context) { Policy = info.GetValue(nameof(Policy)); diff --git a/Lib9c/Action/PolicyExpiredException.cs b/Lib9c/Action/PolicyExpiredException.cs index c7b991e574..3be9fc6869 100644 --- a/Lib9c/Action/PolicyExpiredException.cs +++ b/Lib9c/Action/PolicyExpiredException.cs @@ -10,12 +10,15 @@ public class PolicyExpiredException : AdminPermissionException { public long BlockIndex { get; } - public PolicyExpiredException(AdminState policy, long blockIndex) - : base(policy) + public PolicyExpiredException(AdminState policy, long blockIndex) : base(policy) { BlockIndex = blockIndex; } + public PolicyExpiredException(string message) : base(message) + { + } + public PolicyExpiredException(SerializationInfo info, StreamingContext context) : base(info, context) { BlockIndex = info.GetValue(nameof(BlockIndex)); diff --git a/Lib9c/Action/RankingExceededException.cs b/Lib9c/Action/RankingExceededException.cs index 4020aace43..0d2e7165b4 100644 --- a/Lib9c/Action/RankingExceededException.cs +++ b/Lib9c/Action/RankingExceededException.cs @@ -4,14 +4,18 @@ namespace Nekoyume.Action { [Serializable] - public class RankingExceededException: InvalidOperationException + public class RankingExceededException : InvalidOperationException { public RankingExceededException() { } - protected RankingExceededException(SerializationInfo info, StreamingContext context) - : base(info, context) + public RankingExceededException(string message) : base(message) + { + } + + protected RankingExceededException(SerializationInfo info, StreamingContext context) : + base(info, context) { } } diff --git a/Lib9c/Action/TotalSupplyDoesNotExistException.cs b/Lib9c/Action/TotalSupplyDoesNotExistException.cs index 56516dc601..45958b2034 100644 --- a/Lib9c/Action/TotalSupplyDoesNotExistException.cs +++ b/Lib9c/Action/TotalSupplyDoesNotExistException.cs @@ -7,15 +7,21 @@ namespace Nekoyume.Action [Serializable] public class TotalSupplyDoesNotExistException : Exception { + public Currency Currency { get; } + public TotalSupplyDoesNotExistException(Currency currency) { Currency = currency; } - protected TotalSupplyDoesNotExistException(SerializationInfo info, StreamingContext context) - : base(info, context) + public TotalSupplyDoesNotExistException(string message) : base(message) { - Currency = (Currency) info.GetValue(nameof(Currency), typeof(Currency)); + } + + protected TotalSupplyDoesNotExistException(SerializationInfo info, StreamingContext context) : + base(info, context) + { + Currency = (Currency)info.GetValue(nameof(Currency), typeof(Currency)); } public override void GetObjectData(SerializationInfo info, StreamingContext context) @@ -23,7 +29,5 @@ public override void GetObjectData(SerializationInfo info, StreamingContext cont base.GetObjectData(info, context); info.AddValue(nameof(Currency), Currency); } - - public Currency Currency { get; } } } diff --git a/Lib9c/Battle/WeightedSelector.cs b/Lib9c/Battle/WeightedSelector.cs index c0c38bc05f..ed7c43c335 100644 --- a/Lib9c/Battle/WeightedSelector.cs +++ b/Lib9c/Battle/WeightedSelector.cs @@ -154,7 +154,11 @@ private void Validate(int count) public class InvalidCountException : InvalidOperationException { - public InvalidCountException() : base("count must be greater than 0.") + public InvalidCountException() : this("count must be greater than 0.") + { + } + + public InvalidCountException(string message) : base(message) { } } diff --git a/Lib9c/Exceptions/PetCostNotFoundException.cs b/Lib9c/Exceptions/PetCostNotFoundException.cs index 1895921e2b..88ece85c79 100644 --- a/Lib9c/Exceptions/PetCostNotFoundException.cs +++ b/Lib9c/Exceptions/PetCostNotFoundException.cs @@ -5,7 +5,11 @@ namespace Nekoyume.Exceptions [Serializable] public class PetCostNotFoundException : Exception { - public PetCostNotFoundException(string message, Exception innerException = null) : + public PetCostNotFoundException(string message) : base(message) + { + } + + public PetCostNotFoundException(string message, Exception innerException) : base(message, innerException) { } @@ -14,8 +18,8 @@ public PetCostNotFoundException( string actionType, string addressesHex, string message, - Exception innerException = null) - : base( + Exception innerException = null) : + base( $"[{actionType}][{addressesHex}] {message}", innerException) { diff --git a/Lib9c/TableData/Exceptions.cs b/Lib9c/TableData/Exceptions.cs index 990965d978..67f841068c 100644 --- a/Lib9c/TableData/Exceptions.cs +++ b/Lib9c/TableData/Exceptions.cs @@ -47,13 +47,13 @@ public SheetRowNotFoundException(string sheetName, string key) } public SheetRowNotFoundException(string sheetName, string condition, string value) : - base($"{sheetName}: {condition} - {value}") + this($"{sheetName}: {condition} - {value}") { } - public SheetRowNotFoundException(string addressesHex, string sheetName, int intKey) - : base($"{addressesHex}{sheetName}:" + - $" Key - {intKey.ToString(CultureInfo.InvariantCulture)}") + public SheetRowNotFoundException(string addressesHex, string sheetName, int intKey) : + this($"{addressesHex}{sheetName}:" + + $" Key - {intKey.ToString(CultureInfo.InvariantCulture)}") { } @@ -61,16 +61,18 @@ public SheetRowNotFoundException( string actionType, string addressesHex, string sheetName, - int intKey) - : base($"[{actionType}][{addressesHex}]{sheetName}:" + - $" Key - {intKey.ToString(CultureInfo.InvariantCulture)}") + int intKey) : + this($"[{actionType}][{addressesHex}]{sheetName}:" + + $" Key - {intKey.ToString(CultureInfo.InvariantCulture)}") { } - protected SheetRowNotFoundException( - SerializationInfo info, - StreamingContext context) - : base(info, context) + public SheetRowNotFoundException(string message) : base(message) + { + } + + protected SheetRowNotFoundException(SerializationInfo info, StreamingContext context) : + base(info, context) { } } From ef609f71532f06d2bd73663b1824afb9d783eaec Mon Sep 17 00:00:00 2001 From: Hyun Seungmin Date: Mon, 4 Nov 2024 09:56:36 +0900 Subject: [PATCH 015/136] ignore several types of exception --- .Lib9c.Tests/Action/ExceptionTest.cs | 29 ++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/.Lib9c.Tests/Action/ExceptionTest.cs b/.Lib9c.Tests/Action/ExceptionTest.cs index 2b5df76bf3..9d192cc191 100644 --- a/.Lib9c.Tests/Action/ExceptionTest.cs +++ b/.Lib9c.Tests/Action/ExceptionTest.cs @@ -39,6 +39,35 @@ e.Namespace is not null && .ToArray(); foreach (var e in exceptions) { + if (e == typeof(InvalidSignatureException)) + { + // FIXME + // Error Message: + // MessagePack.MessagePackSerializationException : Failed to serialize System.Exception value. + // ---- System.NullReferenceException : Object reference not set to an instance of an object. + continue; + } + + if (e == typeof(BalanceDoesNotExistsException)) + { + // FIXME + // Error Message: + // MessagePack.MessagePackSerializationException : Failed to serialize System.Exception value. + // ---- MessagePack.MessagePackSerializationException : Failed to serialize Libplanet.Types.Assets.Currency value. + // -------- MessagePack.FormatterNotRegisteredException : Libplanet.Types.Assets.Currency is not registered in resolver: MessagePack.Resolvers.CompositeResolver+CachingResolver + continue; + } + + if (e == typeof(TotalSupplyDoesNotExistException)) + { + // FIXME + // Error Message: + // MessagePack.MessagePackSerializationException : Failed to serialize System.Exception value. + // ---- MessagePack.MessagePackSerializationException : Failed to serialize Libplanet.Types.Assets.Currency value. + // -------- MessagePack.FormatterNotRegisteredException : Libplanet.Types.Assets.Currency is not registered in resolver: MessagePack.Resolvers.CompositeResolver+CachingResolver + continue; + } + yield return new object[] { e, }; } } From 6ba2f80f2aecd710ccbdd71f7c776df5c4862896 Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Mon, 4 Nov 2024 21:03:21 +0900 Subject: [PATCH 016/136] remove skip exception types --- .Lib9c.Tests/Action/ExceptionTest.cs | 29 ---------------------------- 1 file changed, 29 deletions(-) diff --git a/.Lib9c.Tests/Action/ExceptionTest.cs b/.Lib9c.Tests/Action/ExceptionTest.cs index 9d192cc191..2b5df76bf3 100644 --- a/.Lib9c.Tests/Action/ExceptionTest.cs +++ b/.Lib9c.Tests/Action/ExceptionTest.cs @@ -39,35 +39,6 @@ e.Namespace is not null && .ToArray(); foreach (var e in exceptions) { - if (e == typeof(InvalidSignatureException)) - { - // FIXME - // Error Message: - // MessagePack.MessagePackSerializationException : Failed to serialize System.Exception value. - // ---- System.NullReferenceException : Object reference not set to an instance of an object. - continue; - } - - if (e == typeof(BalanceDoesNotExistsException)) - { - // FIXME - // Error Message: - // MessagePack.MessagePackSerializationException : Failed to serialize System.Exception value. - // ---- MessagePack.MessagePackSerializationException : Failed to serialize Libplanet.Types.Assets.Currency value. - // -------- MessagePack.FormatterNotRegisteredException : Libplanet.Types.Assets.Currency is not registered in resolver: MessagePack.Resolvers.CompositeResolver+CachingResolver - continue; - } - - if (e == typeof(TotalSupplyDoesNotExistException)) - { - // FIXME - // Error Message: - // MessagePack.MessagePackSerializationException : Failed to serialize System.Exception value. - // ---- MessagePack.MessagePackSerializationException : Failed to serialize Libplanet.Types.Assets.Currency value. - // -------- MessagePack.FormatterNotRegisteredException : Libplanet.Types.Assets.Currency is not registered in resolver: MessagePack.Resolvers.CompositeResolver+CachingResolver - continue; - } - yield return new object[] { e, }; } } From c0c94dfeb29e06bffc96821afc595cda241fe51c Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Wed, 6 Nov 2024 21:22:42 +0900 Subject: [PATCH 017/136] add synthesize result pool --- Lib9c/Action/Synthesize.cs | 185 +++++++++++++++++++++++++++---------- 1 file changed, 134 insertions(+), 51 deletions(-) diff --git a/Lib9c/Action/Synthesize.cs b/Lib9c/Action/Synthesize.cs index 6af2de1799..144f2a2dd2 100644 --- a/Lib9c/Action/Synthesize.cs +++ b/Lib9c/Action/Synthesize.cs @@ -11,11 +11,14 @@ using Nekoyume.Extensions; using Nekoyume.Model.Item; using Nekoyume.Model.State; +using Nekoyume.Model.EnumType; using Nekoyume.Module; using Nekoyume.TableData; namespace Nekoyume.Action { + using Sheets = Dictionary; + /// /// Synthesize action is a type of action that synthesizes items. /// TODO: Implement Synthesize action. @@ -79,11 +82,18 @@ public override IWorld Execute(IActionContext context) throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); } - // TODO - Use Sheets - + // Use Sheets + Sheets sheets = context.PreviousState.GetSheets(sheetTypes: new[] + { + typeof(CostumeItemSheet), + typeof(EquipmentItemSheet), + }); + // TODO - TransferAsset (NCG) // Select id to equipment + var sourceGrade = Grade.Normal; // TODO: change to set + var materialEquipments = new List(); var materialCostumes = new List(); foreach (var materialId in MaterialIds) @@ -100,11 +110,13 @@ public override IWorld Execute(IActionContext context) if (materialEquipment != null) { materialEquipments.Add(materialEquipment); + sourceGrade = (Grade)materialEquipment.Grade; } if (materialCostume != null) { materialCostumes.Add(materialCostume); + sourceGrade = (Grade)materialCostume.Grade; } } @@ -130,81 +142,68 @@ public override IWorld Execute(IActionContext context) } // clone random item - avatarState.inventory.AddNonFungibleItem(GetSynthesizedItem(context, _cachedItemSubType.Value)); + // TODO: Add items to inventory + avatarState.inventory.AddNonFungibleItem(GetSynthesizedItem(sourceGrade, sheets, context, _cachedItemSubType.Value)); return states.SetAvatarState(AvatarAddress, avatarState, true, true, false, false); } - // TODO: Use Sheet - private ItemBase GetSynthesizedItem(IActionContext context, ItemSubType itemSubTypeValue) + // TODO: Use Sheet, grade의 set화 + private ItemBase GetSynthesizedItem(Grade grade, Sheets sheets, IActionContext context, ItemSubType itemSubTypeValue) { - // TODO: 기획 상세에 따라 구현, 현재는 임의 아이템 생성 switch (itemSubTypeValue) { case ItemSubType.FullCostume: - return GetRandomFullCostume(context); + return GetRandomCostume(grade, itemSubTypeValue, sheets, context); case ItemSubType.Title: - return GetRandomTitle(context); + return GetRandomCostume(grade, itemSubTypeValue, sheets, context); case ItemSubType.Aura: - return GetRandomAura(context); + return GetRandomEquipment(grade, itemSubTypeValue, sheets, context); case ItemSubType.Grimoire: - return GetRandomGrimoire(context); + return GetRandomEquipment(grade, itemSubTypeValue, sheets, context); default: throw new ArgumentOutOfRangeException(); } } - private ItemBase GetRandomFullCostume(IActionContext context) - { - return GetRandomCostume(context, 40100000, 30); - } - - private ItemBase GetRandomTitle(IActionContext context) - { - return GetRandomCostume(context, 49900001, 21); - } - - private ItemBase GetRandomAura(IActionContext context) + private ItemBase GetRandomCostume(Grade grade, ItemSubType itemSubType, Sheets sheets, IActionContext context) { - // TODO: 시트 로드 최적화, 테스트 코드라 냅둠 - Dictionary sheets = context.PreviousState.GetSheets(sheetTypes: new[] + var sheet = sheets.GetSheet(); + var random = context.GetRandom(); + var synthesizeResultPool = GetSynthesizeResultPool( + new List() { grade, }, + itemSubType, + sheet + ); + + // TODO: add weight + var randomId = synthesizeResultPool[random.Next(synthesizeResultPool.Count)]; + if (!sheet.TryGetValue(randomId, out var costumeRow)) { - typeof(EquipmentItemSheet), - }); - var sheet = sheets.GetSheet(); + throw new SheetRowNotFoundException( + $"Aborted as the costume row ({randomId}) was failed to load in {nameof(CostumeItemSheet)}", randomId + ); + } - var row = sheet.Values.FirstOrDefault(r => r.ItemSubType == ItemSubType.Aura); - return ItemFactory.CreateItem(row, context.GetRandom()); + return ItemFactory.CreateItem(costumeRow, context.GetRandom()); } - private ItemBase GetRandomGrimoire(IActionContext context) + private ItemBase GetRandomEquipment(Grade grade, ItemSubType itemSubType, Sheets sheets, IActionContext context) { - // TODO: 시트 로드 최적화, 테스트 코드라 냅둠 - Dictionary sheets = context.PreviousState.GetSheets(sheetTypes: new[] - { - typeof(EquipmentItemSheet), - }); var sheet = sheets.GetSheet(); - - var row = sheet.Values.FirstOrDefault(r => r.ItemSubType == ItemSubType.Grimoire); - return ItemFactory.CreateItem(row, context.GetRandom()); - } - - private ItemBase GetRandomCostume(IActionContext context, int keyBase, int keyUpperBound) - { - var random = context.GetRandom(); - var randomId = keyBase + random.Next(0, keyUpperBound); - - // TODO: 시트 로드 최적화, 테스트 코드라 냅둠 - Dictionary sheets = context.PreviousState.GetSheets(sheetTypes: new[] - { - typeof(CostumeItemSheet), - }); - var sheet = sheets.GetSheet(); + var random = context.GetRandom(); + var synthesizeResultPool = GetSynthesizeResultPool( + new List() { grade, }, + itemSubType, + sheet + ); + + // TODO: add weight + var randomId = synthesizeResultPool[random.Next(synthesizeResultPool.Count)]; if (!sheet.TryGetValue(randomId, out var costumeRow)) { throw new SheetRowNotFoundException( - $"Aborted as the costume row ({randomId}) was failed to load in {nameof(CostumeItemSheet)}", randomId + $"Aborted as the equipment row ({randomId}) was failed to load in {nameof(EquipmentItemSheet)}", randomId ); } @@ -298,5 +297,89 @@ protected override void LoadPlainValueInternal(IImmutableDictionary + /// Returns a list of items that may come out as a result of that synthesis. + /// + /// grades of material items + /// excepted FullCostume,Title + /// CostumeItemSheet to use + /// list of items key(int) + public static List GetSynthesizeResultPool(List sourceGrades, ItemSubType subType, CostumeItemSheet sheet) + { + return sheet.Values + .Where(r => r.ItemSubType == subType) + .Where(r => sourceGrades.Any(grade => (Grade)r.Grade == GetUpgradeGrade(grade, subType, sheet))) + .Select(r => r.Id) + .ToList(); + } + + /// + /// Returns a list of items that may come out as a result of that synthesis. + /// + /// grades of material items + /// excepted Grimoire,Aura + /// EquipmentItemSheet to use + /// list of items key(int) + public static List GetSynthesizeResultPool(List sourceGrades, ItemSubType subType, EquipmentItemSheet sheet) + { + return sheet.Values + .Where(r => r.ItemSubType == subType) + .Where(r => sourceGrades.Any(grade => (Grade)r.Grade == GetUpgradeGrade(grade, subType, sheet))) + .Select(r => r.Id) + .ToList(); + } + + public static Grade GetUpgradeGrade(Grade grade ,ItemSubType subType, CostumeItemSheet sheet) + { + var targetGrade = GetTargetGrade(grade); + var hasGrade = sheet.Values + .Any(r => r.ItemSubType == subType && r.Grade == (int)targetGrade); + + return hasGrade ? targetGrade : grade; + } + + public static Grade GetUpgradeGrade(Grade grade ,ItemSubType subType, EquipmentItemSheet sheet) + { + var targetGrade = GetTargetGrade(grade); + var hasGrade = sheet.Values + .Any(r => r.ItemSubType == subType && r.Grade == (int)targetGrade); + + return hasGrade ? targetGrade : grade; + } + + private static Grade GetTargetGrade(Grade grade) => grade switch + { + Grade.Normal => Grade.Rare, + Grade.Rare => Grade.Epic, + Grade.Epic => Grade.Unique, + Grade.Unique => Grade.Legendary, + Grade.Legendary => Grade.Divinity, + Grade.Divinity => Grade.Divinity, + _ => throw new ArgumentOutOfRangeException(nameof(grade), grade, null), + }; + + // TODO: move to ItemExtensions + public static List GetItemGuid(ItemBase itemBase) => itemBase switch + { + Costume costume => new List { costume.ItemId, }, + ItemUsable itemUsable => new List { itemUsable.ItemId, }, + _ => throw new ArgumentException($"Unexpected item type: {itemBase.GetType()}", nameof(itemBase)), + }; + + public static List GetItemGuids(IEnumerable itemBases) => itemBases.Select( + i => + { + return i switch + { + Costume costume => costume.ItemId, + ItemUsable itemUsable => itemUsable.ItemId, + _ => throw new ArgumentException($"Unexpected item type: {i.GetType()}", nameof(i)), + }; + }).ToList(); + +#endregion Helper } } From a9feb6a3fc34f1489e1fdc25d7f43b10a2354fe9 Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Wed, 6 Nov 2024 21:22:47 +0900 Subject: [PATCH 018/136] add synthesize result pool test --- .Lib9c.Tests/Action/SynthesizeTest.cs | 124 +++++++++++++++++++------- 1 file changed, 91 insertions(+), 33 deletions(-) diff --git a/.Lib9c.Tests/Action/SynthesizeTest.cs b/.Lib9c.Tests/Action/SynthesizeTest.cs index 6e8a12c5a2..a56ff594f5 100644 --- a/.Lib9c.Tests/Action/SynthesizeTest.cs +++ b/.Lib9c.Tests/Action/SynthesizeTest.cs @@ -11,10 +11,13 @@ using Nekoyume.Action; using Nekoyume.Model.Item; using Nekoyume.Model.State; +using Nekoyume.Model.EnumType; using Nekoyume.Module; using Nekoyume.TableData; using Xunit; +using Sheets = System.Collections.Generic.Dictionary; + public class SynthesizeTest { private static readonly Dictionary Sheets = @@ -59,20 +62,32 @@ public IWorld Init(out Address agentAddress, out Address avatarAddress, out long } [Theory] - [InlineData(new[] { ItemSubType.FullCostume, ItemSubType.FullCostume, ItemSubType.FullCostume })] - [InlineData(new[] { ItemSubType.Title, ItemSubType.Title, ItemSubType.Title })] - [InlineData(new[] { ItemSubType.Grimoire, ItemSubType.Grimoire, ItemSubType.Grimoire })] - [InlineData(new[] { ItemSubType.Aura, ItemSubType.Aura, ItemSubType.Aura })] - public void Execute(ItemSubType[] itemSubTypes) + [InlineData((Grade)3, new[] { ItemSubType.FullCostume, ItemSubType.FullCostume, ItemSubType.FullCostume, })] + [InlineData((Grade)4, new[] { ItemSubType.FullCostume, ItemSubType.FullCostume, ItemSubType.FullCostume, })] + [InlineData((Grade)5, new[] { ItemSubType.FullCostume, ItemSubType.FullCostume, ItemSubType.FullCostume, })] + [InlineData((Grade)3, new[] { ItemSubType.Title, ItemSubType.Title, ItemSubType.Title, })] + [InlineData((Grade)4, new[] { ItemSubType.Title, ItemSubType.Title, ItemSubType.Title, })] + [InlineData((Grade)5, new[] { ItemSubType.Title, ItemSubType.Title, ItemSubType.Title, })] + [InlineData((Grade)3, new[] { ItemSubType.Grimoire, ItemSubType.Grimoire, ItemSubType.Grimoire, })] + [InlineData((Grade)4, new[] { ItemSubType.Grimoire, ItemSubType.Grimoire, ItemSubType.Grimoire, })] + [InlineData((Grade)5, new[] { ItemSubType.Grimoire, ItemSubType.Grimoire, ItemSubType.Grimoire, })] + [InlineData((Grade)6, new[] { ItemSubType.Grimoire, ItemSubType.Grimoire, ItemSubType.Grimoire, })] + [InlineData((Grade)1, new[] { ItemSubType.Aura, ItemSubType.Aura, ItemSubType.Aura, })] + [InlineData((Grade)2, new[] { ItemSubType.Aura, ItemSubType.Aura, ItemSubType.Aura, })] + [InlineData((Grade)3, new[] { ItemSubType.Aura, ItemSubType.Aura, ItemSubType.Aura, })] + [InlineData((Grade)4, new[] { ItemSubType.Aura, ItemSubType.Aura, ItemSubType.Aura, })] + [InlineData((Grade)5, new[] { ItemSubType.Aura, ItemSubType.Aura, ItemSubType.Aura, })] + [InlineData((Grade)6, new[] { ItemSubType.Aura, ItemSubType.Aura, ItemSubType.Aura, })] + public void Execute(Grade grade, ItemSubType[] itemSubTypes) { var context = new ActionContext(); var state = Init(out var agentAddress, out var avatarAddress, out var blockIndex); - (state, var items) = UpdateItemsFromSubType(itemSubTypes, state, avatarAddress); + (state, var items) = UpdateItemsFromSubType(grade, itemSubTypes, state, avatarAddress); var action = new Synthesize() { AvatarAddress = avatarAddress, - MaterialIds = items, + MaterialIds = Synthesize.GetItemGuids(items), }; var ctx = new ActionContext @@ -86,26 +101,57 @@ public void Execute(ItemSubType[] itemSubTypes) state = action.Execute(ctx); var inventory = state.GetInventoryV2(avatarAddress); + // Tests // 현재 아이템 3개를 넣어 1개가 나오는 구조라 이런 형태로 체크, 추후 변경될 수 있음 Assert.Single(inventory.Items); - Assert.True(inventory.Items.First().item.ItemSubType == itemSubTypes[0]); + var firstItem = inventory.Items.First().item; + Assert.True(firstItem.ItemSubType == itemSubTypes[0]); + + var subType = firstItem.ItemSubType; + var exceptedGrade = Grade.Normal; + var resultGrade = (Grade)firstItem.Grade; + switch (subType) + { + case ItemSubType.FullCostume: + case ItemSubType.Title: + if (firstItem is not Costume costume) + { + throw new InvalidCastException("Invalid item type, is not Costume"); + } + + resultGrade = (Grade)costume.Grade; + exceptedGrade = Synthesize.GetUpgradeGrade(grade, subType, TableSheets.CostumeItemSheet); + break; + case ItemSubType.Aura: + case ItemSubType.Grimoire: + if (firstItem is not ItemUsable itemUsable) + { + throw new InvalidCastException("Invalid item type, is not ItemUsable"); + } + + resultGrade = (Grade)itemUsable.Grade; + exceptedGrade = Synthesize.GetUpgradeGrade(grade, subType, TableSheets.EquipmentItemSheet); + break; + } + + Assert.Equal(exceptedGrade, resultGrade); } [Theory] - [InlineData(new[] { ItemSubType.Aura, ItemSubType.FullCostume, ItemSubType.FullCostume })] - [InlineData(new[] { ItemSubType.Title, ItemSubType.Grimoire, ItemSubType.Title })] - [InlineData(new[] { ItemSubType.Grimoire, ItemSubType.Title, ItemSubType.Grimoire })] - [InlineData(new[] { ItemSubType.Aura, ItemSubType.Aura, ItemSubType.Grimoire })] - public void ExecuteMixedSubTypes(ItemSubType[] itemSubTypes) + [InlineData((Grade)3, new[] { ItemSubType.Aura, ItemSubType.FullCostume, ItemSubType.FullCostume })] + [InlineData((Grade)3, new[] { ItemSubType.Title, ItemSubType.Grimoire, ItemSubType.Title })] + [InlineData((Grade)3, new[] { ItemSubType.Grimoire, ItemSubType.Title, ItemSubType.Grimoire })] + [InlineData((Grade)3, new[] { ItemSubType.Aura, ItemSubType.Aura, ItemSubType.Grimoire })] + public void ExecuteMixedSubTypes(Grade grade, ItemSubType[] itemSubTypes) { var context = new ActionContext(); var state = Init(out var agentAddress, out var avatarAddress, out var blockIndex); - (state, var items) = UpdateItemsFromSubType(itemSubTypes, state, avatarAddress); + (state, var items) = UpdateItemsFromSubType(grade, itemSubTypes, state, avatarAddress); var action = new Synthesize() { AvatarAddress = avatarAddress, - MaterialIds = items, + MaterialIds = Synthesize.GetItemGuids(items), }; var ctx = new ActionContext @@ -119,22 +165,22 @@ public void ExecuteMixedSubTypes(ItemSubType[] itemSubTypes) Assert.Throws(() => action.Execute(ctx)); } - private static (IWorld, List) UpdateItemsFromSubType(ItemSubType[] itemSubTypes, IWorld state, Address avatarAddress) + private static (IWorld, List) UpdateItemsFromSubType(Grade grade, ItemSubType[] itemSubTypes, IWorld state, Address avatarAddress) { var avatarState = state.GetAvatarState(avatarAddress); - var items = new List(); + var items = new List(); foreach (var subType in itemSubTypes) { - var item = GetItem(subType); + var item = GetItem(grade, subType); avatarState.inventory.AddItem(item); switch (item) { case Costume costume: - items.Add(costume.ItemId); + items.Add(costume); break; case ItemUsable itemUsable: - items.Add(itemUsable.ItemId); + items.Add(itemUsable); break; default: throw new ArgumentException($"Unexpected item type: {item.GetType()}", nameof(item)); @@ -144,50 +190,62 @@ private static (IWorld, List) UpdateItemsFromSubType(ItemSubType[] itemSub return (state.SetInventory(avatarAddress, avatarState.inventory), items); } - private static ItemBase GetItem(ItemSubType type) + private static ItemBase GetItem(Grade grade, ItemSubType type) { switch (type) { case ItemSubType.FullCostume: - return GetFirstCostume(); + return GetFirstCostume(grade); case ItemSubType.Title: - return GetFirstTitle(); + return GetFirstTitle(grade); case ItemSubType.Aura: - return GetFirstAura(); + return GetFirstAura(grade); case ItemSubType.Grimoire: - return GetFirstGrimoire(); + return GetFirstGrimoire(grade); default: throw new ArgumentOutOfRangeException(nameof(type), type, null); } } - private static ItemBase GetFirstCostume() + private static ItemBase GetFirstCostume(Grade grade) { - var row = TableSheets.CostumeItemSheet.FirstOrDefault(r => r.Value.ItemSubType == ItemSubType.FullCostume).Value; + var sheet = TableSheets.CostumeItemSheet; + var row = sheet + .Where(r => (Grade)r.Value.Grade == grade) + .FirstOrDefault(r => r.Value.ItemSubType == ItemSubType.FullCostume).Value; Assert.NotNull(row); return ItemFactory.CreateItem(row, new TestRandom(_randomSeed++)); } - private static ItemBase GetFirstTitle() + private static ItemBase GetFirstTitle(Grade grade) { - var row = TableSheets.CostumeItemSheet.FirstOrDefault(r => r.Value.ItemSubType == ItemSubType.Title).Value; + var sheet = TableSheets.CostumeItemSheet; + var row = sheet + .Where(r => (Grade)r.Value.Grade == grade) + .FirstOrDefault(r => r.Value.ItemSubType == ItemSubType.Title).Value; Assert.NotNull(row); return ItemFactory.CreateItem(row, new TestRandom(_randomSeed++)); } - private static ItemBase GetFirstAura() + private static ItemBase GetFirstAura(Grade grade) { - var row = TableSheets.EquipmentItemSheet.FirstOrDefault(r => r.Value.ItemSubType == ItemSubType.Aura).Value; + var sheet = TableSheets.EquipmentItemSheet; + var row = sheet + .Where(r => (Grade)r.Value.Grade == grade) + .FirstOrDefault(r => r.Value.ItemSubType == ItemSubType.Aura).Value; Assert.NotNull(row); return ItemFactory.CreateItem(row, new TestRandom(_randomSeed++)); } - private static ItemBase GetFirstGrimoire() + private static ItemBase GetFirstGrimoire(Grade grade) { - var row = TableSheets.EquipmentItemSheet.FirstOrDefault(r => r.Value.ItemSubType == ItemSubType.Grimoire).Value; + var sheet = TableSheets.EquipmentItemSheet; + var row = sheet + .Where(r => (Grade)r.Value.Grade == grade) + .FirstOrDefault(r => r.Value.ItemSubType == ItemSubType.Grimoire).Value; Assert.NotNull(row); return ItemFactory.CreateItem(row, new TestRandom(_randomSeed++)); From 7d6406e8cb6f01f2fdce8c8045cf8998781d19e2 Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Wed, 6 Nov 2024 21:30:06 +0900 Subject: [PATCH 019/136] add testcase --- .Lib9c.Tests/Action/SynthesizeTest.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.Lib9c.Tests/Action/SynthesizeTest.cs b/.Lib9c.Tests/Action/SynthesizeTest.cs index a56ff594f5..853815ebed 100644 --- a/.Lib9c.Tests/Action/SynthesizeTest.cs +++ b/.Lib9c.Tests/Action/SynthesizeTest.cs @@ -134,7 +134,9 @@ public void Execute(Grade grade, ItemSubType[] itemSubTypes) break; } - Assert.Equal(exceptedGrade, resultGrade); + // TODO: if success, grade should be exceptedGrade, but sometimes it is not. + // Assert.Equal(exceptedGrade, resultGrade); + Assert.True(exceptedGrade == resultGrade || resultGrade == grade); } [Theory] From fbbb61edfa8556e0320e5f42bc62a6170ec7ebd7 Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Wed, 6 Nov 2024 23:37:23 +0900 Subject: [PATCH 020/136] refactor code --- .Lib9c.Tests/Action/SynthesizeTest.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.Lib9c.Tests/Action/SynthesizeTest.cs b/.Lib9c.Tests/Action/SynthesizeTest.cs index 853815ebed..16135931b7 100644 --- a/.Lib9c.Tests/Action/SynthesizeTest.cs +++ b/.Lib9c.Tests/Action/SynthesizeTest.cs @@ -108,7 +108,7 @@ public void Execute(Grade grade, ItemSubType[] itemSubTypes) Assert.True(firstItem.ItemSubType == itemSubTypes[0]); var subType = firstItem.ItemSubType; - var exceptedGrade = Grade.Normal; + var expectedGrade = Grade.Normal; var resultGrade = (Grade)firstItem.Grade; switch (subType) { @@ -120,7 +120,7 @@ public void Execute(Grade grade, ItemSubType[] itemSubTypes) } resultGrade = (Grade)costume.Grade; - exceptedGrade = Synthesize.GetUpgradeGrade(grade, subType, TableSheets.CostumeItemSheet); + expectedGrade = Synthesize.GetUpgradeGrade(grade, subType, TableSheets.CostumeItemSheet); break; case ItemSubType.Aura: case ItemSubType.Grimoire: @@ -130,13 +130,13 @@ public void Execute(Grade grade, ItemSubType[] itemSubTypes) } resultGrade = (Grade)itemUsable.Grade; - exceptedGrade = Synthesize.GetUpgradeGrade(grade, subType, TableSheets.EquipmentItemSheet); + expectedGrade = Synthesize.GetUpgradeGrade(grade, subType, TableSheets.EquipmentItemSheet); break; } // TODO: if success, grade should be exceptedGrade, but sometimes it is not. - // Assert.Equal(exceptedGrade, resultGrade); - Assert.True(exceptedGrade == resultGrade || resultGrade == grade); + // Assert.Equal(expectedGrade, resultGrade); + Assert.True(expectedGrade == resultGrade || resultGrade == grade); } [Theory] @@ -205,7 +205,7 @@ private static ItemBase GetItem(Grade grade, ItemSubType type) case ItemSubType.Grimoire: return GetFirstGrimoire(grade); default: - throw new ArgumentOutOfRangeException(nameof(type), type, null); + throw new ArgumentOutOfRangeException(nameof(type), type, "Invalid ItemSubType"); } } From 1c1ed4300c9fa7735a26ce8412562e51846ce143 Mon Sep 17 00:00:00 2001 From: Hyun Seungmin Date: Thu, 7 Nov 2024 12:21:41 +0900 Subject: [PATCH 021/136] handle all of Libplanet exceptions --- .Lib9c.Tests/Action/ExceptionTest.cs | 210 +++++++++++++++++++++++---- 1 file changed, 183 insertions(+), 27 deletions(-) diff --git a/.Lib9c.Tests/Action/ExceptionTest.cs b/.Lib9c.Tests/Action/ExceptionTest.cs index 2b5df76bf3..13537e2a22 100644 --- a/.Lib9c.Tests/Action/ExceptionTest.cs +++ b/.Lib9c.Tests/Action/ExceptionTest.cs @@ -2,13 +2,23 @@ namespace Lib9c.Tests.Action { using System; using System.Collections.Generic; + using System.Collections.Immutable; using System.IO; using System.Linq; using System.Runtime.Serialization.Formatters.Binary; + using System.Security.Cryptography; + using Bencodex.Types; using Lib9c.Formatters; + using Libplanet.Action; + using Libplanet.Common; using Libplanet.Crypto; + using Libplanet.Types.Assets; + using Libplanet.Types.Blocks; + using Libplanet.Types.Evidence; + using Libplanet.Types.Tx; using MessagePack; using MessagePack.Resolvers; + using Nekoyume; using Nekoyume.Action; using Nekoyume.Model.State; using Xunit; @@ -25,57 +35,203 @@ public ExceptionTest() MessagePackSerializer.DefaultOptions = options; } - public static IEnumerable GetExceptions() + public static IEnumerable GetLibplanetExceptions() { var t = typeof(Exception); var exceptions = AppDomain.CurrentDomain.GetAssemblies() .SelectMany(e => e.GetTypes()) .Where(e => e.Namespace is not null && - e.Namespace.Contains("Nekoyume") && + e.Namespace.StartsWith("Libplanet") && !e.IsAbstract && e.IsClass && e.IsAssignableTo(t)) .ToArray(); foreach (var e in exceptions) { + if (e == typeof(InvalidBlockProtocolVersionException) || + e == typeof(InvalidBlockStateRootHashException) || + e == typeof(DuplicateVoteException)) + { + // FIXME: + // MessagePack.MessagePackSerializationException: Failed to serialize System.Exception value. + continue; + } + yield return new object[] { e, }; } } - [Theory] - [MemberData(nameof(GetExceptions))] - public void Exception_Serializable(Type excType) + public static IEnumerable GetLib9cExceptions() { - if (Activator.CreateInstance(excType, "for testing") is Exception exc) - { - AssertException(excType, exc); - } - else + var t = typeof(Exception); + var exceptions = AppDomain.CurrentDomain.GetAssemblies() + .SelectMany(e => e.GetTypes()) + .Where(e => + e.Namespace is not null && + e.Namespace.StartsWith("Nekoyume") && + !e.IsAbstract && + e.IsClass && + e.IsAssignableTo(t)) + .ToArray(); + foreach (var e in exceptions) { - throw new InvalidCastException(); + yield return new object[] { e, }; } } - /// - /// Libplanet Exception을 수정하기 위한 임시 테스트 코드입니다 - /// TODO: Libplanet Exception을 수정하고 테스트 코드 케이스를 추가해야 합니다 - /// - /// 예외타입. [Theory] - [InlineData(typeof(Libplanet.Action.State.InsufficientBalanceException))] - public void Libplanet_Exception_Serializable(Type excType) + [MemberData(nameof(GetLibplanetExceptions))] + [MemberData(nameof(GetLib9cExceptions))] + public void Exception_Serializable(Type excType) { - // TODO: 테스트 받는 방식 수정 - var customAddress = new Address("399bddF9F7B6d902ea27037B907B2486C9910730"); - var customFav = new Libplanet.Types.Assets.FungibleAssetValue(Currencies.Crystal); - if (Activator.CreateInstance(excType, "for testing", customAddress, customFav) is Exception exc) + var constructorTuples = excType.GetConstructors() + .Select(e => (constructorInfo: e, parameters: e.GetParameters())) + .OrderBy(tuple => tuple.parameters.Length); + foreach (var (constructorInfo, parameters) in constructorTuples) { - AssertException(excType, exc); + var parametersLength = parameters.Length; + if (parametersLength == 0) + { + AssertException((Exception)constructorInfo.Invoke(Array.Empty())); + return; + } + + var found = true; + var parameterValues = new List(); + for (var i = 0; i < parametersLength; i++) + { + if (TryGetDefaultValue(parameters[i].ParameterType, out var value)) + { + parameterValues.Add(value); + } + else + { + found = false; + break; + } + } + + if (!found) + { + continue; + } + + AssertException((Exception)constructorInfo.Invoke(parameterValues.ToArray())); + return; } - else + + throw new InvalidOperationException($"No suitable constructor found for {excType.FullName}."); + + bool TryGetDefaultValue(Type type, out object value) { - throw new InvalidCastException(); + if (Nullable.GetUnderlyingType(type) != null) + { + value = null; + return true; + } + + if (type.IsClass) + { + value = null; + return true; + } + + if (type == typeof(bool)) + { + value = default(bool); + return true; + } + + if (type == typeof(int)) + { + value = default(int); + return true; + } + + if (type == typeof(long)) + { + value = default(long); + return true; + } + + if (type == typeof(string)) + { + value = "for testing"; + return true; + } + + if (type.IsAssignableTo(typeof(Exception))) + { + value = null; + return true; + } + + if (type == typeof(HashDigest)) + { + value = HashDigest.FromString("baa2081d3b485ef2906c95a3965531ec750a74cfaefe91d0c3061865608b426c"); + return true; + } + + if (type == typeof(ImmutableArray)) + { + value = ImmutableArray.Empty; + return true; + } + + if (type == typeof(IImmutableSet)) + { + value = ImmutableHashSet.Empty; + return true; + } + + if (type == typeof(IAction)) + { + value = new DailyReward + { + avatarAddress = Addresses.Agent, + }; + return true; + } + + if (type == typeof(IValue)) + { + value = Bencodex.Types.Null.Value; + return true; + } + + if (type == typeof(Address)) + { + value = Nekoyume.Addresses.Admin; + return true; + } + + if (type == typeof(Currency)) + { + value = Currencies.Crystal; + return true; + } + + if (type == typeof(FungibleAssetValue)) + { + value = FungibleAssetValue.Parse(Currencies.Crystal, "1"); + return true; + } + + if (type == typeof(BlockHash)) + { + value = BlockHash.FromString("4582250d0da33b06779a8475d283d5dd210c683b9b999d74d03fac4f58fa6bce"); + return true; + } + + if (type == typeof(TxId)) + { + value = TxId.FromHex("300826da62b595d8cd663dadf04995a7411534d1cdc17dac75ce88754472f774"); + return true; + } + + value = null; + return false; } } @@ -114,10 +270,10 @@ public void AdminPermissionExceptionSerializable() private static void AssertException(Exception exc) where T : Exception { - AssertException(typeof(T), exc); + AssertException(exc); } - private static void AssertException(Type type, Exception exc) + private static void AssertException(Exception exc) { var b = MessagePackSerializer.Serialize(exc); var des = MessagePackSerializer.Deserialize(b); From cb33053f0ef8fd9a567dee476201d9f0c3d05e08 Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Thu, 7 Nov 2024 18:40:54 +0900 Subject: [PATCH 022/136] reduce duplicate call get random --- Lib9c/Action/Synthesize.cs | 43 +++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/Lib9c/Action/Synthesize.cs b/Lib9c/Action/Synthesize.cs index 144f2a2dd2..06b96fd90c 100644 --- a/Lib9c/Action/Synthesize.cs +++ b/Lib9c/Action/Synthesize.cs @@ -18,7 +18,7 @@ namespace Nekoyume.Action { using Sheets = Dictionary; - + /// /// Synthesize action is a type of action that synthesizes items. /// TODO: Implement Synthesize action. @@ -88,12 +88,12 @@ public override IWorld Execute(IActionContext context) typeof(CostumeItemSheet), typeof(EquipmentItemSheet), }); - + // TODO - TransferAsset (NCG) // Select id to equipment var sourceGrade = Grade.Normal; // TODO: change to set - + var materialEquipments = new List(); var materialCostumes = new List(); foreach (var materialId in MaterialIds) @@ -143,33 +143,33 @@ public override IWorld Execute(IActionContext context) // clone random item // TODO: Add items to inventory - avatarState.inventory.AddNonFungibleItem(GetSynthesizedItem(sourceGrade, sheets, context, _cachedItemSubType.Value)); + var random = context.GetRandom(); + avatarState.inventory.AddNonFungibleItem(GetSynthesizedItem(sourceGrade, sheets, random, _cachedItemSubType.Value)); return states.SetAvatarState(AvatarAddress, avatarState, true, true, false, false); } // TODO: Use Sheet, grade의 set화 - private ItemBase GetSynthesizedItem(Grade grade, Sheets sheets, IActionContext context, ItemSubType itemSubTypeValue) + private ItemBase GetSynthesizedItem(Grade grade, Sheets sheets, IRandom random, ItemSubType itemSubTypeValue) { switch (itemSubTypeValue) { case ItemSubType.FullCostume: - return GetRandomCostume(grade, itemSubTypeValue, sheets, context); + return GetRandomCostume(grade, itemSubTypeValue, sheets, random); case ItemSubType.Title: - return GetRandomCostume(grade, itemSubTypeValue, sheets, context); + return GetRandomCostume(grade, itemSubTypeValue, sheets, random); case ItemSubType.Aura: - return GetRandomEquipment(grade, itemSubTypeValue, sheets, context); + return GetRandomEquipment(grade, itemSubTypeValue, sheets, random); case ItemSubType.Grimoire: - return GetRandomEquipment(grade, itemSubTypeValue, sheets, context); + return GetRandomEquipment(grade, itemSubTypeValue, sheets, random); default: throw new ArgumentOutOfRangeException(); } } - private ItemBase GetRandomCostume(Grade grade, ItemSubType itemSubType, Sheets sheets, IActionContext context) + private ItemBase GetRandomCostume(Grade grade, ItemSubType itemSubType, Sheets sheets, IRandom random) { var sheet = sheets.GetSheet(); - var random = context.GetRandom(); var synthesizeResultPool = GetSynthesizeResultPool( new List() { grade, }, itemSubType, @@ -185,13 +185,12 @@ private ItemBase GetRandomCostume(Grade grade, ItemSubType itemSubType, Sheets s ); } - return ItemFactory.CreateItem(costumeRow, context.GetRandom()); + return ItemFactory.CreateItem(costumeRow, random); } - private ItemBase GetRandomEquipment(Grade grade, ItemSubType itemSubType, Sheets sheets, IActionContext context) + private ItemBase GetRandomEquipment(Grade grade, ItemSubType itemSubType, Sheets sheets, IRandom random) { var sheet = sheets.GetSheet(); - var random = context.GetRandom(); var synthesizeResultPool = GetSynthesizeResultPool( new List() { grade, }, itemSubType, @@ -207,7 +206,7 @@ private ItemBase GetRandomEquipment(Grade grade, ItemSubType itemSubType, Sheets ); } - return ItemFactory.CreateItem(costumeRow, context.GetRandom()); + return ItemFactory.CreateItem(costumeRow, random); } private Equipment? GetEquipmentFromId(Guid materialId, AvatarState avatarState, IActionContext context, string addressesHex) @@ -299,7 +298,7 @@ protected override void LoadPlainValueInternal(IImmutableDictionary /// Returns a list of items that may come out as a result of that synthesis. /// @@ -315,7 +314,7 @@ public static List GetSynthesizeResultPool(List sourceGrades, ItemSu .Select(r => r.Id) .ToList(); } - + /// /// Returns a list of items that may come out as a result of that synthesis. /// @@ -331,7 +330,7 @@ public static List GetSynthesizeResultPool(List sourceGrades, ItemSu .Select(r => r.Id) .ToList(); } - + public static Grade GetUpgradeGrade(Grade grade ,ItemSubType subType, CostumeItemSheet sheet) { var targetGrade = GetTargetGrade(grade); @@ -340,7 +339,7 @@ public static Grade GetUpgradeGrade(Grade grade ,ItemSubType subType, CostumeIte return hasGrade ? targetGrade : grade; } - + public static Grade GetUpgradeGrade(Grade grade ,ItemSubType subType, EquipmentItemSheet sheet) { var targetGrade = GetTargetGrade(grade); @@ -349,7 +348,7 @@ public static Grade GetUpgradeGrade(Grade grade ,ItemSubType subType, EquipmentI return hasGrade ? targetGrade : grade; } - + private static Grade GetTargetGrade(Grade grade) => grade switch { Grade.Normal => Grade.Rare, @@ -368,7 +367,7 @@ public static Grade GetUpgradeGrade(Grade grade ,ItemSubType subType, EquipmentI ItemUsable itemUsable => new List { itemUsable.ItemId, }, _ => throw new ArgumentException($"Unexpected item type: {itemBase.GetType()}", nameof(itemBase)), }; - + public static List GetItemGuids(IEnumerable itemBases) => itemBases.Select( i => { @@ -379,7 +378,7 @@ public static List GetItemGuids(IEnumerable itemBases) => itemBa _ => throw new ArgumentException($"Unexpected item type: {i.GetType()}", nameof(i)), }; }).ToList(); - + #endregion Helper } } From de2aa1f7b5887f4d47a01ca3ad4c9b9b51c27a4e Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Wed, 13 Nov 2024 16:18:19 +0900 Subject: [PATCH 023/136] add SynthesizeSheet --- Lib9c/TableCSV/Item/SynthesizeSheet.csv | 8 +++ Lib9c/TableCSV/Item/SynthesizeSheet.csv.meta | 7 +++ Lib9c/TableCSV/Item/SynthesizeWeightSheet.csv | 7 +++ .../Item/SynthesizeWeightSheet.csv.meta | 7 +++ Lib9c/TableData/Item/SynthesizeSheet.cs | 31 +++++++++++ Lib9c/TableData/Item/SynthesizeWeightSheet.cs | 53 +++++++++++++++++++ Lib9c/TableData/TableExtensions.cs | 12 +++++ 7 files changed, 125 insertions(+) create mode 100644 Lib9c/TableCSV/Item/SynthesizeSheet.csv create mode 100644 Lib9c/TableCSV/Item/SynthesizeSheet.csv.meta create mode 100644 Lib9c/TableCSV/Item/SynthesizeWeightSheet.csv create mode 100644 Lib9c/TableCSV/Item/SynthesizeWeightSheet.csv.meta create mode 100644 Lib9c/TableData/Item/SynthesizeSheet.cs create mode 100644 Lib9c/TableData/Item/SynthesizeWeightSheet.cs diff --git a/Lib9c/TableCSV/Item/SynthesizeSheet.csv b/Lib9c/TableCSV/Item/SynthesizeSheet.csv new file mode 100644 index 0000000000..306f3aa642 --- /dev/null +++ b/Lib9c/TableCSV/Item/SynthesizeSheet.csv @@ -0,0 +1,8 @@ +grade_id,required_count,succeed_rate +_value of successful_rate is between 0 and 1. +1,25,0.5 +2,20,0.4 +3,15,0.3 +4,10,0.2 +5,5,0.2 +6,3,0.0 \ No newline at end of file diff --git a/Lib9c/TableCSV/Item/SynthesizeSheet.csv.meta b/Lib9c/TableCSV/Item/SynthesizeSheet.csv.meta new file mode 100644 index 0000000000..3a96139362 --- /dev/null +++ b/Lib9c/TableCSV/Item/SynthesizeSheet.csv.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 92bd59b6eadf47443ae612f44eb2a8d9 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Lib9c/TableCSV/Item/SynthesizeWeightSheet.csv b/Lib9c/TableCSV/Item/SynthesizeWeightSheet.csv new file mode 100644 index 0000000000..5cb8f77329 --- /dev/null +++ b/Lib9c/TableCSV/Item/SynthesizeWeightSheet.csv @@ -0,0 +1,7 @@ +grade_id,item_id,weight +_default value for weight is 1 +3,40100001,0.3 +3,40100015,0.1 +3,40100016,0.1 +3,40100019,0.1 +5,49900027,4 \ No newline at end of file diff --git a/Lib9c/TableCSV/Item/SynthesizeWeightSheet.csv.meta b/Lib9c/TableCSV/Item/SynthesizeWeightSheet.csv.meta new file mode 100644 index 0000000000..244190e596 --- /dev/null +++ b/Lib9c/TableCSV/Item/SynthesizeWeightSheet.csv.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 9ce4b6514480fcc47b5c05de6cbe7657 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Lib9c/TableData/Item/SynthesizeSheet.cs b/Lib9c/TableData/Item/SynthesizeSheet.cs new file mode 100644 index 0000000000..9be097ba65 --- /dev/null +++ b/Lib9c/TableData/Item/SynthesizeSheet.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using static Nekoyume.TableData.TableExtensions; + +namespace Nekoyume.TableData +{ + [Serializable] + public class SynthesizeSheet : Sheet + { + [Serializable] + public class Row : SheetRow + { + public override int Key => GradeId; + + public int GradeId { get; private set; } + public int RequiredCount { get; private set; } + public float SucceedRate { get; private set; } + + public override void Set(IReadOnlyList fields) + { + GradeId = ParseInt(fields[0]); + RequiredCount = ParseInt(fields[1], 25); + SucceedRate = ParseFloat(fields[2], 0.0f); + } + } + + public SynthesizeSheet() : base(nameof(SynthesizeSheet)) + { + } + } +} diff --git a/Lib9c/TableData/Item/SynthesizeWeightSheet.cs b/Lib9c/TableData/Item/SynthesizeWeightSheet.cs new file mode 100644 index 0000000000..2fd45385b8 --- /dev/null +++ b/Lib9c/TableData/Item/SynthesizeWeightSheet.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using static Nekoyume.TableData.TableExtensions; + +namespace Nekoyume.TableData +{ + using System.Linq; + + [Serializable] + public class SynthesizeWeightSheet : Sheet + { + [Serializable] + public class Row : SheetRow + { + public override int Key => GradeId; + + public int GradeId { get; private set; } + + public Dictionary WeightDict { get; private set; } + + public override void Set(IReadOnlyList fields) + { + GradeId = ParseInt(fields[0]); + + WeightDict = new Dictionary(); + var itemId = ParseInt(fields[1]); + var weight = ParseFloat(fields[2], 1.0f); + WeightDict.Add(itemId, weight); + } + } + + public SynthesizeWeightSheet() : base(nameof(SynthesizeWeightSheet)) + { + } + + protected override void AddRow(int key, Row value) + { + if (!TryGetValue(key, out var row)) + { + Add(key, value); + + return; + } + + if (!value.WeightDict.Any()) + { + return; + } + + row.WeightDict.TryAdd(value.WeightDict.First().Key, value.WeightDict.First().Value); + } + } +} diff --git a/Lib9c/TableData/TableExtensions.cs b/Lib9c/TableData/TableExtensions.cs index 33e2bed784..eaf3d6377d 100644 --- a/Lib9c/TableData/TableExtensions.cs +++ b/Lib9c/TableData/TableExtensions.cs @@ -45,6 +45,18 @@ public static decimal ParseDecimal(string value) public static decimal ParseDecimal(string value, decimal defaultValue) => TryParseDecimal(value, out var result) ? result : defaultValue; + public static float ParseFloat(string value) + { + if (TryParseFloat(value, out var result)) + { + return result; + } + throw new ArgumentException(value); + } + + public static float ParseFloat(string value, float defaultValue) => + TryParseFloat(value, out var result) ? result : defaultValue; + public static long ParseLong(string value) { if (TryParseLong(value, out var result)) From 8e385375d9edcefeb84c3a947b64bf9e047ee90f Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Thu, 14 Nov 2024 18:21:09 +0900 Subject: [PATCH 024/136] add comment to synthesizeSheet --- Lib9c/TableCSV/Item/SynthesizeSheet.csv | 2 +- Lib9c/TableCSV/Item/SynthesizeWeightSheet.csv | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib9c/TableCSV/Item/SynthesizeSheet.csv b/Lib9c/TableCSV/Item/SynthesizeSheet.csv index 306f3aa642..5ba7c54219 100644 --- a/Lib9c/TableCSV/Item/SynthesizeSheet.csv +++ b/Lib9c/TableCSV/Item/SynthesizeSheet.csv @@ -1,5 +1,5 @@ grade_id,required_count,succeed_rate -_value of successful_rate is between 0 and 1. +_value of successful_rate is between 0 and 1. only the second decimal place 1,25,0.5 2,20,0.4 3,15,0.3 diff --git a/Lib9c/TableCSV/Item/SynthesizeWeightSheet.csv b/Lib9c/TableCSV/Item/SynthesizeWeightSheet.csv index 5cb8f77329..796ab41a14 100644 --- a/Lib9c/TableCSV/Item/SynthesizeWeightSheet.csv +++ b/Lib9c/TableCSV/Item/SynthesizeWeightSheet.csv @@ -1,5 +1,5 @@ grade_id,item_id,weight -_default value for weight is 1 +_default value for weight is 1. only the second decimal place 3,40100001,0.3 3,40100015,0.1 3,40100016,0.1 From cde21efa6ec566a171c774bddb186b0fc75d4c37 Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Thu, 14 Nov 2024 18:21:34 +0900 Subject: [PATCH 025/136] apply sheet to synthesize action --- Lib9c/Action/Synthesize.cs | 263 ++++++++++++++++++++++++++++++------- 1 file changed, 216 insertions(+), 47 deletions(-) diff --git a/Lib9c/Action/Synthesize.cs b/Lib9c/Action/Synthesize.cs index 06b96fd90c..2f94486fd1 100644 --- a/Lib9c/Action/Synthesize.cs +++ b/Lib9c/Action/Synthesize.cs @@ -18,10 +18,10 @@ namespace Nekoyume.Action { using Sheets = Dictionary; + using GradeDict = Dictionary>; /// /// Synthesize action is a type of action that synthesizes items. - /// TODO: Implement Synthesize action. /// [Serializable] [ActionType(TypeIdentifier)] @@ -53,7 +53,6 @@ public class Synthesize : GameAction private ItemSubType? _cachedItemSubType; #endregion Fields - // TODO: 세부 기획 정해지면 그에 맞게 테스트 코드 우선 작성 /// /// Execute Synthesize action, Input and . /// Depending on the probability, you can get different items. @@ -87,15 +86,16 @@ public override IWorld Execute(IActionContext context) { typeof(CostumeItemSheet), typeof(EquipmentItemSheet), + typeof(SynthesizeSheet), + typeof(SynthesizeWeightSheet), }); - // TODO - TransferAsset (NCG) - - // Select id to equipment - var sourceGrade = Grade.Normal; // TODO: change to set - + // Initialize variables var materialEquipments = new List(); var materialCostumes = new List(); + var gradeDict = new GradeDict(); + + // Process materials foreach (var materialId in MaterialIds) { var materialEquipment = GetEquipmentFromId(materialId, avatarState, context, addressesHex); @@ -110,13 +110,13 @@ public override IWorld Execute(IActionContext context) if (materialEquipment != null) { materialEquipments.Add(materialEquipment); - sourceGrade = (Grade)materialEquipment.Grade; + SetGradeDict(ref gradeDict, materialEquipment.Grade, materialEquipment.ItemSubType); } if (materialCostume != null) { materialCostumes.Add(materialCostume); - sourceGrade = (Grade)materialCostume.Grade; + SetGradeDict(ref gradeDict, materialCostume.Grade, materialCostume.ItemSubType); } } @@ -125,41 +125,96 @@ public override IWorld Execute(IActionContext context) throw new InvalidOperationException("ItemSubType is not set."); } - // Unequip items + var synthesizeSheet = sheets.GetSheet(); + var random = context.GetRandom(); + var synthesizedItems = new List(); + + // Calculate the number of items to be synthesized based on materials + foreach (var gradeItem in gradeDict) + { + var gradeId = gradeItem.Key; + var subTypeDict = gradeItem.Value; + + foreach (var subTypeItem in subTypeDict) + { + var itemSubType = subTypeItem.Key; + var materialCount = subTypeItem.Value; + + if (!synthesizeSheet.TryGetValue(gradeId, out var synthesizeRow)) + { + throw new SheetRowNotFoundException( + $"Aborted as the synthesize row for grade ({gradeId}) was failed to load in {nameof(SynthesizeSheet)}", gradeId + ); + } + + // TODO: subType별로 필요한 아이템 개수가 다를 수 있음 + var requiredCount = synthesizeRow.RequiredCount; + var succeedRate = Math.Clamp(synthesizeRow.SucceedRate, 0, 1); + var succeedRatePercentage = (int)(succeedRate * 100); + + var synthesizeCount = materialCount / requiredCount; + var remainder = materialCount % requiredCount; + + if (synthesizeCount <= 0 || remainder > 0) + { + throw new NotEnoughMaterialException( + $"{addressesHex} Aborted as the number of materials for grade {gradeId} and subtype {itemSubType} is not enough." + ); + } + + // Calculate success for each synthesis + for (var i = 0; i < synthesizeCount; i++) + { + var isSuccess = random.Next(100) < succeedRatePercentage; + + var grade = (Grade)gradeId; + var outputGradeId = isSuccess ? GetTargetGrade(grade) : grade; + + // Decide the item to add to inventory based on SynthesizeWeightSheet + var synthesizedItem = GetSynthesizedItem(outputGradeId, sheets, random, itemSubType); + synthesizedItems.Add(synthesizedItem); + } + } + } + + // Unequip items (if necessary) foreach (var materialEquipment in materialEquipments) { materialEquipment.Unequip(); } - foreach (var materialEquipment in materialCostumes) + foreach (var materialCostume in materialCostumes) { - materialEquipment.Unequip(); + materialCostume.Unequip(); } - // Remove materials + // Remove materials from inventory foreach (var materialId in MaterialIds) { - avatarState.inventory.RemoveNonFungibleItem(materialId); + if (!avatarState.inventory.RemoveNonFungibleItem(materialId)) + { + throw new NotEnoughMaterialException( + $"{addressesHex} Aborted as the material item ({materialId}) does not exist in inventory." + ); + } } - // clone random item - // TODO: Add items to inventory - var random = context.GetRandom(); - avatarState.inventory.AddNonFungibleItem(GetSynthesizedItem(sourceGrade, sheets, random, _cachedItemSubType.Value)); + // Add synthesized items to inventory + foreach (var item in synthesizedItems) + { + avatarState.inventory.AddNonFungibleItem(item); + } return states.SetAvatarState(AvatarAddress, avatarState, true, true, false, false); } - // TODO: Use Sheet, grade의 set화 private ItemBase GetSynthesizedItem(Grade grade, Sheets sheets, IRandom random, ItemSubType itemSubTypeValue) { switch (itemSubTypeValue) { case ItemSubType.FullCostume: - return GetRandomCostume(grade, itemSubTypeValue, sheets, random); case ItemSubType.Title: return GetRandomCostume(grade, itemSubTypeValue, sheets, random); case ItemSubType.Aura: - return GetRandomEquipment(grade, itemSubTypeValue, sheets, random); case ItemSubType.Grimoire: return GetRandomEquipment(grade, itemSubTypeValue, sheets, random); default: @@ -167,48 +222,95 @@ private ItemBase GetSynthesizedItem(Grade grade, Sheets sheets, IRandom random, } } +#region GetRandomItem + private ItemBase GetRandomCostume(Grade grade, ItemSubType itemSubType, Sheets sheets, IRandom random) { var sheet = sheets.GetSheet(); - var synthesizeResultPool = GetSynthesizeResultPool( - new List() { grade, }, - itemSubType, - sheet - ); - - // TODO: add weight - var randomId = synthesizeResultPool[random.Next(synthesizeResultPool.Count)]; - if (!sheet.TryGetValue(randomId, out var costumeRow)) + var synthesizeWeightSheet = sheets.GetSheet(); + var synthesizeResultPool = GetSynthesizeResultPool(grade, itemSubType, sheet); + + if (synthesizeResultPool.Count == 0) { - throw new SheetRowNotFoundException( - $"Aborted as the costume row ({randomId}) was failed to load in {nameof(CostumeItemSheet)}", randomId - ); + throw new InvalidOperationException($"No available items to synthesize for grade {grade} and subtype {itemSubType}"); } - return ItemFactory.CreateItem(costumeRow, random); + var randomValue = GetRandomValueForItem(grade, synthesizeResultPool, synthesizeWeightSheet, random, out var itemWeights); + float cumulativeWeight = 0; + foreach (var (itemId, weight) in itemWeights) + { + cumulativeWeight += weight; + if (randomValue >= cumulativeWeight) + { + continue; + } + + if (!sheet.TryGetValue(itemId, out var equipmentRow)) + { + throw new SheetRowNotFoundException( + $"Aborted as the equipment row ({itemId}) was failed to load in {nameof(EquipmentItemSheet)}", itemId + ); + } + return ItemFactory.CreateItem(equipmentRow, random); + } + + // Should not reach here + throw new InvalidOperationException("Failed to select a synthesized item."); } private ItemBase GetRandomEquipment(Grade grade, ItemSubType itemSubType, Sheets sheets, IRandom random) { var sheet = sheets.GetSheet(); - var synthesizeResultPool = GetSynthesizeResultPool( - new List() { grade, }, - itemSubType, - sheet - ); - - // TODO: add weight - var randomId = synthesizeResultPool[random.Next(synthesizeResultPool.Count)]; - if (!sheet.TryGetValue(randomId, out var costumeRow)) + var synthesizeWeightSheet = sheets.GetSheet(); + var synthesizeResultPool = GetSynthesizeResultPool(grade, itemSubType, sheet); + + if (synthesizeResultPool.Count == 0) { - throw new SheetRowNotFoundException( - $"Aborted as the equipment row ({randomId}) was failed to load in {nameof(EquipmentItemSheet)}", randomId - ); + throw new InvalidOperationException($"No available items to synthesize for grade {grade} and subtype {itemSubType}"); + } + + var randomValue = GetRandomValueForItem(grade, synthesizeResultPool, synthesizeWeightSheet, random, out var itemWeights); + float cumulativeWeight = 0; + foreach (var (itemId, weight) in itemWeights) + { + cumulativeWeight += weight; + if (randomValue >= cumulativeWeight) + { + continue; + } + + if (!sheet.TryGetValue(itemId, out var equipmentRow)) + { + throw new SheetRowNotFoundException( + $"Aborted as the equipment row ({itemId}) was failed to load in {nameof(EquipmentItemSheet)}", itemId + ); + } + return ItemFactory.CreateItem(equipmentRow, random); } - return ItemFactory.CreateItem(costumeRow, random); + // Should not reach here + throw new InvalidOperationException("Failed to select a synthesized item."); } + private float GetRandomValueForItem(Grade grade, HashSet synthesizeResultPool, SynthesizeWeightSheet synthesizeWeightSheet, + IRandom random, out List<(int ItemId, float Weight)> itemWeights) + { + float totalWeight = 0; + itemWeights = new List<(int ItemId, float Weight)>(); + foreach (var itemId in synthesizeResultPool) + { + var weight = GetWeight(grade, itemId, synthesizeWeightSheet); + itemWeights.Add((itemId, weight)); + totalWeight += weight; + } + + // Random selection based on weight + var randomValuePercentage = random.Next((int)(totalWeight * 100)); + return randomValuePercentage * 0.01f; + } + +#endregion GetRandomItem + private Equipment? GetEquipmentFromId(Guid materialId, AvatarState avatarState, IActionContext context, string addressesHex) { if (!avatarState.inventory.TryGetNonFungibleItem(materialId, out Equipment materialEquipment)) @@ -283,6 +385,29 @@ private ItemBase GetRandomEquipment(Grade grade, ItemSubType itemSubType, Sheets return costumeItem; } + private void SetGradeDict(ref GradeDict gradeDict, int grade, ItemSubType itemSubType) + { + if (gradeDict.ContainsKey(grade)) + { + if (gradeDict[grade].ContainsKey(itemSubType)) + { + gradeDict[grade][itemSubType]++; + } + else + { + gradeDict[grade][itemSubType] = 1; + } + } + else + { + gradeDict[grade] = new Dictionary + { + { itemSubType, 1 }, + }; + } + } + +#region Serialize protected override IImmutableDictionary PlainValueInternal => new Dictionary { @@ -296,9 +421,28 @@ protected override void LoadPlainValueInternal(IImmutableDictionary GetSynthesizeResultPool(Grade sourceGrade, ItemSubType subType, CostumeItemSheet sheet) + { + return sheet.Values + .Where(r => r.ItemSubType == subType) + .Where(r => (Grade)r.Grade == sourceGrade) + .Select(r => r.Id) + .ToHashSet(); + } + + public static HashSet GetSynthesizeResultPool(Grade sourceGrade, ItemSubType subType, EquipmentItemSheet sheet) + { + return sheet.Values + .Where(r => r.ItemSubType == subType) + .Where(r => (Grade)r.Grade == sourceGrade) + .Select(r => r.Id) + .ToHashSet(); + } + /// /// Returns a list of items that may come out as a result of that synthesis. /// @@ -360,6 +504,20 @@ public static Grade GetUpgradeGrade(Grade grade ,ItemSubType subType, EquipmentI _ => throw new ArgumentOutOfRangeException(nameof(grade), grade, null), }; + public static int GetTargetGrade(int gradeId) + { + return gradeId switch + { + 1 => 2, // Grade.Normal => Grade.Rare + 2 => 3, // Grade.Rare => Grade.Epic + 3 => 4, // Grade.Epic => Grade.Unique + 4 => 5, // Grade.Unique => Grade.Legendary + 5 => 6, // Grade.Legendary => Grade.Divinity + 6 => 6, // Grade.Divinity => Grade.Divinity (Max) + _ => throw new ArgumentOutOfRangeException(nameof(gradeId), gradeId, null), + }; + } + // TODO: move to ItemExtensions public static List GetItemGuid(ItemBase itemBase) => itemBase switch { @@ -379,6 +537,17 @@ public static List GetItemGuids(IEnumerable itemBases) => itemBa }; }).ToList(); + public static float GetWeight(Grade grade, int itemId, SynthesizeWeightSheet sheet) + { + var gradeRow = sheet.Values.FirstOrDefault(r => r.GradeId == (int)grade); + if (gradeRow == null) + { + return 1; + } + + return gradeRow.WeightDict.TryGetValue(itemId, out var weight) ? weight : 1; + } + #endregion Helper } } From 1f3a41dc6c58cac5e0d7b2c6871cbed9c755f13e Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Thu, 14 Nov 2024 18:22:58 +0900 Subject: [PATCH 026/136] add sheet object in tableSheets object --- .Lib9c.Tests/TableSheets.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.Lib9c.Tests/TableSheets.cs b/.Lib9c.Tests/TableSheets.cs index 5f15693b32..bc24037738 100644 --- a/.Lib9c.Tests/TableSheets.cs +++ b/.Lib9c.Tests/TableSheets.cs @@ -281,6 +281,10 @@ public TableSheets(Dictionary sheets, bool ignoreFailedGetProper public ClaimableGiftsSheet ClaimableGiftsSheet { get; private set; } + public SynthesizeSheet SynthesizeSheet { get; private set; } + + public SynthesizeWeightSheet SynthesizeWeightSheet { get; private set; } + public void ItemSheetInitialize() { ItemSheet ??= new ItemSheet(); From 251652e7769efea708fc6ec9edc1e8f9d1c0e362 Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Thu, 14 Nov 2024 18:26:13 +0900 Subject: [PATCH 027/136] apply sheet to synthesize action test --- .Lib9c.Tests/Action/SynthesizeTest.cs | 100 ++++++++++++++++++++------ 1 file changed, 80 insertions(+), 20 deletions(-) diff --git a/.Lib9c.Tests/Action/SynthesizeTest.cs b/.Lib9c.Tests/Action/SynthesizeTest.cs index 16135931b7..2356841d5e 100644 --- a/.Lib9c.Tests/Action/SynthesizeTest.cs +++ b/.Lib9c.Tests/Action/SynthesizeTest.cs @@ -62,25 +62,27 @@ public IWorld Init(out Address agentAddress, out Address avatarAddress, out long } [Theory] - [InlineData((Grade)3, new[] { ItemSubType.FullCostume, ItemSubType.FullCostume, ItemSubType.FullCostume, })] - [InlineData((Grade)4, new[] { ItemSubType.FullCostume, ItemSubType.FullCostume, ItemSubType.FullCostume, })] - [InlineData((Grade)5, new[] { ItemSubType.FullCostume, ItemSubType.FullCostume, ItemSubType.FullCostume, })] - [InlineData((Grade)3, new[] { ItemSubType.Title, ItemSubType.Title, ItemSubType.Title, })] - [InlineData((Grade)4, new[] { ItemSubType.Title, ItemSubType.Title, ItemSubType.Title, })] - [InlineData((Grade)5, new[] { ItemSubType.Title, ItemSubType.Title, ItemSubType.Title, })] - [InlineData((Grade)3, new[] { ItemSubType.Grimoire, ItemSubType.Grimoire, ItemSubType.Grimoire, })] - [InlineData((Grade)4, new[] { ItemSubType.Grimoire, ItemSubType.Grimoire, ItemSubType.Grimoire, })] - [InlineData((Grade)5, new[] { ItemSubType.Grimoire, ItemSubType.Grimoire, ItemSubType.Grimoire, })] - [InlineData((Grade)6, new[] { ItemSubType.Grimoire, ItemSubType.Grimoire, ItemSubType.Grimoire, })] - [InlineData((Grade)1, new[] { ItemSubType.Aura, ItemSubType.Aura, ItemSubType.Aura, })] - [InlineData((Grade)2, new[] { ItemSubType.Aura, ItemSubType.Aura, ItemSubType.Aura, })] - [InlineData((Grade)3, new[] { ItemSubType.Aura, ItemSubType.Aura, ItemSubType.Aura, })] - [InlineData((Grade)4, new[] { ItemSubType.Aura, ItemSubType.Aura, ItemSubType.Aura, })] - [InlineData((Grade)5, new[] { ItemSubType.Aura, ItemSubType.Aura, ItemSubType.Aura, })] - [InlineData((Grade)6, new[] { ItemSubType.Aura, ItemSubType.Aura, ItemSubType.Aura, })] - public void Execute(Grade grade, ItemSubType[] itemSubTypes) + [InlineData((Grade)3, ItemSubType.FullCostume)] + [InlineData((Grade)4, ItemSubType.FullCostume)] + [InlineData((Grade)5, ItemSubType.FullCostume)] + [InlineData((Grade)3, ItemSubType.Title)] + [InlineData((Grade)4, ItemSubType.Title)] + [InlineData((Grade)5, ItemSubType.Title)] + [InlineData((Grade)3, ItemSubType.Grimoire)] + [InlineData((Grade)4, ItemSubType.Grimoire)] + [InlineData((Grade)5, ItemSubType.Grimoire)] + [InlineData((Grade)6, ItemSubType.Grimoire)] + [InlineData((Grade)1, ItemSubType.Aura)] + [InlineData((Grade)2, ItemSubType.Aura)] + [InlineData((Grade)3, ItemSubType.Aura)] + [InlineData((Grade)4, ItemSubType.Aura)] + [InlineData((Grade)5, ItemSubType.Aura)] + [InlineData((Grade)6, ItemSubType.Aura)] + public void ExecuteSingle(Grade grade, ItemSubType itemSubType) { var context = new ActionContext(); + var itemSubTypes = GetSubTypeArray(itemSubType, GetSucceededMaterialCount(grade)); + var state = Init(out var agentAddress, out var avatarAddress, out var blockIndex); (state, var items) = UpdateItemsFromSubType(grade, itemSubTypes, state, avatarAddress); @@ -101,8 +103,7 @@ public void Execute(Grade grade, ItemSubType[] itemSubTypes) state = action.Execute(ctx); var inventory = state.GetInventoryV2(avatarAddress); - // Tests - // 현재 아이템 3개를 넣어 1개가 나오는 구조라 이런 형태로 체크, 추후 변경될 수 있음 + // Tests - result inventory should have only one item in this case. Assert.Single(inventory.Items); var firstItem = inventory.Items.First().item; Assert.True(firstItem.ItemSubType == itemSubTypes[0]); @@ -139,12 +140,53 @@ public void Execute(Grade grade, ItemSubType[] itemSubTypes) Assert.True(expectedGrade == resultGrade || resultGrade == grade); } + [Theory] + [InlineData(2)] + [InlineData(3)] + [InlineData(12)] + public void ExecuteMultipleSameType(int testCount) + { + var grade = Grade.Rare; + var itemSubType = ItemSubType.FullCostume; + var materialCount = GetSucceededMaterialCount(grade) * testCount; + var itemSubTypes = GetSubTypeArray(itemSubType, materialCount); + + var state = Init(out var agentAddress, out var avatarAddress, out var blockIndex); + (state, var items) = UpdateItemsFromSubType(grade, itemSubTypes, state, avatarAddress); + + var action = new Synthesize() + { + AvatarAddress = avatarAddress, + MaterialIds = Synthesize.GetItemGuids(items), + }; + + var ctx = new ActionContext + { + BlockIndex = blockIndex, + PreviousState = state, + RandomSeed = 0, + Signer = agentAddress, + }; + + state = action.Execute(ctx); + var inventory = state.GetInventoryV2(avatarAddress); + + // Assert + Assert.Equal(testCount, inventory.Items.Count); + foreach (var item in inventory.Items.Select(i => i.item)) + { + Assert.Equal(itemSubType, item.ItemSubType); + var expectedGrade = Synthesize.GetTargetGrade((int)grade); + Assert.True(item.Grade == expectedGrade || item.Grade == (int)grade); + } + } + [Theory] [InlineData((Grade)3, new[] { ItemSubType.Aura, ItemSubType.FullCostume, ItemSubType.FullCostume })] [InlineData((Grade)3, new[] { ItemSubType.Title, ItemSubType.Grimoire, ItemSubType.Title })] [InlineData((Grade)3, new[] { ItemSubType.Grimoire, ItemSubType.Title, ItemSubType.Grimoire })] [InlineData((Grade)3, new[] { ItemSubType.Aura, ItemSubType.Aura, ItemSubType.Grimoire })] - public void ExecuteMixedSubTypes(Grade grade, ItemSubType[] itemSubTypes) + public void ExecuteInvalidMaterial(Grade grade, ItemSubType[] itemSubTypes) { var context = new ActionContext(); var state = Init(out var agentAddress, out var avatarAddress, out var blockIndex); @@ -252,4 +294,22 @@ private static ItemBase GetFirstGrimoire(Grade grade) return ItemFactory.CreateItem(row, new TestRandom(_randomSeed++)); } + + private static ItemSubType[] GetSubTypeArray(ItemSubType subtype, int count) + { + var subTypes = new ItemSubType[count]; + for (var i = 0; i < count; i++) + { + subTypes[i] = subtype; + } + + return subTypes; + } + + private static int GetSucceededMaterialCount(Grade grade) + { + var synthesizeSheet = TableSheets.SynthesizeSheet; + var row = synthesizeSheet.Values.First(r => (Grade)r.GradeId == grade); + return row.RequiredCount; + } } From fe7e0e7fc01a9ee784cd11f7d38a058c8b3f919f Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Thu, 14 Nov 2024 18:29:49 +0900 Subject: [PATCH 028/136] add todo comment --- .Lib9c.Tests/Action/SynthesizeTest.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.Lib9c.Tests/Action/SynthesizeTest.cs b/.Lib9c.Tests/Action/SynthesizeTest.cs index 2356841d5e..180a248137 100644 --- a/.Lib9c.Tests/Action/SynthesizeTest.cs +++ b/.Lib9c.Tests/Action/SynthesizeTest.cs @@ -181,6 +181,9 @@ public void ExecuteMultipleSameType(int testCount) } } + // TODO: Add Simulator for test and client + // TODO: Exception Tests + // TODO: ExecuteMultiple [Theory] [InlineData((Grade)3, new[] { ItemSubType.Aura, ItemSubType.FullCostume, ItemSubType.FullCostume })] [InlineData((Grade)3, new[] { ItemSubType.Title, ItemSubType.Grimoire, ItemSubType.Title })] From ad1f7665a8a8fd93d8dda2dd68d715234c31e792 Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Fri, 15 Nov 2024 18:57:00 +0900 Subject: [PATCH 029/136] add synthesize simulator --- .Lib9c.Tests/Action/SynthesizeTest.cs | 13 +- Lib9c/Action/Synthesize.cs | 291 +---------------------- Lib9c/Helper/SynthesizeSimulator.cs | 324 ++++++++++++++++++++++++++ 3 files changed, 338 insertions(+), 290 deletions(-) create mode 100644 Lib9c/Helper/SynthesizeSimulator.cs diff --git a/.Lib9c.Tests/Action/SynthesizeTest.cs b/.Lib9c.Tests/Action/SynthesizeTest.cs index 180a248137..979e482119 100644 --- a/.Lib9c.Tests/Action/SynthesizeTest.cs +++ b/.Lib9c.Tests/Action/SynthesizeTest.cs @@ -14,6 +14,7 @@ using Nekoyume.Model.EnumType; using Nekoyume.Module; using Nekoyume.TableData; +using Nekoyume.Helper; using Xunit; using Sheets = System.Collections.Generic.Dictionary; @@ -89,7 +90,7 @@ public void ExecuteSingle(Grade grade, ItemSubType itemSubType) var action = new Synthesize() { AvatarAddress = avatarAddress, - MaterialIds = Synthesize.GetItemGuids(items), + MaterialIds = SynthesizeSimulator.GetItemGuids(items), }; var ctx = new ActionContext @@ -121,7 +122,7 @@ public void ExecuteSingle(Grade grade, ItemSubType itemSubType) } resultGrade = (Grade)costume.Grade; - expectedGrade = Synthesize.GetUpgradeGrade(grade, subType, TableSheets.CostumeItemSheet); + expectedGrade = SynthesizeSimulator.GetUpgradeGrade(grade, subType, TableSheets.CostumeItemSheet); break; case ItemSubType.Aura: case ItemSubType.Grimoire: @@ -131,7 +132,7 @@ public void ExecuteSingle(Grade grade, ItemSubType itemSubType) } resultGrade = (Grade)itemUsable.Grade; - expectedGrade = Synthesize.GetUpgradeGrade(grade, subType, TableSheets.EquipmentItemSheet); + expectedGrade = SynthesizeSimulator.GetUpgradeGrade(grade, subType, TableSheets.EquipmentItemSheet); break; } @@ -157,7 +158,7 @@ public void ExecuteMultipleSameType(int testCount) var action = new Synthesize() { AvatarAddress = avatarAddress, - MaterialIds = Synthesize.GetItemGuids(items), + MaterialIds = SynthesizeSimulator.GetItemGuids(items), }; var ctx = new ActionContext @@ -176,7 +177,7 @@ public void ExecuteMultipleSameType(int testCount) foreach (var item in inventory.Items.Select(i => i.item)) { Assert.Equal(itemSubType, item.ItemSubType); - var expectedGrade = Synthesize.GetTargetGrade((int)grade); + var expectedGrade = SynthesizeSimulator.GetTargetGrade((int)grade); Assert.True(item.Grade == expectedGrade || item.Grade == (int)grade); } } @@ -198,7 +199,7 @@ public void ExecuteInvalidMaterial(Grade grade, ItemSubType[] itemSubTypes) var action = new Synthesize() { AvatarAddress = avatarAddress, - MaterialIds = Synthesize.GetItemGuids(items), + MaterialIds = SynthesizeSimulator.GetItemGuids(items), }; var ctx = new ActionContext diff --git a/Lib9c/Action/Synthesize.cs b/Lib9c/Action/Synthesize.cs index 2f94486fd1..94389caa9d 100644 --- a/Lib9c/Action/Synthesize.cs +++ b/Lib9c/Action/Synthesize.cs @@ -8,10 +8,9 @@ using Libplanet.Action; using Libplanet.Action.State; using Libplanet.Crypto; -using Nekoyume.Extensions; using Nekoyume.Model.Item; using Nekoyume.Model.State; -using Nekoyume.Model.EnumType; +using Nekoyume.Helper; using Nekoyume.Module; using Nekoyume.TableData; @@ -125,57 +124,12 @@ public override IWorld Execute(IActionContext context) throw new InvalidOperationException("ItemSubType is not set."); } - var synthesizeSheet = sheets.GetSheet(); - var random = context.GetRandom(); - var synthesizedItems = new List(); - - // Calculate the number of items to be synthesized based on materials - foreach (var gradeItem in gradeDict) + var synthesizedItems = SynthesizeSimulator.Simulate(new SynthesizeSimulator.InputData() { - var gradeId = gradeItem.Key; - var subTypeDict = gradeItem.Value; - - foreach (var subTypeItem in subTypeDict) - { - var itemSubType = subTypeItem.Key; - var materialCount = subTypeItem.Value; - - if (!synthesizeSheet.TryGetValue(gradeId, out var synthesizeRow)) - { - throw new SheetRowNotFoundException( - $"Aborted as the synthesize row for grade ({gradeId}) was failed to load in {nameof(SynthesizeSheet)}", gradeId - ); - } - - // TODO: subType별로 필요한 아이템 개수가 다를 수 있음 - var requiredCount = synthesizeRow.RequiredCount; - var succeedRate = Math.Clamp(synthesizeRow.SucceedRate, 0, 1); - var succeedRatePercentage = (int)(succeedRate * 100); - - var synthesizeCount = materialCount / requiredCount; - var remainder = materialCount % requiredCount; - - if (synthesizeCount <= 0 || remainder > 0) - { - throw new NotEnoughMaterialException( - $"{addressesHex} Aborted as the number of materials for grade {gradeId} and subtype {itemSubType} is not enough." - ); - } - - // Calculate success for each synthesis - for (var i = 0; i < synthesizeCount; i++) - { - var isSuccess = random.Next(100) < succeedRatePercentage; - - var grade = (Grade)gradeId; - var outputGradeId = isSuccess ? GetTargetGrade(grade) : grade; - - // Decide the item to add to inventory based on SynthesizeWeightSheet - var synthesizedItem = GetSynthesizedItem(outputGradeId, sheets, random, itemSubType); - synthesizedItems.Add(synthesizedItem); - } - } - } + Sheets = sheets, + RandomObject = context.GetRandom(), + GradeDict = gradeDict, + }); // Unequip items (if necessary) foreach (var materialEquipment in materialEquipments) @@ -201,116 +155,12 @@ public override IWorld Execute(IActionContext context) // Add synthesized items to inventory foreach (var item in synthesizedItems) { - avatarState.inventory.AddNonFungibleItem(item); + avatarState.inventory.AddNonFungibleItem(item.ItemBase); } return states.SetAvatarState(AvatarAddress, avatarState, true, true, false, false); } - private ItemBase GetSynthesizedItem(Grade grade, Sheets sheets, IRandom random, ItemSubType itemSubTypeValue) - { - switch (itemSubTypeValue) - { - case ItemSubType.FullCostume: - case ItemSubType.Title: - return GetRandomCostume(grade, itemSubTypeValue, sheets, random); - case ItemSubType.Aura: - case ItemSubType.Grimoire: - return GetRandomEquipment(grade, itemSubTypeValue, sheets, random); - default: - throw new ArgumentOutOfRangeException(); - } - } - -#region GetRandomItem - - private ItemBase GetRandomCostume(Grade grade, ItemSubType itemSubType, Sheets sheets, IRandom random) - { - var sheet = sheets.GetSheet(); - var synthesizeWeightSheet = sheets.GetSheet(); - var synthesizeResultPool = GetSynthesizeResultPool(grade, itemSubType, sheet); - - if (synthesizeResultPool.Count == 0) - { - throw new InvalidOperationException($"No available items to synthesize for grade {grade} and subtype {itemSubType}"); - } - - var randomValue = GetRandomValueForItem(grade, synthesizeResultPool, synthesizeWeightSheet, random, out var itemWeights); - float cumulativeWeight = 0; - foreach (var (itemId, weight) in itemWeights) - { - cumulativeWeight += weight; - if (randomValue >= cumulativeWeight) - { - continue; - } - - if (!sheet.TryGetValue(itemId, out var equipmentRow)) - { - throw new SheetRowNotFoundException( - $"Aborted as the equipment row ({itemId}) was failed to load in {nameof(EquipmentItemSheet)}", itemId - ); - } - return ItemFactory.CreateItem(equipmentRow, random); - } - - // Should not reach here - throw new InvalidOperationException("Failed to select a synthesized item."); - } - - private ItemBase GetRandomEquipment(Grade grade, ItemSubType itemSubType, Sheets sheets, IRandom random) - { - var sheet = sheets.GetSheet(); - var synthesizeWeightSheet = sheets.GetSheet(); - var synthesizeResultPool = GetSynthesizeResultPool(grade, itemSubType, sheet); - - if (synthesizeResultPool.Count == 0) - { - throw new InvalidOperationException($"No available items to synthesize for grade {grade} and subtype {itemSubType}"); - } - - var randomValue = GetRandomValueForItem(grade, synthesizeResultPool, synthesizeWeightSheet, random, out var itemWeights); - float cumulativeWeight = 0; - foreach (var (itemId, weight) in itemWeights) - { - cumulativeWeight += weight; - if (randomValue >= cumulativeWeight) - { - continue; - } - - if (!sheet.TryGetValue(itemId, out var equipmentRow)) - { - throw new SheetRowNotFoundException( - $"Aborted as the equipment row ({itemId}) was failed to load in {nameof(EquipmentItemSheet)}", itemId - ); - } - return ItemFactory.CreateItem(equipmentRow, random); - } - - // Should not reach here - throw new InvalidOperationException("Failed to select a synthesized item."); - } - - private float GetRandomValueForItem(Grade grade, HashSet synthesizeResultPool, SynthesizeWeightSheet synthesizeWeightSheet, - IRandom random, out List<(int ItemId, float Weight)> itemWeights) - { - float totalWeight = 0; - itemWeights = new List<(int ItemId, float Weight)>(); - foreach (var itemId in synthesizeResultPool) - { - var weight = GetWeight(grade, itemId, synthesizeWeightSheet); - itemWeights.Add((itemId, weight)); - totalWeight += weight; - } - - // Random selection based on weight - var randomValuePercentage = random.Next((int)(totalWeight * 100)); - return randomValuePercentage * 0.01f; - } - -#endregion GetRandomItem - private Equipment? GetEquipmentFromId(Guid materialId, AvatarState avatarState, IActionContext context, string addressesHex) { if (!avatarState.inventory.TryGetNonFungibleItem(materialId, out Equipment materialEquipment)) @@ -422,132 +272,5 @@ protected override void LoadPlainValueInternal(IImmutableDictionary GetSynthesizeResultPool(Grade sourceGrade, ItemSubType subType, CostumeItemSheet sheet) - { - return sheet.Values - .Where(r => r.ItemSubType == subType) - .Where(r => (Grade)r.Grade == sourceGrade) - .Select(r => r.Id) - .ToHashSet(); - } - - public static HashSet GetSynthesizeResultPool(Grade sourceGrade, ItemSubType subType, EquipmentItemSheet sheet) - { - return sheet.Values - .Where(r => r.ItemSubType == subType) - .Where(r => (Grade)r.Grade == sourceGrade) - .Select(r => r.Id) - .ToHashSet(); - } - - /// - /// Returns a list of items that may come out as a result of that synthesis. - /// - /// grades of material items - /// excepted FullCostume,Title - /// CostumeItemSheet to use - /// list of items key(int) - public static List GetSynthesizeResultPool(List sourceGrades, ItemSubType subType, CostumeItemSheet sheet) - { - return sheet.Values - .Where(r => r.ItemSubType == subType) - .Where(r => sourceGrades.Any(grade => (Grade)r.Grade == GetUpgradeGrade(grade, subType, sheet))) - .Select(r => r.Id) - .ToList(); - } - - /// - /// Returns a list of items that may come out as a result of that synthesis. - /// - /// grades of material items - /// excepted Grimoire,Aura - /// EquipmentItemSheet to use - /// list of items key(int) - public static List GetSynthesizeResultPool(List sourceGrades, ItemSubType subType, EquipmentItemSheet sheet) - { - return sheet.Values - .Where(r => r.ItemSubType == subType) - .Where(r => sourceGrades.Any(grade => (Grade)r.Grade == GetUpgradeGrade(grade, subType, sheet))) - .Select(r => r.Id) - .ToList(); - } - - public static Grade GetUpgradeGrade(Grade grade ,ItemSubType subType, CostumeItemSheet sheet) - { - var targetGrade = GetTargetGrade(grade); - var hasGrade = sheet.Values - .Any(r => r.ItemSubType == subType && r.Grade == (int)targetGrade); - - return hasGrade ? targetGrade : grade; - } - - public static Grade GetUpgradeGrade(Grade grade ,ItemSubType subType, EquipmentItemSheet sheet) - { - var targetGrade = GetTargetGrade(grade); - var hasGrade = sheet.Values - .Any(r => r.ItemSubType == subType && r.Grade == (int)targetGrade); - - return hasGrade ? targetGrade : grade; - } - - private static Grade GetTargetGrade(Grade grade) => grade switch - { - Grade.Normal => Grade.Rare, - Grade.Rare => Grade.Epic, - Grade.Epic => Grade.Unique, - Grade.Unique => Grade.Legendary, - Grade.Legendary => Grade.Divinity, - Grade.Divinity => Grade.Divinity, - _ => throw new ArgumentOutOfRangeException(nameof(grade), grade, null), - }; - - public static int GetTargetGrade(int gradeId) - { - return gradeId switch - { - 1 => 2, // Grade.Normal => Grade.Rare - 2 => 3, // Grade.Rare => Grade.Epic - 3 => 4, // Grade.Epic => Grade.Unique - 4 => 5, // Grade.Unique => Grade.Legendary - 5 => 6, // Grade.Legendary => Grade.Divinity - 6 => 6, // Grade.Divinity => Grade.Divinity (Max) - _ => throw new ArgumentOutOfRangeException(nameof(gradeId), gradeId, null), - }; - } - - // TODO: move to ItemExtensions - public static List GetItemGuid(ItemBase itemBase) => itemBase switch - { - Costume costume => new List { costume.ItemId, }, - ItemUsable itemUsable => new List { itemUsable.ItemId, }, - _ => throw new ArgumentException($"Unexpected item type: {itemBase.GetType()}", nameof(itemBase)), - }; - - public static List GetItemGuids(IEnumerable itemBases) => itemBases.Select( - i => - { - return i switch - { - Costume costume => costume.ItemId, - ItemUsable itemUsable => itemUsable.ItemId, - _ => throw new ArgumentException($"Unexpected item type: {i.GetType()}", nameof(i)), - }; - }).ToList(); - - public static float GetWeight(Grade grade, int itemId, SynthesizeWeightSheet sheet) - { - var gradeRow = sheet.Values.FirstOrDefault(r => r.GradeId == (int)grade); - if (gradeRow == null) - { - return 1; - } - - return gradeRow.WeightDict.TryGetValue(itemId, out var weight) ? weight : 1; - } - -#endregion Helper } } diff --git a/Lib9c/Helper/SynthesizeSimulator.cs b/Lib9c/Helper/SynthesizeSimulator.cs new file mode 100644 index 0000000000..754d04cad2 --- /dev/null +++ b/Lib9c/Helper/SynthesizeSimulator.cs @@ -0,0 +1,324 @@ +#nullable enable + +using System; +using System.Collections.Generic; +using System.Linq; +using Libplanet.Action; +using Libplanet.Crypto; +using Nekoyume.Action; +using Nekoyume.Extensions; +using Nekoyume.Model.Item; +using Nekoyume.Model.EnumType; +using Nekoyume.TableData; + +namespace Nekoyume.Helper +{ + using Sheets = Dictionary; + using GradeDict = Dictionary>; + + public struct SynthesizeResult + { + public ItemBase ItemBase; + // TODO: Add more fields + } + + public static class SynthesizeSimulator + { + public struct InputData + { + public Sheets Sheets; + public IRandom RandomObject; + public GradeDict GradeDict; + } + + public static List Simulate(InputData inputData) + { + var synthesizeResults = new List(); + + var sheets = inputData.Sheets; + var synthesizeSheet = sheets.GetSheet(); + var random = inputData.RandomObject; + var gradeDict = inputData.GradeDict; + + // Calculate the number of items to be synthesized based on materials + foreach (var gradeItem in gradeDict) + { + var gradeId = gradeItem.Key; + var subTypeDict = gradeItem.Value; + + foreach (var subTypeItem in subTypeDict) + { + var itemSubType = subTypeItem.Key; + var materialCount = subTypeItem.Value; + + if (!synthesizeSheet.TryGetValue(gradeId, out var synthesizeRow)) + { + throw new SheetRowNotFoundException( + $"Aborted as the synthesize row for grade ({gradeId}) was failed to load in {nameof(SynthesizeSheet)}", gradeId + ); + } + + // TODO: subType별로 필요한 아이템 개수가 다를 수 있음 + var requiredCount = synthesizeRow.RequiredCount; + var succeedRate = Math.Clamp(synthesizeRow.SucceedRate, 0, 1); + var succeedRatePercentage = (int)(succeedRate * 100); + + var synthesizeCount = materialCount / requiredCount; + var remainder = materialCount % requiredCount; + + if (synthesizeCount <= 0 || remainder > 0) + { + throw new NotEnoughMaterialException( + $"Aborted as the number of materials for grade {gradeId} and subtype {itemSubType} is not enough." + ); + } + + // Calculate success for each synthesis + for (var i = 0; i < synthesizeCount; i++) + { + var isSuccess = random.Next(100) < succeedRatePercentage; + + var grade = (Grade)gradeId; + var outputGradeId = isSuccess ? GetTargetGrade(grade) : grade; + + // Decide the item to add to inventory based on SynthesizeWeightSheet + var synthesizedItem = GetSynthesizedItem(outputGradeId, sheets, random, itemSubType); + synthesizeResults.Add(new SynthesizeResult { ItemBase = synthesizedItem, }); + } + } + } + + return synthesizeResults; + } + + private static ItemBase GetSynthesizedItem(Grade grade, Sheets sheets, IRandom random, ItemSubType itemSubTypeValue) + { + switch (itemSubTypeValue) + { + case ItemSubType.FullCostume: + case ItemSubType.Title: + return GetRandomCostume(grade, itemSubTypeValue, sheets, random); + case ItemSubType.Aura: + case ItemSubType.Grimoire: + return GetRandomEquipment(grade, itemSubTypeValue, sheets, random); + default: + throw new ArgumentOutOfRangeException(); + } + } + +#region GetRandomItem + + private static ItemBase GetRandomCostume(Grade grade, ItemSubType itemSubType, Sheets sheets, IRandom random) + { + var sheet = sheets.GetSheet(); + var synthesizeWeightSheet = sheets.GetSheet(); + var synthesizeResultPool = GetSynthesizeResultPool(grade, itemSubType, sheet); + + if (synthesizeResultPool.Count == 0) + { + throw new InvalidOperationException($"No available items to synthesize for grade {grade} and subtype {itemSubType}"); + } + + var randomValue = GetRandomValueForItem(grade, synthesizeResultPool, synthesizeWeightSheet, random, out var itemWeights); + float cumulativeWeight = 0; + foreach (var (itemId, weight) in itemWeights) + { + cumulativeWeight += weight; + if (randomValue >= cumulativeWeight) + { + continue; + } + + if (!sheet.TryGetValue(itemId, out var equipmentRow)) + { + throw new SheetRowNotFoundException( + $"Aborted as the equipment row ({itemId}) was failed to load in {nameof(EquipmentItemSheet)}", itemId + ); + } + return ItemFactory.CreateItem(equipmentRow, random); + } + + // Should not reach here + throw new InvalidOperationException("Failed to select a synthesized item."); + } + + private static ItemBase GetRandomEquipment(Grade grade, ItemSubType itemSubType, Sheets sheets, IRandom random) + { + var sheet = sheets.GetSheet(); + var synthesizeWeightSheet = sheets.GetSheet(); + var synthesizeResultPool = GetSynthesizeResultPool(grade, itemSubType, sheet); + + if (synthesizeResultPool.Count == 0) + { + throw new InvalidOperationException($"No available items to synthesize for grade {grade} and subtype {itemSubType}"); + } + + var randomValue = GetRandomValueForItem(grade, synthesizeResultPool, synthesizeWeightSheet, random, out var itemWeights); + float cumulativeWeight = 0; + foreach (var (itemId, weight) in itemWeights) + { + cumulativeWeight += weight; + if (randomValue >= cumulativeWeight) + { + continue; + } + + if (!sheet.TryGetValue(itemId, out var equipmentRow)) + { + throw new SheetRowNotFoundException( + $"Aborted as the equipment row ({itemId}) was failed to load in {nameof(EquipmentItemSheet)}", itemId + ); + } + return ItemFactory.CreateItem(equipmentRow, random); + } + + // Should not reach here + throw new InvalidOperationException("Failed to select a synthesized item."); + } + + private static float GetRandomValueForItem(Grade grade, HashSet synthesizeResultPool, SynthesizeWeightSheet synthesizeWeightSheet, + IRandom random, out List<(int ItemId, float Weight)> itemWeights) + { + float totalWeight = 0; + itemWeights = new List<(int ItemId, float Weight)>(); + foreach (var itemId in synthesizeResultPool) + { + var weight = GetWeight(grade, itemId, synthesizeWeightSheet); + itemWeights.Add((itemId, weight)); + totalWeight += weight; + } + + // Random selection based on weight + var randomValuePercentage = random.Next((int)(totalWeight * 100)); + return randomValuePercentage * 0.01f; + } + +#endregion GetRandomItem + +#region Helper + + public static HashSet GetSynthesizeResultPool(Grade sourceGrade, ItemSubType subType, CostumeItemSheet sheet) + { + return sheet.Values + .Where(r => r.ItemSubType == subType) + .Where(r => (Grade)r.Grade == sourceGrade) + .Select(r => r.Id) + .ToHashSet(); + } + + public static HashSet GetSynthesizeResultPool(Grade sourceGrade, ItemSubType subType, EquipmentItemSheet sheet) + { + return sheet.Values + .Where(r => r.ItemSubType == subType) + .Where(r => (Grade)r.Grade == sourceGrade) + .Select(r => r.Id) + .ToHashSet(); + } + + /// + /// Returns a list of items that may come out as a result of that synthesis. + /// + /// grades of material items + /// excepted FullCostume,Title + /// CostumeItemSheet to use + /// list of items key(int) + public static List GetSynthesizeResultPool(List sourceGrades, ItemSubType subType, CostumeItemSheet sheet) + { + return sheet.Values + .Where(r => r.ItemSubType == subType) + .Where(r => sourceGrades.Any(grade => (Grade)r.Grade == GetUpgradeGrade(grade, subType, sheet))) + .Select(r => r.Id) + .ToList(); + } + + /// + /// Returns a list of items that may come out as a result of that synthesis. + /// + /// grades of material items + /// excepted Grimoire,Aura + /// EquipmentItemSheet to use + /// list of items key(int) + public static List GetSynthesizeResultPool(List sourceGrades, ItemSubType subType, EquipmentItemSheet sheet) + { + return sheet.Values + .Where(r => r.ItemSubType == subType) + .Where(r => sourceGrades.Any(grade => (Grade)r.Grade == GetUpgradeGrade(grade, subType, sheet))) + .Select(r => r.Id) + .ToList(); + } + + public static Grade GetUpgradeGrade(Grade grade ,ItemSubType subType, CostumeItemSheet sheet) + { + var targetGrade = GetTargetGrade(grade); + var hasGrade = sheet.Values + .Any(r => r.ItemSubType == subType && r.Grade == (int)targetGrade); + + return hasGrade ? targetGrade : grade; + } + + public static Grade GetUpgradeGrade(Grade grade ,ItemSubType subType, EquipmentItemSheet sheet) + { + var targetGrade = GetTargetGrade(grade); + var hasGrade = sheet.Values + .Any(r => r.ItemSubType == subType && r.Grade == (int)targetGrade); + + return hasGrade ? targetGrade : grade; + } + + public static float GetWeight(Grade grade, int itemId, SynthesizeWeightSheet sheet) + { + var gradeRow = sheet.Values.FirstOrDefault(r => r.GradeId == (int)grade); + if (gradeRow == null) + { + return 1; + } + + return gradeRow.WeightDict.TryGetValue(itemId, out var weight) ? weight : 1; + } + + // TODO: move to ItemExtensions + public static List GetItemGuid(ItemBase itemBase) => itemBase switch + { + Costume costume => new List { costume.ItemId, }, + ItemUsable itemUsable => new List { itemUsable.ItemId, }, + _ => throw new ArgumentException($"Unexpected item type: {itemBase.GetType()}", nameof(itemBase)), + }; + + public static List GetItemGuids(IEnumerable itemBases) => itemBases.Select( + i => + { + return i switch + { + Costume costume => costume.ItemId, + ItemUsable itemUsable => itemUsable.ItemId, + _ => throw new ArgumentException($"Unexpected item type: {i.GetType()}", nameof(i)), + }; + }).ToList(); + + private static Grade GetTargetGrade(Grade grade) => grade switch + { + Grade.Normal => Grade.Rare, + Grade.Rare => Grade.Epic, + Grade.Epic => Grade.Unique, + Grade.Unique => Grade.Legendary, + Grade.Legendary => Grade.Divinity, + Grade.Divinity => Grade.Divinity, + _ => throw new ArgumentOutOfRangeException(nameof(grade), grade, null), + }; + + public static int GetTargetGrade(int gradeId) + { + return gradeId switch + { + 1 => 2, // Grade.Normal => Grade.Rare + 2 => 3, // Grade.Rare => Grade.Epic + 3 => 4, // Grade.Epic => Grade.Unique + 4 => 5, // Grade.Unique => Grade.Legendary + 5 => 6, // Grade.Legendary => Grade.Divinity + 6 => 6, // Grade.Divinity => Grade.Divinity (Max) + _ => throw new ArgumentOutOfRangeException(nameof(gradeId), gradeId, null), + }; + } +#endregion Helper + } +} From 08e309a5fcf14d677e80e4d93a533e92365da204 Mon Sep 17 00:00:00 2001 From: Atralupus Date: Sun, 17 Nov 2024 16:42:48 +0900 Subject: [PATCH 030/136] Remove any type --- .../lib9c/src/actions/hack_and_slash.ts | 50 +++++++++++-------- .../tests/actions/hack_and_slash.test.ts | 4 +- 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/integrations/javascript/@planetarium/lib9c/src/actions/hack_and_slash.ts b/integrations/javascript/@planetarium/lib9c/src/actions/hack_and_slash.ts index a8cf8d548b..77c007149e 100644 --- a/integrations/javascript/@planetarium/lib9c/src/actions/hack_and_slash.ts +++ b/integrations/javascript/@planetarium/lib9c/src/actions/hack_and_slash.ts @@ -1,9 +1,11 @@ -import { BencodexDictionary, Value, type Dictionary } from "@planetarium/bencodex"; -import { GameAction, type GameActionArgs } from "./common.js"; import type { Address } from "@planetarium/account"; -import { CreateAvatarArgs } from "./create_avatar.js"; -import { RuneSlotInfo } from "../models/rune_slot_info.js"; - +import { + BencodexDictionary, + type Dictionary, + type Value, +} from "@planetarium/bencodex"; +import type { RuneSlotInfo } from "../models/rune_slot_info.js"; +import { GameAction, type GameActionArgs } from "./common.js"; export type HackAndSlashArgs = { costumes: Array; @@ -13,9 +15,9 @@ export type HackAndSlashArgs = { worldId: bigint; stageId: bigint; stageBuffId: bigint | null; - avatarAddress:Address; - totalPlayCount:bigint; - apStoneCount:bigint; + avatarAddress: Address; + totalPlayCount: bigint; + apStoneCount: bigint; } & GameActionArgs; export class HackAndSlash extends GameAction { @@ -32,8 +34,19 @@ export class HackAndSlash extends GameAction { public readonly runeInfos: Array; public readonly stageBuffId: bigint | null; - - constructor({ avatarAddress, worldId, stageId, equipments, foods, totalPlayCount, apStoneCount, runeInfos, stageBuffId, costumes, id }: HackAndSlashArgs) { + constructor({ + avatarAddress, + worldId, + stageId, + equipments, + foods, + totalPlayCount, + apStoneCount, + runeInfos, + stageBuffId, + costumes, + id, + }: HackAndSlashArgs) { super({ id }); this.costumes = costumes; this.avatarAddress = avatarAddress; @@ -48,7 +61,7 @@ export class HackAndSlash extends GameAction { } protected plain_value_internal(): Dictionary { - var data = new BencodexDictionary([ + const params: [string, Value][] = [ ["avatarAddress", this.avatarAddress.toBytes()], ["equipments", this.equipments.map((equipment) => equipment)], ["costumes", this.costumes.map((costume) => costume)], @@ -57,20 +70,13 @@ export class HackAndSlash extends GameAction { ["worldId", this.worldId.toString()], ["stageId", this.stageId.toString()], ["totalPlayCount", this.totalPlayCount.toString()], - ["apStoneCount", this.apStoneCount.toString()] - ]); + ["apStoneCount", this.apStoneCount.toString()], + ]; if (this.stageBuffId !== null) { - (data as any)["stageBuffId"] = this.stageBuffId.toString(); + params.push(["stageBuffId", this.stageBuffId.toString()]); } - - return data + return new BencodexDictionary(params); } - - - } - - -//stageBuffId \ No newline at end of file diff --git a/integrations/javascript/@planetarium/lib9c/tests/actions/hack_and_slash.test.ts b/integrations/javascript/@planetarium/lib9c/tests/actions/hack_and_slash.test.ts index 0feb3e6f88..dca2f59a87 100644 --- a/integrations/javascript/@planetarium/lib9c/tests/actions/hack_and_slash.test.ts +++ b/integrations/javascript/@planetarium/lib9c/tests/actions/hack_and_slash.test.ts @@ -1,8 +1,8 @@ import { describe } from "vitest"; +import { HackAndSlash } from "../../src/actions/hack_and_slash.js"; import { RuneSlotInfo, uuidToGuidBytes } from "../../src/index.js"; import { runTests } from "./common.js"; import { avatarAddress } from "./fixtures.js"; -import { HackAndSlash } from "../../src/actions/hack_and_slash.js"; describe("HackAndSlash", () => { describe("odin", () => { @@ -13,7 +13,7 @@ describe("HackAndSlash", () => { worldId: BigInt(1), stageId: BigInt(1), stageBuffId: null, - apStoneCount:BigInt(1), + apStoneCount: BigInt(1), totalPlayCount: BigInt(1), costumes: [uuidToGuidBytes("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa")], equipments: [uuidToGuidBytes("bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb")], From 75e0d17a4e1b00979ca869e02506e47030b23142 Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Mon, 18 Nov 2024 20:21:52 +0900 Subject: [PATCH 031/136] add subtype to sheet --- .Lib9c.Tests/Action/SynthesizeTest.cs | 8 ++--- Lib9c/TableCSV/Item/SynthesizeSheet.csv | 32 ++++++++++++++----- Lib9c/TableData/Item/SynthesizeSheet.cs | 42 ++++++++++++++++++++++--- 3 files changed, 67 insertions(+), 15 deletions(-) diff --git a/.Lib9c.Tests/Action/SynthesizeTest.cs b/.Lib9c.Tests/Action/SynthesizeTest.cs index 979e482119..bfc123f6c8 100644 --- a/.Lib9c.Tests/Action/SynthesizeTest.cs +++ b/.Lib9c.Tests/Action/SynthesizeTest.cs @@ -82,7 +82,7 @@ public IWorld Init(out Address agentAddress, out Address avatarAddress, out long public void ExecuteSingle(Grade grade, ItemSubType itemSubType) { var context = new ActionContext(); - var itemSubTypes = GetSubTypeArray(itemSubType, GetSucceededMaterialCount(grade)); + var itemSubTypes = GetSubTypeArray(itemSubType, GetSucceededMaterialCount(itemSubType, grade)); var state = Init(out var agentAddress, out var avatarAddress, out var blockIndex); (state, var items) = UpdateItemsFromSubType(grade, itemSubTypes, state, avatarAddress); @@ -149,7 +149,7 @@ public void ExecuteMultipleSameType(int testCount) { var grade = Grade.Rare; var itemSubType = ItemSubType.FullCostume; - var materialCount = GetSucceededMaterialCount(grade) * testCount; + var materialCount = GetSucceededMaterialCount(itemSubType, grade) * testCount; var itemSubTypes = GetSubTypeArray(itemSubType, materialCount); var state = Init(out var agentAddress, out var avatarAddress, out var blockIndex); @@ -310,10 +310,10 @@ private static ItemSubType[] GetSubTypeArray(ItemSubType subtype, int count) return subTypes; } - private static int GetSucceededMaterialCount(Grade grade) + private static int GetSucceededMaterialCount(ItemSubType itemSubType, Grade grade) { var synthesizeSheet = TableSheets.SynthesizeSheet; var row = synthesizeSheet.Values.First(r => (Grade)r.GradeId == grade); - return row.RequiredCount; + return row.RequiredCountDict[itemSubType].RequiredCount; } } diff --git a/Lib9c/TableCSV/Item/SynthesizeSheet.csv b/Lib9c/TableCSV/Item/SynthesizeSheet.csv index 5ba7c54219..692681082a 100644 --- a/Lib9c/TableCSV/Item/SynthesizeSheet.csv +++ b/Lib9c/TableCSV/Item/SynthesizeSheet.csv @@ -1,8 +1,26 @@ -grade_id,required_count,succeed_rate +grade_id,item_sub_type,required_count,succeed_rate _value of successful_rate is between 0 and 1. only the second decimal place -1,25,0.5 -2,20,0.4 -3,15,0.3 -4,10,0.2 -5,5,0.2 -6,3,0.0 \ No newline at end of file +1,Aura,25,0.5 +1,Grimoire,25,0.5 +1,FullCostume,25,0.5 +1,Title,25,0.5 +2,Aura,20,0.4 +2,Grimoire,20,0.4 +2,FullCostume,20,0.4 +2,Title,20,0.4 +3,Aura,15,0.3 +3,Grimoire,15,0.3 +3,FullCostume,15,0.3 +3,Title,15,0.3 +4,Aura,10,0.2 +4,Grimoire,10,0.2 +4,FullCostume,10,0.2 +4,Title,10,0.2 +5,Aura,5,0.1 +5,Grimoire,5,0.1 +5,FullCostume,5,0.1 +5,Title,5,0.1 +6,Aura,3,0.0 +6,Grimoire,3,0.0 +6,FullCostume,3,0.0 +6,Title,3,0.0 \ No newline at end of file diff --git a/Lib9c/TableData/Item/SynthesizeSheet.cs b/Lib9c/TableData/Item/SynthesizeSheet.cs index 9be097ba65..302f44e51e 100644 --- a/Lib9c/TableData/Item/SynthesizeSheet.cs +++ b/Lib9c/TableData/Item/SynthesizeSheet.cs @@ -1,31 +1,65 @@ using System; using System.Collections.Generic; +using Nekoyume.Model.Item; using static Nekoyume.TableData.TableExtensions; namespace Nekoyume.TableData { + using System.Linq; + [Serializable] public class SynthesizeSheet : Sheet { + public struct SynthesizeData + { + public int RequiredCount; + public float SucceedRate; + } + [Serializable] public class Row : SheetRow { public override int Key => GradeId; public int GradeId { get; private set; } - public int RequiredCount { get; private set; } - public float SucceedRate { get; private set; } + public Dictionary RequiredCountDict { get; private set; } public override void Set(IReadOnlyList fields) { GradeId = ParseInt(fields[0]); - RequiredCount = ParseInt(fields[1], 25); - SucceedRate = ParseFloat(fields[2], 0.0f); + var itemSubType = (ItemSubType) Enum.Parse(typeof(ItemSubType), fields[1]); + var requiredCount = ParseInt(fields[2], 25); + var succeedRate = ParseFloat(fields[3], 0.0f); + RequiredCountDict = new Dictionary + { + [itemSubType] = new () + { + RequiredCount = requiredCount, + SucceedRate = succeedRate, + }, + }; } } public SynthesizeSheet() : base(nameof(SynthesizeSheet)) { } + + protected override void AddRow(int key, Row value) + { + if (!TryGetValue(key, out var row)) + { + Add(key, value); + + return; + } + + if (!value.RequiredCountDict.Any()) + { + return; + } + + row.RequiredCountDict.TryAdd(value.RequiredCountDict.First().Key, value.RequiredCountDict.First().Value); + } } } From 194cd8273436eaff41f80dcfaff825e4ec321ea8 Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Mon, 18 Nov 2024 20:29:57 +0900 Subject: [PATCH 032/136] use gas tracer --- Lib9c/Action/Synthesize.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib9c/Action/Synthesize.cs b/Lib9c/Action/Synthesize.cs index 2f94486fd1..8e5b2791a2 100644 --- a/Lib9c/Action/Synthesize.cs +++ b/Lib9c/Action/Synthesize.cs @@ -60,7 +60,7 @@ public class Synthesize : GameAction /// public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); var states = context.PreviousState; // Collect addresses From dda8d8d263b9d968bdc2406823e98ac3a996afbd Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Mon, 18 Nov 2024 20:55:47 +0900 Subject: [PATCH 033/136] add subtype to simulator --- Lib9c/Helper/SynthesizeSimulator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib9c/Helper/SynthesizeSimulator.cs b/Lib9c/Helper/SynthesizeSimulator.cs index 754d04cad2..c012aa7011 100644 --- a/Lib9c/Helper/SynthesizeSimulator.cs +++ b/Lib9c/Helper/SynthesizeSimulator.cs @@ -59,8 +59,8 @@ public static List Simulate(InputData inputData) } // TODO: subType별로 필요한 아이템 개수가 다를 수 있음 - var requiredCount = synthesizeRow.RequiredCount; - var succeedRate = Math.Clamp(synthesizeRow.SucceedRate, 0, 1); + var requiredCount = synthesizeRow.RequiredCountDict[itemSubType].RequiredCount; + var succeedRate = Math.Clamp(synthesizeRow.RequiredCountDict[itemSubType].SucceedRate, 0, 1); var succeedRatePercentage = (int)(succeedRate * 100); var synthesizeCount = materialCount / requiredCount; From 81f656e8ded2d89b1dac045614f8c5af175d5799 Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Mon, 18 Nov 2024 21:01:01 +0900 Subject: [PATCH 034/136] add isSuccess field --- Lib9c/Helper/SynthesizeSimulator.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/Lib9c/Helper/SynthesizeSimulator.cs b/Lib9c/Helper/SynthesizeSimulator.cs index c012aa7011..c87898ddcc 100644 --- a/Lib9c/Helper/SynthesizeSimulator.cs +++ b/Lib9c/Helper/SynthesizeSimulator.cs @@ -19,7 +19,7 @@ namespace Nekoyume.Helper public struct SynthesizeResult { public ItemBase ItemBase; - // TODO: Add more fields + public bool IsSuccess; } public static class SynthesizeSimulator @@ -83,7 +83,18 @@ public static List Simulate(InputData inputData) // Decide the item to add to inventory based on SynthesizeWeightSheet var synthesizedItem = GetSynthesizedItem(outputGradeId, sheets, random, itemSubType); - synthesizeResults.Add(new SynthesizeResult { ItemBase = synthesizedItem, }); + + if (isSuccess && grade == (Grade)synthesizedItem.Grade) + { + // If there are no items in the data that are one above the current grade, they cannot succeed. + isSuccess = false; + } + + synthesizeResults.Add(new SynthesizeResult + { + ItemBase = synthesizedItem, + IsSuccess = isSuccess, + }); } } } From 02c55c4b0e0adb31ab0dd41118ffafb8152e5a94 Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Wed, 20 Nov 2024 10:45:58 +0900 Subject: [PATCH 035/136] fix code style for namespace --- Lib9c.sln.DotSettings | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Lib9c.sln.DotSettings b/Lib9c.sln.DotSettings index 4acccc4e49..dd92d3ff7a 100644 --- a/Lib9c.sln.DotSettings +++ b/Lib9c.sln.DotSettings @@ -41,8 +41,12 @@ True False 300 - True - True + False + True + True + False + True + True AI CRYSTAL GARAGE From 58b7a8cd4701fc2a19c9bd3909ea91a04a924b8d Mon Sep 17 00:00:00 2001 From: Hyun Seungmin Date: Wed, 20 Nov 2024 11:35:52 +0900 Subject: [PATCH 036/136] handle more values of the exception's constructor --- .Lib9c.Tests/Action/ExceptionTest.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.Lib9c.Tests/Action/ExceptionTest.cs b/.Lib9c.Tests/Action/ExceptionTest.cs index 13537e2a22..b206b0c6f2 100644 --- a/.Lib9c.Tests/Action/ExceptionTest.cs +++ b/.Lib9c.Tests/Action/ExceptionTest.cs @@ -10,6 +10,7 @@ namespace Lib9c.Tests.Action using Bencodex.Types; using Lib9c.Formatters; using Libplanet.Action; + using Libplanet.Blockchain.Renderers.Debug; using Libplanet.Common; using Libplanet.Crypto; using Libplanet.Types.Assets; @@ -167,6 +168,12 @@ bool TryGetDefaultValue(Type type, out object value) return true; } + if (type == typeof(Guid)) + { + value = Guid.NewGuid(); + return true; + } + if (type == typeof(HashDigest)) { value = HashDigest.FromString("baa2081d3b485ef2906c95a3965531ec750a74cfaefe91d0c3061865608b426c"); @@ -185,6 +192,12 @@ bool TryGetDefaultValue(Type type, out object value) return true; } + if (type == typeof(IReadOnlyList)) + { + value = new List(); + return true; + } + if (type == typeof(IAction)) { value = new DailyReward From f7c8303d4cc6c1b5c475f37d6d0b7c32f7dddd86 Mon Sep 17 00:00:00 2001 From: Hyun Seungmin Date: Wed, 20 Nov 2024 12:11:06 +0900 Subject: [PATCH 037/136] add docs --- .Lib9c.Tests/Action/ExceptionTest.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.Lib9c.Tests/Action/ExceptionTest.cs b/.Lib9c.Tests/Action/ExceptionTest.cs index b206b0c6f2..6085d1ad1f 100644 --- a/.Lib9c.Tests/Action/ExceptionTest.cs +++ b/.Lib9c.Tests/Action/ExceptionTest.cs @@ -36,6 +36,10 @@ public ExceptionTest() MessagePackSerializer.DefaultOptions = options; } + /// + /// Get all exceptions defined in the Libplanet namespace. + /// + /// Enumerable object array that contain an exception object. public static IEnumerable GetLibplanetExceptions() { var t = typeof(Exception); @@ -63,6 +67,10 @@ e.Namespace is not null && } } + /// + /// Get all exceptions defined in the Lib9c namespace. + /// + /// Enumerable object array that contain an exception object. public static IEnumerable GetLib9cExceptions() { var t = typeof(Exception); @@ -81,6 +89,10 @@ e.Namespace is not null && } } + /// + /// Tests weather Libplanet and Lib9c exceptions are serializable using MessagePack. + /// + /// The type of the exception being tested. [Theory] [MemberData(nameof(GetLibplanetExceptions))] [MemberData(nameof(GetLib9cExceptions))] From 7002a49a990f53089db12fc4a421388ccd438d43 Mon Sep 17 00:00:00 2001 From: Lee Dogeon Date: Tue, 26 Nov 2024 08:06:08 +0900 Subject: [PATCH 038/136] Add account addresses documentations Related to #2878 --- For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/planetarium/lib9c/issues/2878?shareId=XXXX-XXXX-XXXX-XXXX). --- docs/articles/system/account.md | 53 +++++++++++++++++++++++++++++++++ docs/articles/system/toc.yml | 2 ++ 2 files changed, 55 insertions(+) create mode 100644 docs/articles/system/account.md diff --git a/docs/articles/system/account.md b/docs/articles/system/account.md new file mode 100644 index 0000000000..a6be35960f --- /dev/null +++ b/docs/articles/system/account.md @@ -0,0 +1,53 @@ +# Account + +Since Lib9c is implemented by using Libplanet, Lib9c has several accounts according to Libplanet's account state model. They are listed below and you can use them to query states via NineChronicles.Headless GraphQL, RPC, or something else. + +| Name (`Addresses.*`) | Address | +|------------------------|----------------------------------------------| +| Shop | 0x0000000000000000000000000000000000000000 | +| Ranking | 0x0000000000000000000000000000000000000001 | +| WeeklyArena | 0x0000000000000000000000000000000000000002 | +| TableSheet | 0x0000000000000000000000000000000000000003 | +| GameConfig | 0x0000000000000000000000000000000000000004 | +| RedeemCode | 0x0000000000000000000000000000000000000005 | +| Admin | 0x0000000000000000000000000000000000000006 | +| PendingActivation | 0x0000000000000000000000000000000000000007 | +| ActivatedAccount | 0x0000000000000000000000000000000000000008 | +| Blacksmith | 0x0000000000000000000000000000000000000009 | +| GoldCurrency | 0x000000000000000000000000000000000000000a | +| GoldDistribution | 0x000000000000000000000000000000000000000b | +| AuthorizedMiners | 0x000000000000000000000000000000000000000c | +| Credits | 0x000000000000000000000000000000000000000d | +| UnlockWorld | 0x000000000000000000000000000000000000000e | +| UnlockEquipmentRecipe | 0x000000000000000000000000000000000000000f | +| MaterialCost | 0x0000000000000000000000000000000000000010 | +| StageRandomBuff | 0x0000000000000000000000000000000000000011 | +| Arena | 0x0000000000000000000000000000000000000012 | +| SuperCraft | 0x0000000000000000000000000000000000000013 | +| EventDungeon | 0x0000000000000000000000000000000000000014 | +| Raid | 0x0000000000000000000000000000000000000015 | +| Rune | 0x0000000000000000000000000000000000000016 | +| Market | 0x0000000000000000000000000000000000000017 | +| GarageWallet | 0x0000000000000000000000000000000000000018 | +| AssetMinters | 0x0000000000000000000000000000000000000019 | +| Agent | 0x000000000000000000000000000000000000001a | +| Avatar | 0x000000000000000000000000000000000000001b | +| Inventory | 0x000000000000000000000000000000000000001c | +| WorldInformation | 0x000000000000000000000000000000000000001d | +| QuestList | 0x000000000000000000000000000000000000001e | +| Collection | 0x000000000000000000000000000000000000001f | +| DailyReward | 0x0000000000000000000000000000000000000020 | +| ActionPoint | 0x0000000000000000000000000000000000000021 | +| RuneState | 0x0000000000000000000000000000000000000022 | +| Relationship | 0x0000000000000000000000000000000000000023 | +| CombinationSlot | 0x0000000000000000000000000000000000000024 | +| ClaimedGiftIds | 0x0000000000000000000000000000000000000025 | +| AdventureBoss | 0x0000000000000000000000000000000000000100 | +| BountyBoard | 0x0000000000000000000000000000000000000101 | +| ExploreBoard | 0x0000000000000000000000000000000000000102 | +| ExplorerList | 0x0000000000000000000000000000000000000103 | +| Guild | 0x0000000000000000000000000000000000000200 | +| GuildMemberCounter | 0x0000000000000000000000000000000000000201 | +| GuildApplication | 0x0000000000000000000000000000000000000202 | +| GuildParticipant | 0x0000000000000000000000000000000000000203 | +| EmptyAccountAddress | 0xffffffffffffffffffffffffffffffffffffffff | diff --git a/docs/articles/system/toc.yml b/docs/articles/system/toc.yml index df4e834a95..843369e7f0 100644 --- a/docs/articles/system/toc.yml +++ b/docs/articles/system/toc.yml @@ -7,3 +7,5 @@ items: href: adventure.md - name: Arena href: arena.md +- name: Account + href: account.md From 76e3528d2577700c769ec1c04f5388af4c2e53c6 Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Tue, 26 Nov 2024 16:14:41 +0900 Subject: [PATCH 039/136] use decimal in sheet --- Lib9c/TableData/Item/SynthesizeSheet.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib9c/TableData/Item/SynthesizeSheet.cs b/Lib9c/TableData/Item/SynthesizeSheet.cs index 9be097ba65..b90f2c3f58 100644 --- a/Lib9c/TableData/Item/SynthesizeSheet.cs +++ b/Lib9c/TableData/Item/SynthesizeSheet.cs @@ -14,13 +14,13 @@ public class Row : SheetRow public int GradeId { get; private set; } public int RequiredCount { get; private set; } - public float SucceedRate { get; private set; } + public decimal SucceedRate { get; private set; } public override void Set(IReadOnlyList fields) { GradeId = ParseInt(fields[0]); RequiredCount = ParseInt(fields[1], 25); - SucceedRate = ParseFloat(fields[2], 0.0f); + SucceedRate = ParseDecimal(fields[2]); } } From b5f3c6aa3aa321204990784585ebc53ec50518cc Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Tue, 26 Nov 2024 16:14:48 +0900 Subject: [PATCH 040/136] add action cost ap value --- Lib9c/GameConfig.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lib9c/GameConfig.cs b/Lib9c/GameConfig.cs index ae16a63481..925b56dddd 100644 --- a/Lib9c/GameConfig.cs +++ b/Lib9c/GameConfig.cs @@ -23,6 +23,8 @@ public static class GameConfig #region action + public const int ActionCostAP = 5; + public const int DefaultAvatarCharacterId = 100010; public const int DefaultAvatarWeaponId = 10100000; public const int DefaultAvatarArmorId = 10200000; From cfaad7275f88108baf685a872b262a8bc46e4eef Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Wed, 27 Nov 2024 00:00:07 +0900 Subject: [PATCH 041/136] add chargeAp Property --- Lib9c/Action/Synthesize.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Lib9c/Action/Synthesize.cs b/Lib9c/Action/Synthesize.cs index 8e5b2791a2..ff48dc203c 100644 --- a/Lib9c/Action/Synthesize.cs +++ b/Lib9c/Action/Synthesize.cs @@ -30,6 +30,7 @@ public class Synthesize : GameAction private const string TypeIdentifier = "synthesize"; private const string MaterialsKey = "m"; + private const string ChargeApKey = "c"; private const string AvatarAddressKey = "a"; private static readonly ItemType[] ValidItemType = @@ -48,6 +49,7 @@ public class Synthesize : GameAction #region Fields public List MaterialIds = new(); + public bool ChargeAp; public Address AvatarAddress; private ItemSubType? _cachedItemSubType; @@ -412,6 +414,7 @@ private void SetGradeDict(ref GradeDict gradeDict, int grade, ItemSubType itemSu new Dictionary { [MaterialsKey] = new List(MaterialIds.OrderBy(i => i).Select(i => i.Serialize())), + [ChargeApKey] = ChargeAp.Serialize(), [AvatarAddressKey] = AvatarAddress.Serialize(), } .ToImmutableDictionary(); @@ -419,6 +422,7 @@ private void SetGradeDict(ref GradeDict gradeDict, int grade, ItemSubType itemSu protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) { MaterialIds = plainValue[MaterialsKey].ToList(StateExtensions.ToGuid); + ChargeAp = plainValue[ChargeApKey].ToBoolean(); AvatarAddress = plainValue[AvatarAddressKey].ToAddress(); } #endregion Serialize From 1676626a39096b16fce2de998867ccbdad90b552 Mon Sep 17 00:00:00 2001 From: Lee Dogeon Date: Tue, 26 Nov 2024 08:49:28 +0900 Subject: [PATCH 042/136] Add `makeTx` function with `NetworkProvider` interface --- .../javascript/@planetarium/lib9c/README.md | 22 ++++++++++ .../@planetarium/lib9c/src/index.ts | 1 + .../javascript/@planetarium/lib9c/src/tx.ts | 40 +++++++++++++++++++ 3 files changed, 63 insertions(+) create mode 100644 integrations/javascript/@planetarium/lib9c/src/tx.ts diff --git a/integrations/javascript/@planetarium/lib9c/README.md b/integrations/javascript/@planetarium/lib9c/README.md index 39c4b50d2f..2f4e135b80 100644 --- a/integrations/javascript/@planetarium/lib9c/README.md +++ b/integrations/javascript/@planetarium/lib9c/README.md @@ -3,3 +3,25 @@ This npm package provides functions to build actions equivalent to [Lib9c]. [Lib9c]: https://github.com/planetarium/lib9c + +## Usage Example + +```typescript +import { HeadlessNetworkProvider } from "@planetarium/9c-headless-network-provider"; +import { RawPrivateKey, Address } from "@planetarium/account"; +import { signTx } from "@planetarium/tx"; +import { makeTx, ClaimStakeReward } from "@planetarium/lib9c"; + +const networkProvider = new HeadlessNetworkProvider("https://9c-main-full-state.nine-chronicles.com/graphql"); +const account = RawPrivateKey.generate(); // Temporary private key key. + +const unsignedTx = await makeTx(account, networkProvider, new ClaimStakeReward({ + avatarAddress: Address.fromHex('
'), +})); + +console.log(unsignedTx); + +const signedTx = await signTx(unsignedTx, account); + +console.log(signedTx); +``` diff --git a/integrations/javascript/@planetarium/lib9c/src/index.ts b/integrations/javascript/@planetarium/lib9c/src/index.ts index 22415cf561..e6797e0261 100644 --- a/integrations/javascript/@planetarium/lib9c/src/index.ts +++ b/integrations/javascript/@planetarium/lib9c/src/index.ts @@ -64,3 +64,4 @@ export { type MigratePledgeToGuildArgs, } from "./actions/migrate_pledge_to_guild.js"; export { MakeGuild } from "./actions/make_guild.js"; +export { makeTx, type NetworkProvider } from "./tx.js"; diff --git a/integrations/javascript/@planetarium/lib9c/src/tx.ts b/integrations/javascript/@planetarium/lib9c/src/tx.ts new file mode 100644 index 0000000000..0d231a1eb8 --- /dev/null +++ b/integrations/javascript/@planetarium/lib9c/src/tx.ts @@ -0,0 +1,40 @@ +import { Buffer } from "buffer"; +import { type Account, Address } from "@planetarium/account"; +import type { UnsignedTx } from "@planetarium/tx"; +import type { PolymorphicAction } from "./actions/common.js"; +import { TransferAsset } from "./actions/transfer_asset.js"; +import { TransferAssets } from "./actions/transfer_assets.js"; +import { MEAD, fav } from "./models/currencies.js"; + +export interface NetworkProvider { + getNextNonce(address: Address): Promise; + getGenesisHash(): Promise; +} + +export async function makeTx( + account: Account, + provider: NetworkProvider, + action: PolymorphicAction, +): Promise { + const publicKey = await account.getPublicKey(); + const signer = Address.deriveFrom(publicKey); + const nonce = await provider.getNextNonce(signer); + const genesisHash = await provider.getGenesisHash(); + + const gasLimit = + action instanceof TransferAsset || action instanceof TransferAssets + ? 4n + : 1n; + + return { + nonce, + genesisHash: Buffer.from(genesisHash, "hex"), + signer: signer.toBytes(), + updatedAddresses: new Set(), + actions: [action.bencode()], + publicKey: publicKey.toBytes("uncompressed"), + timestamp: new Date(), + gasLimit, + maxGasPrice: fav(MEAD, 1n), + }; +} From 4d11831fd4c35cbec4a8828b0cd0961b734494cd Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Wed, 27 Nov 2024 11:55:26 +0900 Subject: [PATCH 043/136] Change order of remove item --- Lib9c/Action/Synthesize.cs | 42 +++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/Lib9c/Action/Synthesize.cs b/Lib9c/Action/Synthesize.cs index ff48dc203c..e272a6319a 100644 --- a/Lib9c/Action/Synthesize.cs +++ b/Lib9c/Action/Synthesize.cs @@ -127,6 +127,27 @@ public override IWorld Execute(IActionContext context) throw new InvalidOperationException("ItemSubType is not set."); } + // Unequip items (if necessary) + foreach (var materialEquipment in materialEquipments) + { + materialEquipment.Unequip(); + } + foreach (var materialCostume in materialCostumes) + { + materialCostume.Unequip(); + } + + // Remove materials from inventory + foreach (var materialId in MaterialIds) + { + if (!avatarState.inventory.RemoveNonFungibleItem(materialId)) + { + throw new NotEnoughMaterialException( + $"{addressesHex} Aborted as the material item ({materialId}) does not exist in inventory." + ); + } + } + var synthesizeSheet = sheets.GetSheet(); var random = context.GetRandom(); var synthesizedItems = new List(); @@ -179,27 +200,6 @@ public override IWorld Execute(IActionContext context) } } - // Unequip items (if necessary) - foreach (var materialEquipment in materialEquipments) - { - materialEquipment.Unequip(); - } - foreach (var materialCostume in materialCostumes) - { - materialCostume.Unequip(); - } - - // Remove materials from inventory - foreach (var materialId in MaterialIds) - { - if (!avatarState.inventory.RemoveNonFungibleItem(materialId)) - { - throw new NotEnoughMaterialException( - $"{addressesHex} Aborted as the material item ({materialId}) does not exist in inventory." - ); - } - } - // Add synthesized items to inventory foreach (var item in synthesizedItems) { From bf33ba9aa406c8e99cc1ca2587b896853e0bf21e Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Wed, 27 Nov 2024 13:26:20 +0900 Subject: [PATCH 044/136] add action point logic --- Lib9c/Action/Synthesize.cs | 62 +++++++++++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 8 deletions(-) diff --git a/Lib9c/Action/Synthesize.cs b/Lib9c/Action/Synthesize.cs index e272a6319a..18d03e843b 100644 --- a/Lib9c/Action/Synthesize.cs +++ b/Lib9c/Action/Synthesize.cs @@ -90,8 +90,12 @@ public override IWorld Execute(IActionContext context) typeof(EquipmentItemSheet), typeof(SynthesizeSheet), typeof(SynthesizeWeightSheet), + typeof(MaterialItemSheet), }); + // Calculate action point + var actionPoint = CalculateActionPoint(states, avatarState, sheets, context); + // Initialize variables var materialEquipments = new List(); var materialCostumes = new List(); @@ -158,18 +162,18 @@ public override IWorld Execute(IActionContext context) var gradeId = gradeItem.Key; var subTypeDict = gradeItem.Value; + if (!synthesizeSheet.TryGetValue(gradeId, out var synthesizeRow)) + { + throw new SheetRowNotFoundException( + $"Aborted as the synthesize row for grade ({gradeId}) was failed to load in {nameof(SynthesizeSheet)}", gradeId + ); + } + foreach (var subTypeItem in subTypeDict) { var itemSubType = subTypeItem.Key; var materialCount = subTypeItem.Value; - if (!synthesizeSheet.TryGetValue(gradeId, out var synthesizeRow)) - { - throw new SheetRowNotFoundException( - $"Aborted as the synthesize row for grade ({gradeId}) was failed to load in {nameof(SynthesizeSheet)}", gradeId - ); - } - // TODO: subType별로 필요한 아이템 개수가 다를 수 있음 var requiredCount = synthesizeRow.RequiredCount; var succeedRate = Math.Clamp(synthesizeRow.SucceedRate, 0, 1); @@ -206,7 +210,9 @@ public override IWorld Execute(IActionContext context) avatarState.inventory.AddNonFungibleItem(item); } - return states.SetAvatarState(AvatarAddress, avatarState, true, true, false, false); + return states + .SetActionPoint(AvatarAddress, actionPoint) + .SetAvatarState(AvatarAddress, avatarState, true, true, false, false); } private ItemBase GetSynthesizedItem(Grade grade, Sheets sheets, IRandom random, ItemSubType itemSubTypeValue) @@ -409,6 +415,46 @@ private void SetGradeDict(ref GradeDict gradeDict, int grade, ItemSubType itemSu } } + private long CalculateActionPoint(IWorld states, AvatarState avatarState, Sheets sheets, IActionContext context) + { + if (!states.TryGetActionPoint(AvatarAddress, out var actionPoint)) + { + actionPoint = avatarState.actionPoint; + } + + if (actionPoint < GameConfig.ActionCostAP) + { + switch (ChargeAp) + { + case false: + throw new NotEnoughActionPointException("Action point is not enough. for synthesize."); + case true: + { + var row = sheets.GetSheet() + .OrderedList? + .First(r => r.ItemSubType == ItemSubType.ApStone); + if (row == null) + { + throw new SheetRowNotFoundException( + nameof(MaterialItemSheet), + ItemSubType.ApStone.ToString() + ); + } + + if (!avatarState.inventory.RemoveFungibleItem(row.ItemId, context.BlockIndex)) + { + throw new NotEnoughMaterialException("not enough ap stone."); + } + actionPoint = DailyReward.ActionPointMax; + break; + } + } + } + + actionPoint -= GameConfig.ActionCostAP; + return actionPoint; + } + #region Serialize protected override IImmutableDictionary PlainValueInternal => new Dictionary From 6a6a08e84048fbd3d2a0496ef1f47bcb10529ec2 Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Wed, 27 Nov 2024 15:08:16 +0900 Subject: [PATCH 045/136] change weight type to int --- Lib9c/Action/Synthesize.cs | 17 ++++++----------- Lib9c/TableCSV/Item/SynthesizeWeightSheet.csv | 12 ++++++------ Lib9c/TableData/Item/SynthesizeWeightSheet.cs | 6 +++--- 3 files changed, 15 insertions(+), 20 deletions(-) diff --git a/Lib9c/Action/Synthesize.cs b/Lib9c/Action/Synthesize.cs index 18d03e843b..992b31f26b 100644 --- a/Lib9c/Action/Synthesize.cs +++ b/Lib9c/Action/Synthesize.cs @@ -300,10 +300,10 @@ private ItemBase GetRandomEquipment(Grade grade, ItemSubType itemSubType, Sheets throw new InvalidOperationException("Failed to select a synthesized item."); } - private float GetRandomValueForItem(Grade grade, HashSet synthesizeResultPool, SynthesizeWeightSheet synthesizeWeightSheet, + private int GetRandomValueForItem(Grade grade, HashSet synthesizeResultPool, SynthesizeWeightSheet synthesizeWeightSheet, IRandom random, out List<(int ItemId, float Weight)> itemWeights) { - float totalWeight = 0; + var totalWeight = 0; itemWeights = new List<(int ItemId, float Weight)>(); foreach (var itemId in synthesizeResultPool) { @@ -313,8 +313,7 @@ private float GetRandomValueForItem(Grade grade, HashSet synthesizeResultPo } // Random selection based on weight - var randomValuePercentage = random.Next((int)(totalWeight * 100)); - return randomValuePercentage * 0.01f; + return random.Next(totalWeight); } #endregion GetRandomItem @@ -587,15 +586,11 @@ public static List GetItemGuids(IEnumerable itemBases) => itemBa }; }).ToList(); - public static float GetWeight(Grade grade, int itemId, SynthesizeWeightSheet sheet) + public static int GetWeight(Grade grade, int itemId, SynthesizeWeightSheet sheet) { + var defaultWeight = SynthesizeWeightSheet.DefaultWeight; var gradeRow = sheet.Values.FirstOrDefault(r => r.GradeId == (int)grade); - if (gradeRow == null) - { - return 1; - } - - return gradeRow.WeightDict.TryGetValue(itemId, out var weight) ? weight : 1; + return gradeRow == null ? defaultWeight : gradeRow.WeightDict.GetValueOrDefault(itemId, defaultWeight); } #endregion Helper diff --git a/Lib9c/TableCSV/Item/SynthesizeWeightSheet.csv b/Lib9c/TableCSV/Item/SynthesizeWeightSheet.csv index 796ab41a14..c808bb9e0b 100644 --- a/Lib9c/TableCSV/Item/SynthesizeWeightSheet.csv +++ b/Lib9c/TableCSV/Item/SynthesizeWeightSheet.csv @@ -1,7 +1,7 @@ grade_id,item_id,weight -_default value for weight is 1. only the second decimal place -3,40100001,0.3 -3,40100015,0.1 -3,40100016,0.1 -3,40100019,0.1 -5,49900027,4 \ No newline at end of file +_default value for weight is 10000. +3,40100001,3000 +3,40100015,1000 +3,40100016,1000 +3,40100019,1000 +5,49900027,40000 \ No newline at end of file diff --git a/Lib9c/TableData/Item/SynthesizeWeightSheet.cs b/Lib9c/TableData/Item/SynthesizeWeightSheet.cs index 2fd45385b8..01d8269697 100644 --- a/Lib9c/TableData/Item/SynthesizeWeightSheet.cs +++ b/Lib9c/TableData/Item/SynthesizeWeightSheet.cs @@ -16,15 +16,15 @@ public class Row : SheetRow public int GradeId { get; private set; } - public Dictionary WeightDict { get; private set; } + public Dictionary WeightDict { get; private set; } public override void Set(IReadOnlyList fields) { GradeId = ParseInt(fields[0]); - WeightDict = new Dictionary(); + WeightDict = new Dictionary(); var itemId = ParseInt(fields[1]); - var weight = ParseFloat(fields[2], 1.0f); + var weight = ParseInt(fields[2], 10000); WeightDict.Add(itemId, weight); } } From 6b18bf334ce2c50b106f4ab8f2df264cd054efc9 Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Wed, 27 Nov 2024 15:09:25 +0900 Subject: [PATCH 046/136] add default weight const value --- Lib9c/TableData/Item/SynthesizeWeightSheet.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Lib9c/TableData/Item/SynthesizeWeightSheet.cs b/Lib9c/TableData/Item/SynthesizeWeightSheet.cs index 01d8269697..f7a8030b01 100644 --- a/Lib9c/TableData/Item/SynthesizeWeightSheet.cs +++ b/Lib9c/TableData/Item/SynthesizeWeightSheet.cs @@ -9,6 +9,8 @@ namespace Nekoyume.TableData [Serializable] public class SynthesizeWeightSheet : Sheet { + public const int DefaultWeight = 10000; + [Serializable] public class Row : SheetRow { @@ -24,7 +26,7 @@ public override void Set(IReadOnlyList fields) WeightDict = new Dictionary(); var itemId = ParseInt(fields[1]); - var weight = ParseInt(fields[2], 10000); + var weight = ParseInt(fields[2], DefaultWeight); WeightDict.Add(itemId, weight); } } From a472a346c0f046775b28fcce81f5e0404e6a514d Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Wed, 27 Nov 2024 15:24:54 +0900 Subject: [PATCH 047/136] change sucessedrate type to int --- Lib9c/Action/Synthesize.cs | 5 +---- Lib9c/TableCSV/Item/SynthesizeSheet.csv | 14 +++++++------- Lib9c/TableData/Item/SynthesizeSheet.cs | 8 ++++++-- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/Lib9c/Action/Synthesize.cs b/Lib9c/Action/Synthesize.cs index 992b31f26b..28c611aecc 100644 --- a/Lib9c/Action/Synthesize.cs +++ b/Lib9c/Action/Synthesize.cs @@ -176,9 +176,6 @@ public override IWorld Execute(IActionContext context) // TODO: subType별로 필요한 아이템 개수가 다를 수 있음 var requiredCount = synthesizeRow.RequiredCount; - var succeedRate = Math.Clamp(synthesizeRow.SucceedRate, 0, 1); - var succeedRatePercentage = (int)(succeedRate * 100); - var synthesizeCount = materialCount / requiredCount; var remainder = materialCount % requiredCount; @@ -192,7 +189,7 @@ public override IWorld Execute(IActionContext context) // Calculate success for each synthesis for (var i = 0; i < synthesizeCount; i++) { - var isSuccess = random.Next(100) < succeedRatePercentage; + var isSuccess = random.Next(SynthesizeSheet.SucceedRateMax) < synthesizeRow.SucceedRate; var grade = (Grade)gradeId; var outputGradeId = isSuccess ? GetTargetGrade(grade) : grade; diff --git a/Lib9c/TableCSV/Item/SynthesizeSheet.csv b/Lib9c/TableCSV/Item/SynthesizeSheet.csv index 5ba7c54219..5edcfb8ab3 100644 --- a/Lib9c/TableCSV/Item/SynthesizeSheet.csv +++ b/Lib9c/TableCSV/Item/SynthesizeSheet.csv @@ -1,8 +1,8 @@ grade_id,required_count,succeed_rate -_value of successful_rate is between 0 and 1. only the second decimal place -1,25,0.5 -2,20,0.4 -3,15,0.3 -4,10,0.2 -5,5,0.2 -6,3,0.0 \ No newline at end of file +_value of successful_rate is between 0 and 10000 +1,25,5000 +2,20,4000 +3,15,3000 +4,10,2000 +5,5,2000 +6,3,0 \ No newline at end of file diff --git a/Lib9c/TableData/Item/SynthesizeSheet.cs b/Lib9c/TableData/Item/SynthesizeSheet.cs index b90f2c3f58..ff7923939b 100644 --- a/Lib9c/TableData/Item/SynthesizeSheet.cs +++ b/Lib9c/TableData/Item/SynthesizeSheet.cs @@ -7,6 +7,9 @@ namespace Nekoyume.TableData [Serializable] public class SynthesizeSheet : Sheet { + public const int SucceedRateMin = 0; + public const int SucceedRateMax = 10000; + [Serializable] public class Row : SheetRow { @@ -14,13 +17,14 @@ public class Row : SheetRow public int GradeId { get; private set; } public int RequiredCount { get; private set; } - public decimal SucceedRate { get; private set; } + public int SucceedRate { get; private set; } public override void Set(IReadOnlyList fields) { GradeId = ParseInt(fields[0]); RequiredCount = ParseInt(fields[1], 25); - SucceedRate = ParseDecimal(fields[2]); + var succeedRate = ParseInt(fields[2], SucceedRateMin); + SucceedRate = Math.Min(SucceedRateMax, Math.Max(SucceedRateMin, succeedRate)); } } From cd3e977def407183e32441a3841d217f3c5bf610 Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Wed, 27 Nov 2024 15:31:19 +0900 Subject: [PATCH 048/136] add action point test --- .Lib9c.Tests/Action/SynthesizeTest.cs | 31 +++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/.Lib9c.Tests/Action/SynthesizeTest.cs b/.Lib9c.Tests/Action/SynthesizeTest.cs index 180a248137..506b32cfa0 100644 --- a/.Lib9c.Tests/Action/SynthesizeTest.cs +++ b/.Lib9c.Tests/Action/SynthesizeTest.cs @@ -85,6 +85,7 @@ public void ExecuteSingle(Grade grade, ItemSubType itemSubType) var state = Init(out var agentAddress, out var avatarAddress, out var blockIndex); (state, var items) = UpdateItemsFromSubType(grade, itemSubTypes, state, avatarAddress); + state = state.SetActionPoint(avatarAddress, 120); var action = new Synthesize() { @@ -140,6 +141,34 @@ public void ExecuteSingle(Grade grade, ItemSubType itemSubType) Assert.True(expectedGrade == resultGrade || resultGrade == grade); } + [Fact] + public void ExecuteNotEnoughActionPoint() + { + var grade = Grade.Rare; + var itemSubType = ItemSubType.FullCostume; + var context = new ActionContext(); + var itemSubTypes = GetSubTypeArray(itemSubType, GetSucceededMaterialCount(grade)); + + var state = Init(out var agentAddress, out var avatarAddress, out var blockIndex); + (state, var items) = UpdateItemsFromSubType(grade, itemSubTypes, state, avatarAddress); + + var action = new Synthesize() + { + AvatarAddress = avatarAddress, + MaterialIds = Synthesize.GetItemGuids(items), + }; + + var ctx = new ActionContext + { + BlockIndex = blockIndex, + PreviousState = state, + RandomSeed = 0, + Signer = agentAddress, + }; + + Assert.Throws(() => action.Execute(ctx)); + } + [Theory] [InlineData(2)] [InlineData(3)] @@ -153,6 +182,7 @@ public void ExecuteMultipleSameType(int testCount) var state = Init(out var agentAddress, out var avatarAddress, out var blockIndex); (state, var items) = UpdateItemsFromSubType(grade, itemSubTypes, state, avatarAddress); + state = state.SetActionPoint(avatarAddress, 120); var action = new Synthesize() { @@ -194,6 +224,7 @@ public void ExecuteInvalidMaterial(Grade grade, ItemSubType[] itemSubTypes) var context = new ActionContext(); var state = Init(out var agentAddress, out var avatarAddress, out var blockIndex); (state, var items) = UpdateItemsFromSubType(grade, itemSubTypes, state, avatarAddress); + state = state.SetActionPoint(avatarAddress, 120); var action = new Synthesize() { From 55639a2a9100132921095bf30033ba50a1f6d9f3 Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Wed, 27 Nov 2024 15:39:53 +0900 Subject: [PATCH 049/136] change float to int --- Lib9c/Action/Synthesize.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib9c/Action/Synthesize.cs b/Lib9c/Action/Synthesize.cs index 28c611aecc..9af4a1a994 100644 --- a/Lib9c/Action/Synthesize.cs +++ b/Lib9c/Action/Synthesize.cs @@ -241,7 +241,7 @@ private ItemBase GetRandomCostume(Grade grade, ItemSubType itemSubType, Sheets s } var randomValue = GetRandomValueForItem(grade, synthesizeResultPool, synthesizeWeightSheet, random, out var itemWeights); - float cumulativeWeight = 0; + var cumulativeWeight = 0; foreach (var (itemId, weight) in itemWeights) { cumulativeWeight += weight; @@ -275,7 +275,7 @@ private ItemBase GetRandomEquipment(Grade grade, ItemSubType itemSubType, Sheets } var randomValue = GetRandomValueForItem(grade, synthesizeResultPool, synthesizeWeightSheet, random, out var itemWeights); - float cumulativeWeight = 0; + var cumulativeWeight = 0; foreach (var (itemId, weight) in itemWeights) { cumulativeWeight += weight; @@ -298,10 +298,10 @@ private ItemBase GetRandomEquipment(Grade grade, ItemSubType itemSubType, Sheets } private int GetRandomValueForItem(Grade grade, HashSet synthesizeResultPool, SynthesizeWeightSheet synthesizeWeightSheet, - IRandom random, out List<(int ItemId, float Weight)> itemWeights) + IRandom random, out List<(int ItemId, int Weight)> itemWeights) { var totalWeight = 0; - itemWeights = new List<(int ItemId, float Weight)>(); + itemWeights = new List<(int ItemId, int Weight)>(); foreach (var itemId in synthesizeResultPool) { var weight = GetWeight(grade, itemId, synthesizeWeightSheet); From 3274722638bd13d18c093c66200bfd61c7c2f91f Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Wed, 27 Nov 2024 16:17:14 +0900 Subject: [PATCH 050/136] add comment --- Lib9c/Action/Synthesize.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Lib9c/Action/Synthesize.cs b/Lib9c/Action/Synthesize.cs index 9af4a1a994..d4f2c1b133 100644 --- a/Lib9c/Action/Synthesize.cs +++ b/Lib9c/Action/Synthesize.cs @@ -22,6 +22,8 @@ namespace Nekoyume.Action /// /// Synthesize action is a type of action that synthesizes items. + /// MaterialIds: Id list of items as material + ///
ChargeAp: Whether to charge action points with action execution
///
[Serializable] [ActionType(TypeIdentifier)] @@ -189,6 +191,9 @@ public override IWorld Execute(IActionContext context) // Calculate success for each synthesis for (var i = 0; i < synthesizeCount; i++) { + // random value range is 0 ~ 9999 + // If the SucceedRate of the table is 0, use '<' for always to fail. + // and SucceedRate of the table is 10000, always success(because left value range is 0~9999) var isSuccess = random.Next(SynthesizeSheet.SucceedRateMax) < synthesizeRow.SucceedRate; var grade = (Grade)gradeId; @@ -310,7 +315,7 @@ private int GetRandomValueForItem(Grade grade, HashSet synthesizeResultPool } // Random selection based on weight - return random.Next(totalWeight); + return random.Next(totalWeight + 1); } #endregion GetRandomItem From 9c142f7769a868acea6040b49946ccc7fce0d9b8 Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Wed, 27 Nov 2024 16:21:40 +0900 Subject: [PATCH 051/136] add summary comment to sheet class --- Lib9c/TableData/Item/SynthesizeSheet.cs | 3 +++ Lib9c/TableData/Item/SynthesizeWeightSheet.cs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/Lib9c/TableData/Item/SynthesizeSheet.cs b/Lib9c/TableData/Item/SynthesizeSheet.cs index ff7923939b..7ef292b6ed 100644 --- a/Lib9c/TableData/Item/SynthesizeSheet.cs +++ b/Lib9c/TableData/Item/SynthesizeSheet.cs @@ -4,6 +4,9 @@ namespace Nekoyume.TableData { + /// + /// Represents a SynthesizeSheet. + /// [Serializable] public class SynthesizeSheet : Sheet { diff --git a/Lib9c/TableData/Item/SynthesizeWeightSheet.cs b/Lib9c/TableData/Item/SynthesizeWeightSheet.cs index f7a8030b01..35b270b95a 100644 --- a/Lib9c/TableData/Item/SynthesizeWeightSheet.cs +++ b/Lib9c/TableData/Item/SynthesizeWeightSheet.cs @@ -6,6 +6,9 @@ namespace Nekoyume.TableData { using System.Linq; + /// + /// Represents a SynthesizeWeightSheet. + /// [Serializable] public class SynthesizeWeightSheet : Sheet { From 491ec2a6596a0e10ddcc66ea44d76b98b081ac9f Mon Sep 17 00:00:00 2001 From: s2quake Date: Wed, 27 Nov 2024 16:23:46 +0900 Subject: [PATCH 052/136] fix: Add block actions to PluginActionEvaluator --- .Lib9c.Plugin/PluginActionEvaluator.cs | 22 ++++++++++++++++++---- Lib9c.Policy/Policy/BlockPolicySource.cs | 2 ++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/.Lib9c.Plugin/PluginActionEvaluator.cs b/.Lib9c.Plugin/PluginActionEvaluator.cs index 961d1cab36..3f92335f0b 100644 --- a/.Lib9c.Plugin/PluginActionEvaluator.cs +++ b/.Lib9c.Plugin/PluginActionEvaluator.cs @@ -7,6 +7,7 @@ using Libplanet.Store; using Nekoyume.Action; using Nekoyume.Action.Loader; +using Nekoyume.Action.ValidatorDelegation; namespace Lib9c.Plugin { @@ -19,10 +20,23 @@ public PluginActionEvaluator(IPluginKeyValueStore keyValueStore) var stateStore = new TrieStateStore(new WrappedKeyValueStore(keyValueStore)); _actionEvaluator = new ActionEvaluator( new PolicyActionsRegistry( - beginBlockActions: ImmutableArray.Empty, - endBlockActions: new IAction[] { new RewardGold() }.ToImmutableArray(), - beginTxActions: ImmutableArray.Empty, - endTxActions: ImmutableArray.Empty), + beginBlockActions: new IAction[] { + new SlashValidator(), + new AllocateGuildReward(), + new AllocateReward(), + }.ToImmutableArray(), + endBlockActions: new IAction[] { + new UpdateValidators(), + new RecordProposer(), + new RewardGold(), + new ReleaseValidatorUnbondings(), + }.ToImmutableArray(), + beginTxActions: new IAction[] { + new Mortgage(), + }.ToImmutableArray(), + endTxActions: new IAction[] { + new Reward(), new Refund(), + }.ToImmutableArray()), stateStore, new NCActionLoader()); } diff --git a/Lib9c.Policy/Policy/BlockPolicySource.cs b/Lib9c.Policy/Policy/BlockPolicySource.cs index 17cb854389..5fead8e122 100644 --- a/Lib9c.Policy/Policy/BlockPolicySource.cs +++ b/Lib9c.Policy/Policy/BlockPolicySource.cs @@ -143,6 +143,8 @@ internal IBlockPolicy GetPolicy( maxTransactionsPerSignerPerBlockPolicy); // FIXME: Slight inconsistency due to pre-existing delegate. + // WARNING: If the block actions in the policyActionsRegistry have been modified, + // the constructor of the PluginActionEvaluator must be modified as well. return new BlockPolicy( policyActionsRegistry: new PolicyActionsRegistry( beginBlockActions: new IAction[] { From 1e118972255c6c5ff8cb9aeb720daeb693fa867c Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Wed, 27 Nov 2024 17:21:35 +0900 Subject: [PATCH 053/136] add summary --- Lib9c/Helper/SynthesizeSimulator.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Lib9c/Helper/SynthesizeSimulator.cs b/Lib9c/Helper/SynthesizeSimulator.cs index e1830de253..a3c8678430 100644 --- a/Lib9c/Helper/SynthesizeSimulator.cs +++ b/Lib9c/Helper/SynthesizeSimulator.cs @@ -22,10 +22,18 @@ public struct SynthesizeResult // TODO: Add more fields } + /// + /// A class that simulates the synthesis of items. + /// + /// public static class SynthesizeSimulator { + /// + /// Simulate the synthesis of items. + /// public struct InputData { + // TODO: 각자 테이블로 분리? public Sheets Sheets; public IRandom RandomObject; public GradeDict GradeDict; From de717a8d57c737348a095b20bb7ebca7d8b5ac0f Mon Sep 17 00:00:00 2001 From: Lee Dogeon Date: Wed, 27 Nov 2024 19:41:32 +0900 Subject: [PATCH 054/136] ci(gh-actions): fix `concurrency.group` in `publish-docs` workflow Fixes #3051 There is already `concurrency.group` field in `publish-docs` but it uses `-` as lock key. So it didn't work well and this pull request fixed it as `` --- For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/planetarium/lib9c/issues/3051?shareId=XXXX-XXXX-XXXX-XXXX). --- .github/workflows/publish-docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml index abcfe8e501..999cffca6a 100644 --- a/.github/workflows/publish-docs.yml +++ b/.github/workflows/publish-docs.yml @@ -13,7 +13,7 @@ jobs: permissions: contents: write concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }} steps: - name: Checkout uses: actions/checkout@v4 From 9791bc29216e90b0ed1a44011e368a5491ed1cc2 Mon Sep 17 00:00:00 2001 From: moreal Date: Fri, 29 Nov 2024 08:23:48 +0900 Subject: [PATCH 055/136] ci(gh-actions): remove the step to check Libplanet submodule --- .github/workflows/main.yml | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 16c96c48ea..97261b24ea 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -20,27 +20,6 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Check if .Libplanet refers to a tagged commit - if: | - github.event_name == 'push' && ( - github.ref == 'refs/heads/main' || - startsWith(github.ref, 'refs/heads/rc-') || - startsWith(github.ref, 'refs/tags/') - ) || - github.event_name == 'pull_request' && ( - github.head_ref == 'refs/heads/main' || - startsWith(github.head_ref, 'refs/heads/rc-') || - startsWith(github.head_ref, 'refs/tags/') - ) - run: | - set -e - pushd .Libplanet/ - git fetch origin 'refs/tags/*:refs/tags/*' - if ! git describe --tags --exact-match; then - echo "The unreleased Libplanet shouldn't be used." > /dev/stderr - exit 1 - fi - popd - name: Setup .NET Core uses: actions/setup-dotnet@v4 with: From 2101088f13283f89d389fe075011c3744db8c9b0 Mon Sep 17 00:00:00 2001 From: s2quake Date: Wed, 27 Nov 2024 16:23:46 +0900 Subject: [PATCH 056/136] fix: Add block actions to PluginActionEvaluator --- .Lib9c.Plugin/PluginActionEvaluator.cs | 22 ++++++++++++++++++---- Lib9c.Policy/Policy/BlockPolicySource.cs | 2 ++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/.Lib9c.Plugin/PluginActionEvaluator.cs b/.Lib9c.Plugin/PluginActionEvaluator.cs index 961d1cab36..3f92335f0b 100644 --- a/.Lib9c.Plugin/PluginActionEvaluator.cs +++ b/.Lib9c.Plugin/PluginActionEvaluator.cs @@ -7,6 +7,7 @@ using Libplanet.Store; using Nekoyume.Action; using Nekoyume.Action.Loader; +using Nekoyume.Action.ValidatorDelegation; namespace Lib9c.Plugin { @@ -19,10 +20,23 @@ public PluginActionEvaluator(IPluginKeyValueStore keyValueStore) var stateStore = new TrieStateStore(new WrappedKeyValueStore(keyValueStore)); _actionEvaluator = new ActionEvaluator( new PolicyActionsRegistry( - beginBlockActions: ImmutableArray.Empty, - endBlockActions: new IAction[] { new RewardGold() }.ToImmutableArray(), - beginTxActions: ImmutableArray.Empty, - endTxActions: ImmutableArray.Empty), + beginBlockActions: new IAction[] { + new SlashValidator(), + new AllocateGuildReward(), + new AllocateReward(), + }.ToImmutableArray(), + endBlockActions: new IAction[] { + new UpdateValidators(), + new RecordProposer(), + new RewardGold(), + new ReleaseValidatorUnbondings(), + }.ToImmutableArray(), + beginTxActions: new IAction[] { + new Mortgage(), + }.ToImmutableArray(), + endTxActions: new IAction[] { + new Reward(), new Refund(), + }.ToImmutableArray()), stateStore, new NCActionLoader()); } diff --git a/Lib9c.Policy/Policy/BlockPolicySource.cs b/Lib9c.Policy/Policy/BlockPolicySource.cs index 17cb854389..5fead8e122 100644 --- a/Lib9c.Policy/Policy/BlockPolicySource.cs +++ b/Lib9c.Policy/Policy/BlockPolicySource.cs @@ -143,6 +143,8 @@ internal IBlockPolicy GetPolicy( maxTransactionsPerSignerPerBlockPolicy); // FIXME: Slight inconsistency due to pre-existing delegate. + // WARNING: If the block actions in the policyActionsRegistry have been modified, + // the constructor of the PluginActionEvaluator must be modified as well. return new BlockPolicy( policyActionsRegistry: new PolicyActionsRegistry( beginBlockActions: new IAction[] { From 7c19a0778c19ad8dc51b1876a3ea350762135d4e Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Tue, 3 Dec 2024 13:45:32 +0900 Subject: [PATCH 057/136] remove parse float --- Lib9c/TableData/Character/CharacterSheet.cs | 2 +- Lib9c/TableData/TableExtensions.cs | 12 ------------ 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/Lib9c/TableData/Character/CharacterSheet.cs b/Lib9c/TableData/Character/CharacterSheet.cs index e13c3e9aeb..e9a404ce1d 100644 --- a/Lib9c/TableData/Character/CharacterSheet.cs +++ b/Lib9c/TableData/Character/CharacterSheet.cs @@ -56,7 +56,7 @@ public override void Set(IReadOnlyList fields) RunSpeed = TryParseFloat(fields[16], out var runSpeed) ? runSpeed : 1f; } } - + public CharacterSheet() : base(nameof(CharacterSheet)) { } diff --git a/Lib9c/TableData/TableExtensions.cs b/Lib9c/TableData/TableExtensions.cs index eaf3d6377d..33e2bed784 100644 --- a/Lib9c/TableData/TableExtensions.cs +++ b/Lib9c/TableData/TableExtensions.cs @@ -45,18 +45,6 @@ public static decimal ParseDecimal(string value) public static decimal ParseDecimal(string value, decimal defaultValue) => TryParseDecimal(value, out var result) ? result : defaultValue; - public static float ParseFloat(string value) - { - if (TryParseFloat(value, out var result)) - { - return result; - } - throw new ArgumentException(value); - } - - public static float ParseFloat(string value, float defaultValue) => - TryParseFloat(value, out var result) ? result : defaultValue; - public static long ParseLong(string value) { if (TryParseLong(value, out var result)) From b7135ba6b3dea17e022c4c7818ade9146edf2b0c Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Wed, 4 Dec 2024 13:27:46 +0900 Subject: [PATCH 058/136] fix input data fields --- Lib9c/Action/Synthesize.cs | 5 +++- Lib9c/Helper/SynthesizeSimulator.cs | 38 +++++++++++++---------------- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/Lib9c/Action/Synthesize.cs b/Lib9c/Action/Synthesize.cs index 70a08162ed..938eb03c64 100644 --- a/Lib9c/Action/Synthesize.cs +++ b/Lib9c/Action/Synthesize.cs @@ -156,7 +156,10 @@ public override IWorld Execute(IActionContext context) var synthesizedItems = SynthesizeSimulator.Simulate(new SynthesizeSimulator.InputData() { - Sheets = sheets, + SynthesizeSheet = sheets.GetSheet(), + SynthesizeWeightSheet = sheets.GetSheet(), + CostumeItemSheet = sheets.GetSheet(), + EquipmentItemSheet = sheets.GetSheet(), RandomObject = context.GetRandom(), GradeDict = gradeDict, }); diff --git a/Lib9c/Helper/SynthesizeSimulator.cs b/Lib9c/Helper/SynthesizeSimulator.cs index a3c8678430..0162a2cb01 100644 --- a/Lib9c/Helper/SynthesizeSimulator.cs +++ b/Lib9c/Helper/SynthesizeSimulator.cs @@ -13,7 +13,6 @@ namespace Nekoyume.Helper { - using Sheets = Dictionary; using GradeDict = Dictionary>; public struct SynthesizeResult @@ -33,8 +32,10 @@ public static class SynthesizeSimulator /// public struct InputData { - // TODO: 각자 테이블로 분리? - public Sheets Sheets; + public SynthesizeSheet SynthesizeSheet; + public SynthesizeWeightSheet SynthesizeWeightSheet; + public CostumeItemSheet CostumeItemSheet; + public EquipmentItemSheet EquipmentItemSheet; public IRandom RandomObject; public GradeDict GradeDict; } @@ -43,8 +44,7 @@ public static List Simulate(InputData inputData) { var synthesizeResults = new List(); - var sheets = inputData.Sheets; - var synthesizeSheet = sheets.GetSheet(); + var synthesizeSheet = inputData.SynthesizeSheet; var random = inputData.RandomObject; var gradeDict = inputData.GradeDict; @@ -90,7 +90,7 @@ public static List Simulate(InputData inputData) var outputGradeId = isSuccess ? GetTargetGrade(grade) : grade; // Decide the item to add to inventory based on SynthesizeWeightSheet - var synthesizedItem = GetSynthesizedItem(outputGradeId, sheets, random, itemSubType); + var synthesizedItem = GetSynthesizedItem(outputGradeId, inputData.SynthesizeWeightSheet, inputData.CostumeItemSheet, inputData.EquipmentItemSheet, random, itemSubType); synthesizeResults.Add(new SynthesizeResult { ItemBase = synthesizedItem, }); } } @@ -99,16 +99,16 @@ public static List Simulate(InputData inputData) return synthesizeResults; } - private static ItemBase GetSynthesizedItem(Grade grade, Sheets sheets, IRandom random, ItemSubType itemSubTypeValue) + private static ItemBase GetSynthesizedItem(Grade grade, SynthesizeWeightSheet weightSheet, CostumeItemSheet costumeItemSheet, EquipmentItemSheet equipmentItemSheet, IRandom random, ItemSubType itemSubTypeValue) { switch (itemSubTypeValue) { case ItemSubType.FullCostume: case ItemSubType.Title: - return GetRandomCostume(grade, itemSubTypeValue, sheets, random); + return GetRandomCostume(grade, itemSubTypeValue, weightSheet, costumeItemSheet, random); case ItemSubType.Aura: case ItemSubType.Grimoire: - return GetRandomEquipment(grade, itemSubTypeValue, sheets, random); + return GetRandomEquipment(grade, itemSubTypeValue, weightSheet, equipmentItemSheet, random); default: throw new ArgumentOutOfRangeException(); } @@ -116,18 +116,16 @@ private static ItemBase GetSynthesizedItem(Grade grade, Sheets sheets, IRandom r #region GetRandomItem - private static ItemBase GetRandomCostume(Grade grade, ItemSubType itemSubType, Sheets sheets, IRandom random) + private static ItemBase GetRandomCostume(Grade grade, ItemSubType itemSubType, SynthesizeWeightSheet weightSheet, CostumeItemSheet costumeItemSheet, IRandom random) { - var sheet = sheets.GetSheet(); - var synthesizeWeightSheet = sheets.GetSheet(); - var synthesizeResultPool = GetSynthesizeResultPool(grade, itemSubType, sheet); + var synthesizeResultPool = GetSynthesizeResultPool(grade, itemSubType, costumeItemSheet); if (synthesizeResultPool.Count == 0) { throw new InvalidOperationException($"No available items to synthesize for grade {grade} and subtype {itemSubType}"); } - var randomValue = GetRandomValueForItem(grade, synthesizeResultPool, synthesizeWeightSheet, random, out var itemWeights); + var randomValue = GetRandomValueForItem(grade, synthesizeResultPool, weightSheet, random, out var itemWeights); var cumulativeWeight = 0; foreach (var (itemId, weight) in itemWeights) { @@ -137,7 +135,7 @@ private static ItemBase GetRandomCostume(Grade grade, ItemSubType itemSubType, S continue; } - if (!sheet.TryGetValue(itemId, out var equipmentRow)) + if (!costumeItemSheet.TryGetValue(itemId, out var equipmentRow)) { throw new SheetRowNotFoundException( $"Aborted as the equipment row ({itemId}) was failed to load in {nameof(EquipmentItemSheet)}", itemId @@ -150,18 +148,16 @@ private static ItemBase GetRandomCostume(Grade grade, ItemSubType itemSubType, S throw new InvalidOperationException("Failed to select a synthesized item."); } - private static ItemBase GetRandomEquipment(Grade grade, ItemSubType itemSubType, Sheets sheets, IRandom random) + private static ItemBase GetRandomEquipment(Grade grade, ItemSubType itemSubType, SynthesizeWeightSheet weightSheet, EquipmentItemSheet equipmentItemSheet, IRandom random) { - var sheet = sheets.GetSheet(); - var synthesizeWeightSheet = sheets.GetSheet(); - var synthesizeResultPool = GetSynthesizeResultPool(grade, itemSubType, sheet); + var synthesizeResultPool = GetSynthesizeResultPool(grade, itemSubType, equipmentItemSheet); if (synthesizeResultPool.Count == 0) { throw new InvalidOperationException($"No available items to synthesize for grade {grade} and subtype {itemSubType}"); } - var randomValue = GetRandomValueForItem(grade, synthesizeResultPool, synthesizeWeightSheet, random, out var itemWeights); + var randomValue = GetRandomValueForItem(grade, synthesizeResultPool, weightSheet, random, out var itemWeights); var cumulativeWeight = 0; foreach (var (itemId, weight) in itemWeights) { @@ -171,7 +167,7 @@ private static ItemBase GetRandomEquipment(Grade grade, ItemSubType itemSubType, continue; } - if (!sheet.TryGetValue(itemId, out var equipmentRow)) + if (!equipmentItemSheet.TryGetValue(itemId, out var equipmentRow)) { throw new SheetRowNotFoundException( $"Aborted as the equipment row ({itemId}) was failed to load in {nameof(EquipmentItemSheet)}", itemId From 6b74b8332ad9b5bd09faebaa66a77b8567209673 Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Wed, 4 Dec 2024 13:49:28 +0900 Subject: [PATCH 059/136] fix parameter --- Lib9c/Action/Synthesize.cs | 147 +------------------------- Lib9c/Helper/SynthesizeSimulator.cs | 153 +++++++++++++++++++++++++++- 2 files changed, 153 insertions(+), 147 deletions(-) diff --git a/Lib9c/Action/Synthesize.cs b/Lib9c/Action/Synthesize.cs index 938eb03c64..ae399c345c 100644 --- a/Lib9c/Action/Synthesize.cs +++ b/Lib9c/Action/Synthesize.cs @@ -35,26 +35,10 @@ public class Synthesize : GameAction private const string ChargeApKey = "c"; private const string AvatarAddressKey = "a"; - private static readonly ItemType[] ValidItemType = - { - ItemType.Costume, - ItemType.Equipment, - }; - - private static readonly ItemSubType[] ValidItemSubType = - { - ItemSubType.FullCostume, - ItemSubType.Title, - ItemSubType.Grimoire, - ItemSubType.Aura, - }; - #region Fields public List MaterialIds = new(); public bool ChargeAp; public Address AvatarAddress; - - private ItemSubType? _cachedItemSubType; #endregion Fields /// @@ -99,39 +83,8 @@ public override IWorld Execute(IActionContext context) var actionPoint = CalculateActionPoint(states, avatarState, sheets, context); // Initialize variables - var materialEquipments = new List(); - var materialCostumes = new List(); - var gradeDict = new GradeDict(); - - // Process materials - foreach (var materialId in MaterialIds) - { - var materialEquipment = GetEquipmentFromId(materialId, avatarState, context, addressesHex); - var materialCostume = GetCostumeFromId(materialId, avatarState, addressesHex); - if (materialEquipment == null && materialCostume == null) - { - throw new InvalidMaterialException( - $"{addressesHex} Aborted as the material item is not a valid item type." - ); - } - - if (materialEquipment != null) - { - materialEquipments.Add(materialEquipment); - SetGradeDict(ref gradeDict, materialEquipment.Grade, materialEquipment.ItemSubType); - } - - if (materialCostume != null) - { - materialCostumes.Add(materialCostume); - SetGradeDict(ref gradeDict, materialCostume.Grade, materialCostume.ItemSubType); - } - } - - if (_cachedItemSubType == null) - { - throw new InvalidOperationException("ItemSubType is not set."); - } + var gradeDict = SynthesizeSimulator.GetGradeDict(MaterialIds, avatarState, context.BlockIndex, + addressesHex, out var materialEquipments, out var materialCostumes); // Unequip items (if necessary) foreach (var materialEquipment in materialEquipments) @@ -175,102 +128,6 @@ public override IWorld Execute(IActionContext context) .SetAvatarState(AvatarAddress, avatarState, true, true, false, false); } - private Equipment? GetEquipmentFromId(Guid materialId, AvatarState avatarState, IActionContext context, string addressesHex) - { - if (!avatarState.inventory.TryGetNonFungibleItem(materialId, out Equipment materialEquipment)) - { - return null; - } - - if (materialEquipment.RequiredBlockIndex > context.BlockIndex) - { - throw new RequiredBlockIndexException( - $"{addressesHex} Aborted as the material ({materialId}) is not available yet;" + - $" it will be available at the block #{materialEquipment.RequiredBlockIndex}." - ); - } - - // Validate item type - if (!ValidItemType.Contains(materialEquipment.ItemType)) - { - throw new InvalidMaterialException( - $"{addressesHex} Aborted as the material item is not a valid item type: {materialEquipment.ItemType}." - ); - } - - if (!ValidItemSubType.Contains(materialEquipment.ItemSubType)) - { - throw new InvalidMaterialException( - $"{addressesHex} Aborted as the material item is not a valid item sub type: {materialEquipment.ItemSubType}." - ); - } - - _cachedItemSubType ??= materialEquipment.ItemSubType; - if (materialEquipment.ItemSubType != _cachedItemSubType) - { - throw new InvalidMaterialException( - $"{addressesHex} Aborted as the material item is not a {_cachedItemSubType}, but {materialEquipment.ItemSubType}." - ); - } - - return materialEquipment; - } - - private Costume? GetCostumeFromId(Guid materialId, AvatarState avatarState, string addressesHex) - { - if (!avatarState.inventory.TryGetNonFungibleItem(materialId, out Costume costumeItem)) - { - return null; - } - - // Validate item type - if (!ValidItemType.Contains(costumeItem.ItemType)) - { - throw new InvalidMaterialException( - $"{addressesHex} Aborted as the material item is not a valid item type: {costumeItem.ItemType}." - ); - } - - if (!ValidItemSubType.Contains(costumeItem.ItemSubType)) - { - throw new InvalidMaterialException( - $"{addressesHex} Aborted as the material item is not a valid item sub type: {costumeItem.ItemSubType}." - ); - } - - _cachedItemSubType ??= costumeItem.ItemSubType; - if (costumeItem.ItemSubType != _cachedItemSubType) - { - throw new InvalidMaterialException( - $"{addressesHex} Aborted as the material item is not a {_cachedItemSubType}, but {costumeItem.ItemSubType}." - ); - } - - return costumeItem; - } - - private void SetGradeDict(ref GradeDict gradeDict, int grade, ItemSubType itemSubType) - { - if (gradeDict.ContainsKey(grade)) - { - if (gradeDict[grade].ContainsKey(itemSubType)) - { - gradeDict[grade][itemSubType]++; - } - else - { - gradeDict[grade][itemSubType] = 1; - } - } - else - { - gradeDict[grade] = new Dictionary - { - { itemSubType, 1 }, - }; - } - } - private long CalculateActionPoint(IWorld states, AvatarState avatarState, Sheets sheets, IActionContext context) { if (!states.TryGetActionPoint(AvatarAddress, out var actionPoint)) diff --git a/Lib9c/Helper/SynthesizeSimulator.cs b/Lib9c/Helper/SynthesizeSimulator.cs index 0162a2cb01..a04a73be30 100644 --- a/Lib9c/Helper/SynthesizeSimulator.cs +++ b/Lib9c/Helper/SynthesizeSimulator.cs @@ -4,11 +4,10 @@ using System.Collections.Generic; using System.Linq; using Libplanet.Action; -using Libplanet.Crypto; using Nekoyume.Action; -using Nekoyume.Extensions; using Nekoyume.Model.Item; using Nekoyume.Model.EnumType; +using Nekoyume.Model.State; using Nekoyume.TableData; namespace Nekoyume.Helper @@ -27,6 +26,20 @@ public struct SynthesizeResult /// public static class SynthesizeSimulator { + private static readonly ItemType[] ValidItemType = + { + ItemType.Costume, + ItemType.Equipment, + }; + + private static readonly ItemSubType[] ValidItemSubType = + { + ItemSubType.FullCostume, + ItemSubType.Title, + ItemSubType.Grimoire, + ItemSubType.Aura, + }; + /// /// Simulate the synthesis of items. /// @@ -99,6 +112,142 @@ public static List Simulate(InputData inputData) return synthesizeResults; } + public static GradeDict GetGradeDict(List materialIds, AvatarState avatarState, long blockIndex, + string addressesHex, out List materialEquipments, out List materialCostumes) + { + ItemSubType? cachedItemSubType = null; + var gradeDict = new GradeDict(); + materialEquipments = new List(); + materialCostumes = new List(); + + foreach (var materialId in materialIds) + { + var materialEquipment = GetEquipmentFromId(materialId, avatarState, blockIndex, addressesHex, ref cachedItemSubType); + var materialCostume = GetCostumeFromId(materialId, avatarState, addressesHex, ref cachedItemSubType); + if (materialEquipment == null && materialCostume == null) + { + throw new InvalidMaterialException( + $"{addressesHex} Aborted as the material item is not a valid item type." + ); + } + + if (materialEquipment != null) + { + materialEquipments.Add(materialEquipment); + SetGradeDict(ref gradeDict, materialEquipment.Grade, materialEquipment.ItemSubType); + } + + if (materialCostume != null) + { + materialCostumes.Add(materialCostume); + SetGradeDict(ref gradeDict, materialCostume.Grade, materialCostume.ItemSubType); + } + } + + if (cachedItemSubType == null) + { + throw new InvalidOperationException("ItemSubType is not set."); + } + + return gradeDict; + } + + private static void SetGradeDict(ref GradeDict gradeDict, int grade, ItemSubType itemSubType) + { + if (gradeDict.ContainsKey(grade)) + { + if (gradeDict[grade].ContainsKey(itemSubType)) + { + gradeDict[grade][itemSubType]++; + } + else + { + gradeDict[grade][itemSubType] = 1; + } + } + else + { + gradeDict[grade] = new Dictionary + { + { itemSubType, 1 }, + }; + } + } + + private static Equipment? GetEquipmentFromId(Guid materialId, AvatarState avatarState, long blockIndex, string addressesHex, ref ItemSubType? cachedItemSubType) + { + if (!avatarState.inventory.TryGetNonFungibleItem(materialId, out Equipment materialEquipment)) + { + return null; + } + + if (materialEquipment.RequiredBlockIndex > blockIndex) + { + throw new RequiredBlockIndexException( + $"{addressesHex} Aborted as the material ({materialId}) is not available yet;" + + $" it will be available at the block #{materialEquipment.RequiredBlockIndex}." + ); + } + + // Validate item type + if (!ValidItemType.Contains(materialEquipment.ItemType)) + { + throw new InvalidMaterialException( + $"{addressesHex} Aborted as the material item is not a valid item type: {materialEquipment.ItemType}." + ); + } + + if (!ValidItemSubType.Contains(materialEquipment.ItemSubType)) + { + throw new InvalidMaterialException( + $"{addressesHex} Aborted as the material item is not a valid item sub type: {materialEquipment.ItemSubType}." + ); + } + + cachedItemSubType ??= materialEquipment.ItemSubType; + if (materialEquipment.ItemSubType != cachedItemSubType) + { + throw new InvalidMaterialException( + $"{addressesHex} Aborted as the material item is not a {cachedItemSubType}, but {materialEquipment.ItemSubType}." + ); + } + + return materialEquipment; + } + + private static Costume? GetCostumeFromId(Guid materialId, AvatarState avatarState, string addressesHex, ref ItemSubType? cachedItemSubType) + { + if (!avatarState.inventory.TryGetNonFungibleItem(materialId, out Costume costumeItem)) + { + return null; + } + + // Validate item type + if (!ValidItemType.Contains(costumeItem.ItemType)) + { + throw new InvalidMaterialException( + $"{addressesHex} Aborted as the material item is not a valid item type: {costumeItem.ItemType}." + ); + } + + if (!ValidItemSubType.Contains(costumeItem.ItemSubType)) + { + throw new InvalidMaterialException( + $"{addressesHex} Aborted as the material item is not a valid item sub type: {costumeItem.ItemSubType}." + ); + } + + cachedItemSubType ??= costumeItem.ItemSubType; + if (costumeItem.ItemSubType != cachedItemSubType) + { + throw new InvalidMaterialException( + $"{addressesHex} Aborted as the material item is not a {cachedItemSubType}, but {costumeItem.ItemSubType}." + ); + } + + return costumeItem; + } + private static ItemBase GetSynthesizedItem(Grade grade, SynthesizeWeightSheet weightSheet, CostumeItemSheet costumeItemSheet, EquipmentItemSheet equipmentItemSheet, IRandom random, ItemSubType itemSubTypeValue) { switch (itemSubTypeValue) From 1fea71ee2cb7b4fdbe302cb9c1f327e72744cd18 Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Wed, 4 Dec 2024 15:28:02 +0900 Subject: [PATCH 060/136] fix remainder check --- Lib9c/Helper/SynthesizeSimulator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib9c/Helper/SynthesizeSimulator.cs b/Lib9c/Helper/SynthesizeSimulator.cs index a04a73be30..b2dfa55553 100644 --- a/Lib9c/Helper/SynthesizeSimulator.cs +++ b/Lib9c/Helper/SynthesizeSimulator.cs @@ -84,7 +84,7 @@ public static List Simulate(InputData inputData) var synthesizeCount = materialCount / requiredCount; var remainder = materialCount % requiredCount; - if (synthesizeCount <= 0 || remainder > 0) + if (synthesizeCount <= 0 || remainder != 0) { throw new NotEnoughMaterialException( $"Aborted as the number of materials for grade {gradeId} and subtype {itemSubType} is not enough." From c537139923020ab0553d3b566bee7cfc7abda9bb Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Wed, 4 Dec 2024 16:02:12 +0900 Subject: [PATCH 061/136] fix test --- .Lib9c.Tests/Action/SynthesizeTest.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.Lib9c.Tests/Action/SynthesizeTest.cs b/.Lib9c.Tests/Action/SynthesizeTest.cs index e7ff089456..cc8347557c 100644 --- a/.Lib9c.Tests/Action/SynthesizeTest.cs +++ b/.Lib9c.Tests/Action/SynthesizeTest.cs @@ -48,7 +48,9 @@ public IWorld Init(out Address agentAddress, out Address avatarAddress, out long .SetAgentState(agentAddress, new AgentState(agentAddress)); foreach (var (key, value) in Sheets) + { state = state.SetLegacyState(Addresses.TableSheet.Derive(key), value.Serialize()); + } var gameConfigState = new GameConfigState(Sheets[nameof(GameConfigSheet)]); var avatarState = AvatarState.Create( @@ -81,7 +83,6 @@ public IWorld Init(out Address agentAddress, out Address avatarAddress, out long [InlineData((Grade)6, ItemSubType.Aura)] public void ExecuteSingle(Grade grade, ItemSubType itemSubType) { - var context = new ActionContext(); var itemSubTypes = GetSubTypeArray(itemSubType, GetSucceededMaterialCount(itemSubType, grade)); var state = Init(out var agentAddress, out var avatarAddress, out var blockIndex); @@ -147,8 +148,7 @@ public void ExecuteNotEnoughActionPoint() { var grade = Grade.Rare; var itemSubType = ItemSubType.FullCostume; - var context = new ActionContext(); - var itemSubTypes = GetSubTypeArray(itemSubType, GetSucceededMaterialCount(grade)); + var itemSubTypes = GetSubTypeArray(itemSubType, GetSucceededMaterialCount(itemSubType, grade)); var state = Init(out var agentAddress, out var avatarAddress, out var blockIndex); (state, var items) = UpdateItemsFromSubType(grade, itemSubTypes, state, avatarAddress); @@ -222,7 +222,6 @@ public void ExecuteMultipleSameType(int testCount) [InlineData((Grade)3, new[] { ItemSubType.Aura, ItemSubType.Aura, ItemSubType.Grimoire })] public void ExecuteInvalidMaterial(Grade grade, ItemSubType[] itemSubTypes) { - var context = new ActionContext(); var state = Init(out var agentAddress, out var avatarAddress, out var blockIndex); (state, var items) = UpdateItemsFromSubType(grade, itemSubTypes, state, avatarAddress); state = state.SetActionPoint(avatarAddress, 120); From 8fbdf2e1e3f13f6a65427e881c89f0d0306a84ae Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Wed, 4 Dec 2024 16:34:47 +0900 Subject: [PATCH 062/136] add summary --- Lib9c/Helper/SynthesizeSimulator.cs | 76 +++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/Lib9c/Helper/SynthesizeSimulator.cs b/Lib9c/Helper/SynthesizeSimulator.cs index 94ef6f3ae5..6626fbede9 100644 --- a/Lib9c/Helper/SynthesizeSimulator.cs +++ b/Lib9c/Helper/SynthesizeSimulator.cs @@ -14,6 +14,9 @@ namespace Nekoyume.Helper { using GradeDict = Dictionary>; + /// + /// Represents the result of the synthesis. + /// public struct SynthesizeResult { public ItemBase ItemBase; @@ -53,6 +56,13 @@ public struct InputData public GradeDict GradeDict; } + /// + /// Simulate the synthesis of items. + /// + /// + /// + /// + /// public static List Simulate(InputData inputData) { var synthesizeResults = new List(); @@ -125,6 +135,18 @@ public static List Simulate(InputData inputData) return synthesizeResults; } + /// + /// Get the grade of the material items and return the dictionary of the grade and the number of items. + /// + /// material item ids + /// target avatar state + /// current block index + /// addresses in hex + /// material equipment list + /// material costume list + /// + /// + /// public static GradeDict GetGradeDict(List materialIds, AvatarState avatarState, long blockIndex, string addressesHex, out List materialEquipments, out List materialCostumes) { @@ -362,6 +384,13 @@ private static int GetRandomValueForItem(Grade grade, HashSet synthesizeRes #region Helper + /// + /// get expected result item pool (Costume) + /// + /// grade of material items + /// subtype of material items + /// costume item sheet to use + /// expected result item pool of costume public static HashSet GetSynthesizeResultPool(Grade sourceGrade, ItemSubType subType, CostumeItemSheet sheet) { return sheet.Values @@ -371,6 +400,13 @@ public static HashSet GetSynthesizeResultPool(Grade sourceGrade, ItemSubTyp .ToHashSet(); } + /// + /// get expected result item pool (Equipment) + /// + /// grade of material items + /// subtype of material items + /// equipment item sheet to use + /// expected result item pool of equipment public static HashSet GetSynthesizeResultPool(Grade sourceGrade, ItemSubType subType, EquipmentItemSheet sheet) { return sheet.Values @@ -414,6 +450,13 @@ public static List GetSynthesizeResultPool(List sourceGrades, ItemSu .ToList(); } + /// + /// Returns the grade of the item that can be obtained by synthesizing the item. (Costume) + /// + /// source grade + /// subtype of material items + /// costume item sheet to use + /// grade of the item that can be obtained by synthesizing the item public static Grade GetUpgradeGrade(Grade grade ,ItemSubType subType, CostumeItemSheet sheet) { var targetGrade = GetTargetGrade(grade); @@ -423,6 +466,13 @@ public static Grade GetUpgradeGrade(Grade grade ,ItemSubType subType, CostumeIte return hasGrade ? targetGrade : grade; } + /// + /// Returns the grade of the item that can be obtained by synthesizing the item. (Equipment) + /// + /// source grade + /// subtype of material items + /// equipment item sheet to use + /// grade of the item that can be obtained by synthesizing the item public static Grade GetUpgradeGrade(Grade grade ,ItemSubType subType, EquipmentItemSheet sheet) { var targetGrade = GetTargetGrade(grade); @@ -432,6 +482,13 @@ public static Grade GetUpgradeGrade(Grade grade ,ItemSubType subType, EquipmentI return hasGrade ? targetGrade : grade; } + /// + /// Returns the weight of the item that can be obtained by synthesizing the item. + /// + /// source grade + /// item id of material item + /// SynthesizeWeightSheet to use + /// weight of the item that can be obtained by synthesizing the item public static int GetWeight(Grade grade, int itemId, SynthesizeWeightSheet sheet) { var defaultWeight = SynthesizeWeightSheet.DefaultWeight; @@ -440,6 +497,12 @@ public static int GetWeight(Grade grade, int itemId, SynthesizeWeightSheet sheet } // TODO: move to ItemExtensions + /// + /// Get the item id from the item. + /// + /// item to get id + /// item id + /// public static List GetItemGuid(ItemBase itemBase) => itemBase switch { Costume costume => new List { costume.ItemId, }, @@ -447,6 +510,12 @@ public static int GetWeight(Grade grade, int itemId, SynthesizeWeightSheet sheet _ => throw new ArgumentException($"Unexpected item type: {itemBase.GetType()}", nameof(itemBase)), }; + /// + /// Get the item id from the item. + /// + /// items to get id + /// item id list + /// public static List GetItemGuids(IEnumerable itemBases) => itemBases.Select( i => { @@ -469,6 +538,13 @@ public static List GetItemGuids(IEnumerable itemBases) => itemBa _ => throw new ArgumentOutOfRangeException(nameof(grade), grade, null), }; + /// + /// Get the target grade of the item. + /// max grade is Divinity + /// + /// grade id of the item + /// target grade id + /// public static int GetTargetGrade(int gradeId) { return gradeId switch From 0038e136ae2996fe81bec5fda79730a22247f94f Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Wed, 4 Dec 2024 17:07:13 +0900 Subject: [PATCH 063/136] add summary to synthesize --- Lib9c/TableData/Item/SynthesizeSheet.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Lib9c/TableData/Item/SynthesizeSheet.cs b/Lib9c/TableData/Item/SynthesizeSheet.cs index 77092ae29a..fb42d91988 100644 --- a/Lib9c/TableData/Item/SynthesizeSheet.cs +++ b/Lib9c/TableData/Item/SynthesizeSheet.cs @@ -13,6 +13,9 @@ namespace Nekoyume.TableData [Serializable] public class SynthesizeSheet : Sheet { + /// + /// synthesize data for each subtype + /// public struct SynthesizeData { public int RequiredCount; From 7d00a645d7029bcfe98f230edde838e83a86a5b7 Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Wed, 4 Dec 2024 18:34:45 +0900 Subject: [PATCH 064/136] apply recipe --- Lib9c/Action/AuraSummon.cs | 2 - Lib9c/Action/Synthesize.cs | 9 ++ Lib9c/Helper/SynthesizeSimulator.cs | 156 +++++++++++++++++++++++++--- 3 files changed, 153 insertions(+), 14 deletions(-) diff --git a/Lib9c/Action/AuraSummon.cs b/Lib9c/Action/AuraSummon.cs index 7326cfc2d6..fb0aa96087 100644 --- a/Lib9c/Action/AuraSummon.cs +++ b/Lib9c/Action/AuraSummon.cs @@ -134,7 +134,6 @@ long blockIndex ); AddAndUnlockOption( - agentState, equipment, random, subRecipeRow, @@ -262,7 +261,6 @@ public override IWorld Execute(IActionContext context) } public static void AddAndUnlockOption( - AgentState agentState, Equipment equipment, IRandom random, EquipmentItemSubRecipeSheetV2.Row subRecipe, diff --git a/Lib9c/Action/Synthesize.cs b/Lib9c/Action/Synthesize.cs index ae399c345c..d0035cf080 100644 --- a/Lib9c/Action/Synthesize.cs +++ b/Lib9c/Action/Synthesize.cs @@ -77,6 +77,10 @@ public override IWorld Execute(IActionContext context) typeof(SynthesizeSheet), typeof(SynthesizeWeightSheet), typeof(MaterialItemSheet), + typeof(EquipmentItemRecipeSheet), + typeof(EquipmentItemSubRecipeSheetV2), + typeof(EquipmentItemOptionSheet), + typeof(SkillSheet), }); // Calculate action point @@ -113,6 +117,11 @@ public override IWorld Execute(IActionContext context) SynthesizeWeightSheet = sheets.GetSheet(), CostumeItemSheet = sheets.GetSheet(), EquipmentItemSheet = sheets.GetSheet(), + EquipmentItemRecipeSheet = sheets.GetSheet(), + EquipmentItemSubRecipeSheetV2 = sheets.GetSheet(), + EquipmentItemOptionSheet = sheets.GetSheet(), + SkillSheet = sheets.GetSheet(), + BlockIndex = context.BlockIndex, RandomObject = context.GetRandom(), GradeDict = gradeDict, }); diff --git a/Lib9c/Helper/SynthesizeSimulator.cs b/Lib9c/Helper/SynthesizeSimulator.cs index 6626fbede9..8c03c6a684 100644 --- a/Lib9c/Helper/SynthesizeSimulator.cs +++ b/Lib9c/Helper/SynthesizeSimulator.cs @@ -21,6 +21,10 @@ public struct SynthesizeResult { public ItemBase ItemBase; public bool IsSuccess; + public int RecipeId; + public int SubRecipeId; + + public bool IsEquipment => ItemBase.ItemType == ItemType.Equipment; } /// @@ -52,10 +56,21 @@ public struct InputData public SynthesizeWeightSheet SynthesizeWeightSheet; public CostumeItemSheet CostumeItemSheet; public EquipmentItemSheet EquipmentItemSheet; + public EquipmentItemRecipeSheet EquipmentItemRecipeSheet; + public EquipmentItemSubRecipeSheetV2 EquipmentItemSubRecipeSheetV2; + public EquipmentItemOptionSheet EquipmentItemOptionSheet; + public SkillSheet SkillSheet; + public long BlockIndex; public IRandom RandomObject; public GradeDict GradeDict; } + public struct EquipmentData + { + public int RecipeId; + public int SubRecipeId; + } + /// /// Simulate the synthesis of items. /// @@ -111,11 +126,21 @@ public static List Simulate(InputData inputData) var isSuccess = random.Next(SynthesizeSheet.SucceedRateMax) < succeedRate; var grade = (Grade)gradeId; - var outputGradeId = isSuccess ? GetTargetGrade(grade) : grade; - // Decide the item to add to inventory based on SynthesizeWeightSheet - var synthesizedItem = GetSynthesizedItem(outputGradeId, inputData.SynthesizeWeightSheet, inputData.CostumeItemSheet, - inputData.EquipmentItemSheet, random, itemSubType); + var synthesizedItem = GetSynthesizedItem( + grade, + isSuccess, + inputData.SynthesizeWeightSheet, + inputData.CostumeItemSheet, + inputData.EquipmentItemSheet, + inputData.EquipmentItemRecipeSheet, + inputData.EquipmentItemSubRecipeSheetV2, + inputData.EquipmentItemOptionSheet, + inputData.SkillSheet, + inputData.BlockIndex, + random, + itemSubType, + out var equipmentData); if (isSuccess && grade == (Grade)synthesizedItem.Grade) { @@ -127,6 +152,8 @@ public static List Simulate(InputData inputData) { ItemBase = synthesizedItem, IsSuccess = isSuccess, + RecipeId = equipmentData.RecipeId, + SubRecipeId = equipmentData.SubRecipeId, }); } } @@ -283,16 +310,42 @@ private static void SetGradeDict(ref GradeDict gradeDict, int grade, ItemSubType return costumeItem; } - private static ItemBase GetSynthesizedItem(Grade grade, SynthesizeWeightSheet weightSheet, CostumeItemSheet costumeItemSheet, EquipmentItemSheet equipmentItemSheet, IRandom random, ItemSubType itemSubTypeValue) + private static ItemBase GetSynthesizedItem( + Grade grade, + bool isSuccess, + SynthesizeWeightSheet weightSheet, + CostumeItemSheet costumeItemSheet, + EquipmentItemSheet equipmentItemSheet, + EquipmentItemRecipeSheet equipmentItemRecipeSheet, + EquipmentItemSubRecipeSheetV2 equipmentItemSubRecipeSheetV2, + EquipmentItemOptionSheet equipmentItemOptionSheet, + SkillSheet skillSheet, + long blockIndex, + IRandom random, + ItemSubType itemSubTypeValue, + out EquipmentData equipmentData) { + equipmentData = new EquipmentData(); switch (itemSubTypeValue) { case ItemSubType.FullCostume: case ItemSubType.Title: - return GetRandomCostume(grade, itemSubTypeValue, weightSheet, costumeItemSheet, random); + return GetRandomCostume(grade, isSuccess, itemSubTypeValue, weightSheet, costumeItemSheet, random); case ItemSubType.Aura: case ItemSubType.Grimoire: - return GetRandomEquipment(grade, itemSubTypeValue, weightSheet, equipmentItemSheet, random); + return GetRandomEquipment( + grade, + isSuccess, + itemSubTypeValue, + weightSheet, + equipmentItemSheet, + equipmentItemRecipeSheet, + equipmentItemSubRecipeSheetV2, + equipmentItemOptionSheet, + skillSheet, + blockIndex, + random, + ref equipmentData); default: throw new ArgumentOutOfRangeException(); } @@ -300,9 +353,21 @@ private static ItemBase GetSynthesizedItem(Grade grade, SynthesizeWeightSheet we #region GetRandomItem - private static ItemBase GetRandomCostume(Grade grade, ItemSubType itemSubType, SynthesizeWeightSheet weightSheet, CostumeItemSheet costumeItemSheet, IRandom random) + private static ItemBase GetRandomCostume(Grade grade, bool isSuccess, ItemSubType itemSubType, SynthesizeWeightSheet weightSheet, CostumeItemSheet costumeItemSheet, IRandom random) { - var synthesizeResultPool = GetSynthesizeResultPool(grade, itemSubType, costumeItemSheet); + HashSet? synthesizeResultPool = null; + if (isSuccess) + { + synthesizeResultPool = GetSynthesizeResultPool(GetTargetGrade(grade), itemSubType, costumeItemSheet); + if (synthesizeResultPool.Count == 0) + { + synthesizeResultPool = GetSynthesizeResultPool(grade, itemSubType, costumeItemSheet); + } + } + else + { + synthesizeResultPool = GetSynthesizeResultPool(grade, itemSubType, costumeItemSheet); + } if (synthesizeResultPool.Count == 0) { @@ -332,9 +397,33 @@ private static ItemBase GetRandomCostume(Grade grade, ItemSubType itemSubType, S throw new InvalidOperationException("Failed to select a synthesized item."); } - private static ItemBase GetRandomEquipment(Grade grade, ItemSubType itemSubType, SynthesizeWeightSheet weightSheet, EquipmentItemSheet equipmentItemSheet, IRandom random) + private static ItemBase GetRandomEquipment( + Grade grade, + bool isSuccess, + ItemSubType itemSubType, + SynthesizeWeightSheet weightSheet, + EquipmentItemSheet equipmentItemSheet, + EquipmentItemRecipeSheet equipmentItemRecipeSheet, + EquipmentItemSubRecipeSheetV2 equipmentItemSubRecipeSheetV2, + EquipmentItemOptionSheet equipmentItemOptionSheet, + SkillSheet skillSheet, + long blockIndex, + IRandom random, + ref EquipmentData equipmentData) { - var synthesizeResultPool = GetSynthesizeResultPool(grade, itemSubType, equipmentItemSheet); + HashSet? synthesizeResultPool = null; + if (isSuccess) + { + synthesizeResultPool = GetSynthesizeResultPool(GetTargetGrade(grade), itemSubType, equipmentItemSheet); + if (synthesizeResultPool.Count == 0) + { + synthesizeResultPool = GetSynthesizeResultPool(grade, itemSubType, equipmentItemSheet); + } + } + else + { + synthesizeResultPool = GetSynthesizeResultPool(grade, itemSubType, equipmentItemSheet); + } if (synthesizeResultPool.Count == 0) { @@ -357,7 +446,50 @@ private static ItemBase GetRandomEquipment(Grade grade, ItemSubType itemSubType, $"Aborted as the equipment row ({itemId}) was failed to load in {nameof(EquipmentItemSheet)}", itemId ); } - return ItemFactory.CreateItem(equipmentRow, random); + + // find first recipe (see AuraSummon.SimulateSummon) + var recipeRow = equipmentItemRecipeSheet.Values.FirstOrDefault(r => r.ResultEquipmentId == itemId); + if (recipeRow == null) + { + throw new SheetRowNotFoundException( + $"Aborted as the recipe row for equipment ({itemId}) was failed to load in {nameof(EquipmentItemRecipeSheet)}", itemId + ); + } + equipmentData.RecipeId = recipeRow.Id; + + // Validate subRecipeId + if (recipeRow.SubRecipeIds.Count == 0) + { + throw new InvalidRecipeIdException( + $"Recipe of item id({itemId}) does not have any subRecipe."); + } + + // find first sub recipe (see AuraSummon.SimulateSummon) + var subRecipeId = recipeRow.SubRecipeIds[0]; + if (!equipmentItemSubRecipeSheetV2.TryGetValue(subRecipeId, out var subRecipeRow)) + { + throw new SheetRowNotFoundException( + nameof(EquipmentItemSubRecipeSheetV2), + subRecipeId + ); + } + equipmentData.SubRecipeId = subRecipeId; + + var equipment = (Equipment)ItemFactory.CreateItemUsable( + equipmentRow, + random.GenerateRandomGuid(), + blockIndex + ); + + AuraSummon.AddAndUnlockOption( + equipment, + random, + subRecipeRow, + equipmentItemOptionSheet, + skillSheet + ); + + return equipment; } // Should not reach here From 9d3c4794639e11a377202c230e58a3a332d2a4dc Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Wed, 4 Dec 2024 18:35:52 +0900 Subject: [PATCH 065/136] add multi test --- .Lib9c.Tests/Action/SynthesizeTest.cs | 123 +++++++++++++++++++++++++- 1 file changed, 120 insertions(+), 3 deletions(-) diff --git a/.Lib9c.Tests/Action/SynthesizeTest.cs b/.Lib9c.Tests/Action/SynthesizeTest.cs index cc8347557c..fb16315685 100644 --- a/.Lib9c.Tests/Action/SynthesizeTest.cs +++ b/.Lib9c.Tests/Action/SynthesizeTest.cs @@ -89,6 +89,7 @@ public void ExecuteSingle(Grade grade, ItemSubType itemSubType) (state, var items) = UpdateItemsFromSubType(grade, itemSubTypes, state, avatarAddress); state = state.SetActionPoint(avatarAddress, 120); + var previousAvatarState = state.GetAvatarState(avatarAddress); var action = new Synthesize() { AvatarAddress = avatarAddress, @@ -138,9 +139,125 @@ public void ExecuteSingle(Grade grade, ItemSubType itemSubType) break; } - // TODO: if success, grade should be exceptedGrade, but sometimes it is not. - // Assert.Equal(expectedGrade, resultGrade); - Assert.True(expectedGrade == resultGrade || resultGrade == grade); + var gradeDict = SynthesizeSimulator.GetGradeDict( + action.MaterialIds, + previousAvatarState, + blockIndex, + avatarAddress.ToHex(), + out _, + out _ + ); + + var inputData = new SynthesizeSimulator.InputData() + { + SynthesizeSheet = TableSheets.SynthesizeSheet, + SynthesizeWeightSheet = TableSheets.SynthesizeWeightSheet, + CostumeItemSheet = TableSheets.CostumeItemSheet, + EquipmentItemSheet = TableSheets.EquipmentItemSheet, + EquipmentItemRecipeSheet = TableSheets.EquipmentItemRecipeSheet, + EquipmentItemSubRecipeSheetV2 = TableSheets.EquipmentItemSubRecipeSheetV2, + EquipmentItemOptionSheet = TableSheets.EquipmentItemOptionSheet, + SkillSheet = TableSheets.SkillSheet, + RandomObject = new TestRandom(), + GradeDict = gradeDict, + }; + + var result = SynthesizeSimulator.Simulate(inputData)[0]; + if (result.IsSuccess) + { + Assert.Equal(expectedGrade, resultGrade); + } + else + { + Assert.Equal(resultGrade, grade); + } + } + + [Theory] + [InlineData((Grade)3, ItemSubType.FullCostume)] + [InlineData((Grade)4, ItemSubType.FullCostume)] + [InlineData((Grade)5, ItemSubType.FullCostume)] + [InlineData((Grade)3, ItemSubType.Title)] + [InlineData((Grade)4, ItemSubType.Title)] + [InlineData((Grade)5, ItemSubType.Title)] + [InlineData((Grade)3, ItemSubType.Grimoire)] + [InlineData((Grade)4, ItemSubType.Grimoire)] + [InlineData((Grade)5, ItemSubType.Grimoire)] + [InlineData((Grade)6, ItemSubType.Grimoire)] + [InlineData((Grade)1, ItemSubType.Aura)] + [InlineData((Grade)2, ItemSubType.Aura)] + [InlineData((Grade)3, ItemSubType.Aura)] + [InlineData((Grade)4, ItemSubType.Aura)] + [InlineData((Grade)5, ItemSubType.Aura)] + [InlineData((Grade)6, ItemSubType.Aura)] + public void ExecuteMultiple(Grade grade, ItemSubType itemSubType) + { + var testCount = 100; + var itemSubTypes = GetSubTypeArray(itemSubType, testCount * GetSucceededMaterialCount(itemSubType, grade)); + + var state = Init(out var agentAddress, out var avatarAddress, out var blockIndex); + (state, var items) = UpdateItemsFromSubType(grade, itemSubTypes, state, avatarAddress); + state = state.SetActionPoint(avatarAddress, 120); + + var previousAvatarState = state.GetAvatarState(avatarAddress); + var action = new Synthesize() + { + AvatarAddress = avatarAddress, + MaterialIds = SynthesizeSimulator.GetItemGuids(items), + }; + + var ctx = new ActionContext + { + BlockIndex = blockIndex, + PreviousState = state, + RandomSeed = 0, + Signer = agentAddress, + }; + + state = action.Execute(ctx); + + var gradeDict = SynthesizeSimulator.GetGradeDict( + action.MaterialIds, + previousAvatarState, + blockIndex, + avatarAddress.ToHex(), + out _, + out _ + ); + + var inputData = new SynthesizeSimulator.InputData() + { + SynthesizeSheet = TableSheets.SynthesizeSheet, + SynthesizeWeightSheet = TableSheets.SynthesizeWeightSheet, + CostumeItemSheet = TableSheets.CostumeItemSheet, + EquipmentItemSheet = TableSheets.EquipmentItemSheet, + EquipmentItemRecipeSheet = TableSheets.EquipmentItemRecipeSheet, + EquipmentItemSubRecipeSheetV2 = TableSheets.EquipmentItemSubRecipeSheetV2, + EquipmentItemOptionSheet = TableSheets.EquipmentItemOptionSheet, + SkillSheet = TableSheets.SkillSheet, + RandomObject = new TestRandom(), + GradeDict = gradeDict, + }; + + var resultList = SynthesizeSimulator.Simulate(inputData); + foreach (var result in resultList) + { + // Check Grade + if (result.IsSuccess) + { + Assert.Equal((int)grade + 1, result.ItemBase.Grade); + } + else + { + Assert.Equal((int)grade, result.ItemBase.Grade); + } + + if (result.IsEquipment) + { + Assert.True(result.RecipeId != 0); + Assert.True(result.SubRecipeId != 0); + } + } } [Fact] From 048dfc3fb554d91fcefe8f9138b7450645fb0ca9 Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Wed, 4 Dec 2024 19:40:53 +0900 Subject: [PATCH 066/136] add comments --- .Lib9c.Tests/Action/SynthesizeTest.cs | 39 +++++++++++++++++++-- Lib9c/Helper/SynthesizeSimulator.cs | 50 ++++++++++++++++++++++++++- 2 files changed, 85 insertions(+), 4 deletions(-) diff --git a/.Lib9c.Tests/Action/SynthesizeTest.cs b/.Lib9c.Tests/Action/SynthesizeTest.cs index fb16315685..20f8872269 100644 --- a/.Lib9c.Tests/Action/SynthesizeTest.cs +++ b/.Lib9c.Tests/Action/SynthesizeTest.cs @@ -33,6 +33,14 @@ public class SynthesizeTest private readonly Currency _goldCurrency = Currency.Legacy("NCG", 2, null); #pragma warning restore CS0618 + /// + /// Initializes the game state for testing. + /// Sets up an initial agent, avatar, and world state with relevant table sheets. + /// + /// The address of the agent to be created. + /// The address of the avatar to be created. + /// The initial block index. + /// The initialized world state. public IWorld Init(out Address agentAddress, out Address avatarAddress, out long blockIndex) { agentAddress = new PrivateKey().Address; @@ -64,6 +72,12 @@ public IWorld Init(out Address agentAddress, out Address avatarAddress, out long return state.SetLegacyState(gameConfigState.address, gameConfigState.Serialize()); } + /// + /// Tests the synthesis process for a single item. + /// Verifies that the resulting item matches the expected grade and type. + /// + /// The grade of the material used in synthesis. + /// The subtype of the item to synthesize. [Theory] [InlineData((Grade)3, ItemSubType.FullCostume)] [InlineData((Grade)4, ItemSubType.FullCostume)] @@ -173,6 +187,13 @@ out _ } } + /// + /// Tests the synthesis process for multiple items. + /// Verifies that the resulting items match the expected grades and types. + /// The test case also checks whether the equipment has a recipe. + /// + /// The grade of the material used in synthesis. + /// The subtype of the items to synthesize. [Theory] [InlineData((Grade)3, ItemSubType.FullCostume)] [InlineData((Grade)4, ItemSubType.FullCostume)] @@ -260,6 +281,10 @@ out _ } } + /// + /// Tests the synthesis action when there are not enough action points. + /// Verifies that the action throws a NotEnoughActionPointException. + /// [Fact] public void ExecuteNotEnoughActionPoint() { @@ -287,6 +312,11 @@ public void ExecuteNotEnoughActionPoint() Assert.Throws(() => action.Execute(ctx)); } + /// + /// Tests the synthesis of multiple items of the same type. + /// Verifies that the resulting items are of the correct type and grade. + /// + /// The number of items to synthesize. [Theory] [InlineData(2)] [InlineData(3)] @@ -329,9 +359,12 @@ public void ExecuteMultipleSameType(int testCount) } } - // TODO: Add Simulator for test and client - // TODO: Exception Tests - // TODO: ExecuteMultiple + /// + /// Tests the synthesis action with invalid material combinations. + /// Verifies that the action throws an InvalidMaterialException. + /// + /// The grade of the material used in synthesis. + /// An array of invalid item subtypes to use in synthesis. [Theory] [InlineData((Grade)3, new[] { ItemSubType.Aura, ItemSubType.FullCostume, ItemSubType.FullCostume })] [InlineData((Grade)3, new[] { ItemSubType.Title, ItemSubType.Grimoire, ItemSubType.Title })] diff --git a/Lib9c/Helper/SynthesizeSimulator.cs b/Lib9c/Helper/SynthesizeSimulator.cs index 8c03c6a684..436a6560c2 100644 --- a/Lib9c/Helper/SynthesizeSimulator.cs +++ b/Lib9c/Helper/SynthesizeSimulator.cs @@ -52,22 +52,70 @@ public static class SynthesizeSimulator /// public struct InputData { + /// + /// The sheet that contains the synthesis information. + /// + /// public SynthesizeSheet SynthesizeSheet; + /// + /// The sheet that contains the synthesis weight information. + /// + /// public SynthesizeWeightSheet SynthesizeWeightSheet; + /// + /// The sheet that contains the costume item information. + /// + /// public CostumeItemSheet CostumeItemSheet; + /// + /// The sheet that contains the equipment item information. + /// + /// public EquipmentItemSheet EquipmentItemSheet; + /// + /// The sheet that contains the equipment item recipe information. + /// + /// public EquipmentItemRecipeSheet EquipmentItemRecipeSheet; + /// + /// The sheet that contains the equipment item sub recipe information. + /// + /// public EquipmentItemSubRecipeSheetV2 EquipmentItemSubRecipeSheetV2; + /// + /// The sheet that contains the equipment item option information. + /// + /// public EquipmentItemOptionSheet EquipmentItemOptionSheet; + /// + /// The sheet that contains the skill information. + /// + /// public SkillSheet SkillSheet; + /// + /// The block index when action is executed. + /// public long BlockIndex; + /// + /// The random object to use. + /// Caution: Must have the same seed as when the action is executed + /// public IRandom RandomObject; + /// + /// The grade dictionary of the material items. + /// public GradeDict GradeDict; } - public struct EquipmentData + private struct EquipmentData { + /// + /// RecipeId of the equipment + /// public int RecipeId; + /// + /// SubRecipeId of the equipment + /// public int SubRecipeId; } From 575db75395859e5ee18aacfa4e28d9163bcfeeb5 Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Thu, 5 Dec 2024 22:26:20 +0900 Subject: [PATCH 067/136] fix GetSynthesizeResultPool --- Lib9c/Helper/SynthesizeSimulator.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Lib9c/Helper/SynthesizeSimulator.cs b/Lib9c/Helper/SynthesizeSimulator.cs index 436a6560c2..e758343b04 100644 --- a/Lib9c/Helper/SynthesizeSimulator.cs +++ b/Lib9c/Helper/SynthesizeSimulator.cs @@ -603,14 +603,14 @@ public static HashSet GetSynthesizeResultPool(Grade sourceGrade, ItemSubTyp /// excepted FullCostume,Title /// CostumeItemSheet to use /// list of items key(int) - public static List GetSynthesizeResultPool(List sourceGrades, ItemSubType subType, CostumeItemSheet sheet) + public static HashSet GetSynthesizeResultPool(HashSet sourceGrades, ItemSubType subType, CostumeItemSheet sheet) { return sheet .Values .Where(r => r.ItemSubType == subType) - .Where(r => sourceGrades.Any(grade => (Grade)r.Grade == GetUpgradeGrade(grade, subType, sheet))) + .Where(r => sourceGrades.Any(grade => (Grade)r.Grade == grade)) .Select(r => r.Id) - .ToList(); + .ToHashSet(); } /// @@ -620,14 +620,14 @@ public static List GetSynthesizeResultPool(List sourceGrades, ItemSu /// excepted Grimoire,Aura /// EquipmentItemSheet to use /// list of items key(int) - public static List GetSynthesizeResultPool(List sourceGrades, ItemSubType subType, EquipmentItemSheet sheet) + public static HashSet GetSynthesizeResultPool(HashSet sourceGrades, ItemSubType subType, EquipmentItemSheet sheet) { return sheet .Values .Where(r => r.ItemSubType == subType) - .Where(r => sourceGrades.Any(grade => (Grade)r.Grade == GetUpgradeGrade(grade, subType, sheet))) + .Where(r => sourceGrades.Any(grade => (Grade)r.Grade == grade)) .Select(r => r.Id) - .ToList(); + .ToHashSet(); } /// @@ -707,7 +707,7 @@ public static List GetItemGuids(IEnumerable itemBases) => itemBa }; }).ToList(); - private static Grade GetTargetGrade(Grade grade) => grade switch + public static Grade GetTargetGrade(Grade grade) => grade switch { Grade.Normal => Grade.Rare, Grade.Rare => Grade.Epic, From 87618f4667a0fa63673385af78b6bf69f7cd0a29 Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Thu, 5 Dec 2024 22:30:10 +0900 Subject: [PATCH 068/136] add summary --- Lib9c/Helper/SynthesizeSimulator.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Lib9c/Helper/SynthesizeSimulator.cs b/Lib9c/Helper/SynthesizeSimulator.cs index e758343b04..83b5436a3b 100644 --- a/Lib9c/Helper/SynthesizeSimulator.cs +++ b/Lib9c/Helper/SynthesizeSimulator.cs @@ -707,6 +707,13 @@ public static List GetItemGuids(IEnumerable itemBases) => itemBa }; }).ToList(); + /// + /// Get the target grade of the item. + /// max grade is Divinity + /// + /// grade of the item + /// target grade + /// public static Grade GetTargetGrade(Grade grade) => grade switch { Grade.Normal => Grade.Rare, From af2aada06334edd518c967d8680b114aa7764c38 Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Thu, 5 Dec 2024 22:40:47 +0900 Subject: [PATCH 069/136] return grade in GetSynthesizeResultPool --- Lib9c/Helper/SynthesizeSimulator.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Lib9c/Helper/SynthesizeSimulator.cs b/Lib9c/Helper/SynthesizeSimulator.cs index 83b5436a3b..1157f49718 100644 --- a/Lib9c/Helper/SynthesizeSimulator.cs +++ b/Lib9c/Helper/SynthesizeSimulator.cs @@ -602,14 +602,14 @@ public static HashSet GetSynthesizeResultPool(Grade sourceGrade, ItemSubTyp /// grades of material items /// excepted FullCostume,Title /// CostumeItemSheet to use - /// list of items key(int) - public static HashSet GetSynthesizeResultPool(HashSet sourceGrades, ItemSubType subType, CostumeItemSheet sheet) + /// list of items key(int), grade(Grade) tuple + public static HashSet<(int, Grade)> GetSynthesizeResultPool(HashSet sourceGrades, ItemSubType subType, CostumeItemSheet sheet) { return sheet .Values .Where(r => r.ItemSubType == subType) .Where(r => sourceGrades.Any(grade => (Grade)r.Grade == grade)) - .Select(r => r.Id) + .Select(r => (r.Id, (Grade)r.Grade)) .ToHashSet(); } @@ -619,14 +619,14 @@ public static HashSet GetSynthesizeResultPool(HashSet sourceGrades, /// grades of material items /// excepted Grimoire,Aura /// EquipmentItemSheet to use - /// list of items key(int) - public static HashSet GetSynthesizeResultPool(HashSet sourceGrades, ItemSubType subType, EquipmentItemSheet sheet) + /// list of items key(int), grade(Grade) tuple + public static HashSet<(int, Grade)> GetSynthesizeResultPool(HashSet sourceGrades, ItemSubType subType, EquipmentItemSheet sheet) { return sheet .Values .Where(r => r.ItemSubType == subType) .Where(r => sourceGrades.Any(grade => (Grade)r.Grade == grade)) - .Select(r => r.Id) + .Select(r => (r.Id, (Grade)r.Grade)) .ToHashSet(); } From 086353b7d4ba25aea14a4f700f433178f710405d Mon Sep 17 00:00:00 2001 From: moreal Date: Fri, 6 Dec 2024 13:36:44 +0900 Subject: [PATCH 070/136] feat(js): allow to use shared dependency --- .../@planetarium/lib9c/package.json | 8 +++--- .../javascript/@planetarium/pnpm-lock.yaml | 25 ++++++++++--------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/integrations/javascript/@planetarium/lib9c/package.json b/integrations/javascript/@planetarium/lib9c/package.json index be64363a9c..2f7258acd8 100644 --- a/integrations/javascript/@planetarium/lib9c/package.json +++ b/integrations/javascript/@planetarium/lib9c/package.json @@ -28,11 +28,13 @@ "vitest": "^1.5.2" }, "dependencies": { - "@planetarium/account": "^4.4.1", "@planetarium/bencodex": "^0.2.2", - "@planetarium/tx": "^4.4.1", "buffer": "^6.0.3", "decimal.js": "^10.4.3", "uuid": "^9.0.1" + }, + "peerDependencies": { + "@planetarium/account": "5.x", + "@planetarium/tx": "5.x" } -} \ No newline at end of file +} diff --git a/integrations/javascript/@planetarium/pnpm-lock.yaml b/integrations/javascript/@planetarium/pnpm-lock.yaml index 2adfd78e57..e0da2c67f3 100644 --- a/integrations/javascript/@planetarium/pnpm-lock.yaml +++ b/integrations/javascript/@planetarium/pnpm-lock.yaml @@ -9,14 +9,14 @@ importers: lib9c: dependencies: '@planetarium/account': - specifier: ^4.4.1 - version: 4.4.1 + specifier: 5.x + version: 5.3.2 '@planetarium/bencodex': specifier: ^0.2.2 version: 0.2.2 '@planetarium/tx': - specifier: ^4.4.1 - version: 4.4.1(@planetarium/account@4.4.1) + specifier: 5.x + version: 5.3.2(@planetarium/account@5.3.2) buffer: specifier: ^6.0.3 version: 6.0.3 @@ -540,17 +540,17 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@planetarium/account@4.4.1': - resolution: {integrity: sha512-aCPOiv2//axVtXmsRy7lXqcWj/bq01+DH3CQSjPisil/bg639hSzuCaTwJf/X1pTKxkItOEHW66yyAP83uyARA==} + '@planetarium/account@5.3.2': + resolution: {integrity: sha512-Yx+FpOkzFtuOO3NqMeC+4rtncqtel12uP/b4xr4wNL7HWhYt1Syg8UDh+Ibz9MWuGyoP6lA35D2PCBg0Ar1ykA==} '@planetarium/bencodex@0.2.2': resolution: {integrity: sha512-hXYmV0gzEUxbhpVDiMIe/b0XAUoXBIl8Fo84Fx8nE0U39sU5q7tCFTkceock8TYs5rS4ix793033b7QYAPVoLA==} - '@planetarium/tx@4.4.1': - resolution: {integrity: sha512-SZayh/xU57DxbaYKdpBrevwV43RPumhDtcvr9jV6LZMk00msCyo8d2PUPxA2YkwekMJnj9gWsTyd8n/JR29fug==} + '@planetarium/tx@5.3.2': + resolution: {integrity: sha512-jlxyNhS1kXElR+biWNEAzAPULk6UlyUEB0l1EuS4Xm0N2gXAuPCTV5ERvaD/+DWv+kivBW20tfBmB9N/Jdqitg==} engines: {node: '>=19.0.0'} peerDependencies: - '@planetarium/account': ^4.4.1 + '@planetarium/account': ^5.3.2 '@rollup/rollup-android-arm-eabi@4.16.4': resolution: {integrity: sha512-GkhjAaQ8oUTOKE4g4gsZ0u8K/IHU1+2WQSgS1TwTcYvL+sjbaQjNHFXbOJ6kgqGHIO1DfUhI/Sphi9GkRT9K+Q==} @@ -1933,16 +1933,17 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true - '@planetarium/account@4.4.1': + '@planetarium/account@5.3.2': dependencies: '@noble/hashes': 1.4.0 '@noble/secp256k1': 1.7.1 + buffer: 6.0.3 '@planetarium/bencodex@0.2.2': {} - '@planetarium/tx@4.4.1(@planetarium/account@4.4.1)': + '@planetarium/tx@5.3.2(@planetarium/account@5.3.2)': dependencies: - '@planetarium/account': 4.4.1 + '@planetarium/account': 5.3.2 '@planetarium/bencodex': 0.2.2 '@rollup/rollup-android-arm-eabi@4.16.4': From 1b78189316f23871c594cabdeba42d0ac0c762e8 Mon Sep 17 00:00:00 2001 From: moreal Date: Fri, 6 Dec 2024 14:00:09 +0900 Subject: [PATCH 071/136] Revert "Merge pull request #3071 from moreal/feat/js/peer-dependencies" This reverts commit 6874cf62c012f5367e21d26c283750e7e2561a68, reversing changes made to f674f0ff075a73c518bbd4b3d16f21f2d9a64ccc. --- .../@planetarium/lib9c/package.json | 8 +++--- .../javascript/@planetarium/pnpm-lock.yaml | 25 +++++++++---------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/integrations/javascript/@planetarium/lib9c/package.json b/integrations/javascript/@planetarium/lib9c/package.json index 2f7258acd8..be64363a9c 100644 --- a/integrations/javascript/@planetarium/lib9c/package.json +++ b/integrations/javascript/@planetarium/lib9c/package.json @@ -28,13 +28,11 @@ "vitest": "^1.5.2" }, "dependencies": { + "@planetarium/account": "^4.4.1", "@planetarium/bencodex": "^0.2.2", + "@planetarium/tx": "^4.4.1", "buffer": "^6.0.3", "decimal.js": "^10.4.3", "uuid": "^9.0.1" - }, - "peerDependencies": { - "@planetarium/account": "5.x", - "@planetarium/tx": "5.x" } -} +} \ No newline at end of file diff --git a/integrations/javascript/@planetarium/pnpm-lock.yaml b/integrations/javascript/@planetarium/pnpm-lock.yaml index e0da2c67f3..2adfd78e57 100644 --- a/integrations/javascript/@planetarium/pnpm-lock.yaml +++ b/integrations/javascript/@planetarium/pnpm-lock.yaml @@ -9,14 +9,14 @@ importers: lib9c: dependencies: '@planetarium/account': - specifier: 5.x - version: 5.3.2 + specifier: ^4.4.1 + version: 4.4.1 '@planetarium/bencodex': specifier: ^0.2.2 version: 0.2.2 '@planetarium/tx': - specifier: 5.x - version: 5.3.2(@planetarium/account@5.3.2) + specifier: ^4.4.1 + version: 4.4.1(@planetarium/account@4.4.1) buffer: specifier: ^6.0.3 version: 6.0.3 @@ -540,17 +540,17 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@planetarium/account@5.3.2': - resolution: {integrity: sha512-Yx+FpOkzFtuOO3NqMeC+4rtncqtel12uP/b4xr4wNL7HWhYt1Syg8UDh+Ibz9MWuGyoP6lA35D2PCBg0Ar1ykA==} + '@planetarium/account@4.4.1': + resolution: {integrity: sha512-aCPOiv2//axVtXmsRy7lXqcWj/bq01+DH3CQSjPisil/bg639hSzuCaTwJf/X1pTKxkItOEHW66yyAP83uyARA==} '@planetarium/bencodex@0.2.2': resolution: {integrity: sha512-hXYmV0gzEUxbhpVDiMIe/b0XAUoXBIl8Fo84Fx8nE0U39sU5q7tCFTkceock8TYs5rS4ix793033b7QYAPVoLA==} - '@planetarium/tx@5.3.2': - resolution: {integrity: sha512-jlxyNhS1kXElR+biWNEAzAPULk6UlyUEB0l1EuS4Xm0N2gXAuPCTV5ERvaD/+DWv+kivBW20tfBmB9N/Jdqitg==} + '@planetarium/tx@4.4.1': + resolution: {integrity: sha512-SZayh/xU57DxbaYKdpBrevwV43RPumhDtcvr9jV6LZMk00msCyo8d2PUPxA2YkwekMJnj9gWsTyd8n/JR29fug==} engines: {node: '>=19.0.0'} peerDependencies: - '@planetarium/account': ^5.3.2 + '@planetarium/account': ^4.4.1 '@rollup/rollup-android-arm-eabi@4.16.4': resolution: {integrity: sha512-GkhjAaQ8oUTOKE4g4gsZ0u8K/IHU1+2WQSgS1TwTcYvL+sjbaQjNHFXbOJ6kgqGHIO1DfUhI/Sphi9GkRT9K+Q==} @@ -1933,17 +1933,16 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true - '@planetarium/account@5.3.2': + '@planetarium/account@4.4.1': dependencies: '@noble/hashes': 1.4.0 '@noble/secp256k1': 1.7.1 - buffer: 6.0.3 '@planetarium/bencodex@0.2.2': {} - '@planetarium/tx@5.3.2(@planetarium/account@5.3.2)': + '@planetarium/tx@4.4.1(@planetarium/account@4.4.1)': dependencies: - '@planetarium/account': 5.3.2 + '@planetarium/account': 4.4.1 '@planetarium/bencodex': 0.2.2 '@rollup/rollup-android-arm-eabi@4.16.4': From e0b90e4e671ae71409539c596d8ad3221c961721 Mon Sep 17 00:00:00 2001 From: moreal Date: Fri, 6 Dec 2024 14:03:38 +0900 Subject: [PATCH 072/136] feat(js): prepare 0.4.0 --- integrations/javascript/@planetarium/lib9c/jsr.json | 2 +- integrations/javascript/@planetarium/lib9c/package.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/integrations/javascript/@planetarium/lib9c/jsr.json b/integrations/javascript/@planetarium/lib9c/jsr.json index 53906b81db..f8597242df 100644 --- a/integrations/javascript/@planetarium/lib9c/jsr.json +++ b/integrations/javascript/@planetarium/lib9c/jsr.json @@ -1,7 +1,7 @@ { "$schema": "https://jsr.io/schema/config-file.v1.json", "name": "@planetarium/lib9c", - "version": "0.3.0", + "version": "0.4.0", "exports": "./src/index.ts", "publish": { "include": [ diff --git a/integrations/javascript/@planetarium/lib9c/package.json b/integrations/javascript/@planetarium/lib9c/package.json index be64363a9c..308ea50e94 100644 --- a/integrations/javascript/@planetarium/lib9c/package.json +++ b/integrations/javascript/@planetarium/lib9c/package.json @@ -1,6 +1,6 @@ { "name": "@planetarium/lib9c", - "version": "0.3.0", + "version": "0.4.0", "description": "", "module": "dist/index.js", "type": "module", @@ -35,4 +35,4 @@ "decimal.js": "^10.4.3", "uuid": "^9.0.1" } -} \ No newline at end of file +} From b7994a523f771591a9bc7f8dfa206c439a0353d4 Mon Sep 17 00:00:00 2001 From: moreal Date: Fri, 6 Dec 2024 15:21:39 +0900 Subject: [PATCH 073/136] feat: bump libplanet-related dependencies --- .../@planetarium/lib9c/package.json | 4 +-- .../javascript/@planetarium/pnpm-lock.yaml | 25 ++++++++++--------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/integrations/javascript/@planetarium/lib9c/package.json b/integrations/javascript/@planetarium/lib9c/package.json index 308ea50e94..548aa722d6 100644 --- a/integrations/javascript/@planetarium/lib9c/package.json +++ b/integrations/javascript/@planetarium/lib9c/package.json @@ -28,9 +28,9 @@ "vitest": "^1.5.2" }, "dependencies": { - "@planetarium/account": "^4.4.1", + "@planetarium/account": "^5.4.1", "@planetarium/bencodex": "^0.2.2", - "@planetarium/tx": "^4.4.1", + "@planetarium/tx": "^5.4.1", "buffer": "^6.0.3", "decimal.js": "^10.4.3", "uuid": "^9.0.1" diff --git a/integrations/javascript/@planetarium/pnpm-lock.yaml b/integrations/javascript/@planetarium/pnpm-lock.yaml index 2adfd78e57..d79b8e090d 100644 --- a/integrations/javascript/@planetarium/pnpm-lock.yaml +++ b/integrations/javascript/@planetarium/pnpm-lock.yaml @@ -9,14 +9,14 @@ importers: lib9c: dependencies: '@planetarium/account': - specifier: ^4.4.1 - version: 4.4.1 + specifier: ^5.4.1 + version: 5.4.1 '@planetarium/bencodex': specifier: ^0.2.2 version: 0.2.2 '@planetarium/tx': - specifier: ^4.4.1 - version: 4.4.1(@planetarium/account@4.4.1) + specifier: ^5.4.1 + version: 5.4.1(@planetarium/account@5.4.1) buffer: specifier: ^6.0.3 version: 6.0.3 @@ -540,17 +540,17 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@planetarium/account@4.4.1': - resolution: {integrity: sha512-aCPOiv2//axVtXmsRy7lXqcWj/bq01+DH3CQSjPisil/bg639hSzuCaTwJf/X1pTKxkItOEHW66yyAP83uyARA==} + '@planetarium/account@5.4.1': + resolution: {integrity: sha512-J6sOkL0nyawqqgHj5WtK1w90BDhVJIgddsbL89HCTsn+ybhNdqFM8O9pdx38FuF3REyGmIv2DvD/4MCcljDGvQ==} '@planetarium/bencodex@0.2.2': resolution: {integrity: sha512-hXYmV0gzEUxbhpVDiMIe/b0XAUoXBIl8Fo84Fx8nE0U39sU5q7tCFTkceock8TYs5rS4ix793033b7QYAPVoLA==} - '@planetarium/tx@4.4.1': - resolution: {integrity: sha512-SZayh/xU57DxbaYKdpBrevwV43RPumhDtcvr9jV6LZMk00msCyo8d2PUPxA2YkwekMJnj9gWsTyd8n/JR29fug==} + '@planetarium/tx@5.4.1': + resolution: {integrity: sha512-+OAHaScs/BVZUoQZsbQCWEKybEfSOKmhhaNAVj6Ko6RHE7pHJ+4s2dRmUOaffnbZy0FymHookciJcA3BThfevw==} engines: {node: '>=19.0.0'} peerDependencies: - '@planetarium/account': ^4.4.1 + '@planetarium/account': ^5.4.1 '@rollup/rollup-android-arm-eabi@4.16.4': resolution: {integrity: sha512-GkhjAaQ8oUTOKE4g4gsZ0u8K/IHU1+2WQSgS1TwTcYvL+sjbaQjNHFXbOJ6kgqGHIO1DfUhI/Sphi9GkRT9K+Q==} @@ -1933,16 +1933,17 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true - '@planetarium/account@4.4.1': + '@planetarium/account@5.4.1': dependencies: '@noble/hashes': 1.4.0 '@noble/secp256k1': 1.7.1 + buffer: 6.0.3 '@planetarium/bencodex@0.2.2': {} - '@planetarium/tx@4.4.1(@planetarium/account@4.4.1)': + '@planetarium/tx@5.4.1(@planetarium/account@5.4.1)': dependencies: - '@planetarium/account': 4.4.1 + '@planetarium/account': 5.4.1 '@planetarium/bencodex': 0.2.2 '@rollup/rollup-android-arm-eabi@4.16.4': From f95cdadb3ea9dc3bf0a203a68ab215307b70bfe1 Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Fri, 6 Dec 2024 15:33:08 +0900 Subject: [PATCH 074/136] remove grade dict --- .Lib9c.Tests/Action/SynthesizeTest.cs | 46 ++--- Lib9c/Action/Synthesize.cs | 61 +++++- Lib9c/Helper/SynthesizeSimulator.cs | 276 +++++++++++--------------- 3 files changed, 193 insertions(+), 190 deletions(-) diff --git a/.Lib9c.Tests/Action/SynthesizeTest.cs b/.Lib9c.Tests/Action/SynthesizeTest.cs index 20f8872269..cda7f63720 100644 --- a/.Lib9c.Tests/Action/SynthesizeTest.cs +++ b/.Lib9c.Tests/Action/SynthesizeTest.cs @@ -103,11 +103,13 @@ public void ExecuteSingle(Grade grade, ItemSubType itemSubType) (state, var items) = UpdateItemsFromSubType(grade, itemSubTypes, state, avatarAddress); state = state.SetActionPoint(avatarAddress, 120); - var previousAvatarState = state.GetAvatarState(avatarAddress); var action = new Synthesize() { AvatarAddress = avatarAddress, MaterialIds = SynthesizeSimulator.GetItemGuids(items), + ChargeAp = false, + MaterialGradeId = (int)grade, + MaterialItemSubTypeId = (int)itemSubType, }; var ctx = new ActionContext @@ -153,17 +155,11 @@ public void ExecuteSingle(Grade grade, ItemSubType itemSubType) break; } - var gradeDict = SynthesizeSimulator.GetGradeDict( - action.MaterialIds, - previousAvatarState, - blockIndex, - avatarAddress.ToHex(), - out _, - out _ - ); - var inputData = new SynthesizeSimulator.InputData() { + Grade = grade, + ItemSubType = itemSubType, + MaterialCount = itemSubTypes.Length, SynthesizeSheet = TableSheets.SynthesizeSheet, SynthesizeWeightSheet = TableSheets.SynthesizeWeightSheet, CostumeItemSheet = TableSheets.CostumeItemSheet, @@ -173,7 +169,6 @@ out _ EquipmentItemOptionSheet = TableSheets.EquipmentItemOptionSheet, SkillSheet = TableSheets.SkillSheet, RandomObject = new TestRandom(), - GradeDict = gradeDict, }; var result = SynthesizeSimulator.Simulate(inputData)[0]; @@ -220,11 +215,13 @@ public void ExecuteMultiple(Grade grade, ItemSubType itemSubType) (state, var items) = UpdateItemsFromSubType(grade, itemSubTypes, state, avatarAddress); state = state.SetActionPoint(avatarAddress, 120); - var previousAvatarState = state.GetAvatarState(avatarAddress); var action = new Synthesize() { AvatarAddress = avatarAddress, MaterialIds = SynthesizeSimulator.GetItemGuids(items), + ChargeAp = false, + MaterialGradeId = (int)grade, + MaterialItemSubTypeId = (int)itemSubType, }; var ctx = new ActionContext @@ -235,19 +232,12 @@ public void ExecuteMultiple(Grade grade, ItemSubType itemSubType) Signer = agentAddress, }; - state = action.Execute(ctx); - - var gradeDict = SynthesizeSimulator.GetGradeDict( - action.MaterialIds, - previousAvatarState, - blockIndex, - avatarAddress.ToHex(), - out _, - out _ - ); - + action.Execute(ctx); var inputData = new SynthesizeSimulator.InputData() { + Grade = grade, + ItemSubType = itemSubType, + MaterialCount = itemSubTypes.Length, SynthesizeSheet = TableSheets.SynthesizeSheet, SynthesizeWeightSheet = TableSheets.SynthesizeWeightSheet, CostumeItemSheet = TableSheets.CostumeItemSheet, @@ -257,7 +247,6 @@ out _ EquipmentItemOptionSheet = TableSheets.EquipmentItemOptionSheet, SkillSheet = TableSheets.SkillSheet, RandomObject = new TestRandom(), - GradeDict = gradeDict, }; var resultList = SynthesizeSimulator.Simulate(inputData); @@ -299,6 +288,9 @@ public void ExecuteNotEnoughActionPoint() { AvatarAddress = avatarAddress, MaterialIds = SynthesizeSimulator.GetItemGuids(items), + ChargeAp = false, + MaterialGradeId = (int)grade, + MaterialItemSubTypeId = (int)itemSubType, }; var ctx = new ActionContext @@ -336,6 +328,9 @@ public void ExecuteMultipleSameType(int testCount) { AvatarAddress = avatarAddress, MaterialIds = SynthesizeSimulator.GetItemGuids(items), + ChargeAp = false, + MaterialGradeId = (int)grade, + MaterialItemSubTypeId = (int)itemSubType, }; var ctx = new ActionContext @@ -380,6 +375,9 @@ public void ExecuteInvalidMaterial(Grade grade, ItemSubType[] itemSubTypes) { AvatarAddress = avatarAddress, MaterialIds = SynthesizeSimulator.GetItemGuids(items), + ChargeAp = false, + MaterialGradeId = (int)grade, + MaterialItemSubTypeId = (int)itemSubTypes[0], }; var ctx = new ActionContext diff --git a/Lib9c/Action/Synthesize.cs b/Lib9c/Action/Synthesize.cs index d0035cf080..1f5438af8e 100644 --- a/Lib9c/Action/Synthesize.cs +++ b/Lib9c/Action/Synthesize.cs @@ -11,6 +11,7 @@ using Nekoyume.Model.Item; using Nekoyume.Model.State; using Nekoyume.Helper; +using Nekoyume.Model.EnumType; using Nekoyume.Module; using Nekoyume.TableData; @@ -34,11 +35,30 @@ public class Synthesize : GameAction private const string MaterialsKey = "m"; private const string ChargeApKey = "c"; private const string AvatarAddressKey = "a"; + private const string GradeKey = "g"; + private const string ItemSubTypeKey = "i"; #region Fields + /// + /// Id list of items as material. + /// public List MaterialIds = new(); + /// + /// Whether to charge action points with action execution. + /// public bool ChargeAp; + /// + /// AvatarAddress of the signer. + /// public Address AvatarAddress; + /// + /// MaterialGrade of the material item. + /// + public int MaterialGradeId; + /// + /// ItemSubType of the material item. + /// + public int MaterialItemSubTypeId; #endregion Fields /// @@ -50,6 +70,8 @@ public override IWorld Execute(IActionContext context) { GasTracer.UseGas(1); var states = context.PreviousState; + var materialGrade = (Grade)MaterialGradeId; + var materialItemSubType = (ItemSubType)MaterialItemSubTypeId; // Collect addresses var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress); @@ -85,19 +107,34 @@ public override IWorld Execute(IActionContext context) // Calculate action point var actionPoint = CalculateActionPoint(states, avatarState, sheets, context); - - // Initialize variables - var gradeDict = SynthesizeSimulator.GetGradeDict(MaterialIds, avatarState, context.BlockIndex, - addressesHex, out var materialEquipments, out var materialCostumes); + var materialItems = SynthesizeSimulator.GetMaterialList( + MaterialIds, + avatarState, + context.BlockIndex, + materialGrade, + materialItemSubType, + addressesHex + ); // Unequip items (if necessary) - foreach (var materialEquipment in materialEquipments) + foreach (var materialItem in materialItems) { - materialEquipment.Unequip(); + switch (materialItem) + { + case Equipment equipment: + equipment.Unequip(); + break; + case Costume costume: + costume.Unequip(); + break; + } } - foreach (var materialCostume in materialCostumes) + + if (materialItems.Count == 0 || materialItems.Count != MaterialIds.Count) { - materialCostume.Unequip(); + throw new InvalidMaterialException( + $"{addressesHex} Aborted as the material item is not valid." + ); } // Remove materials from inventory @@ -113,6 +150,9 @@ public override IWorld Execute(IActionContext context) var synthesizedItems = SynthesizeSimulator.Simulate(new SynthesizeSimulator.InputData() { + Grade = materialGrade, + ItemSubType = materialItemSubType, + MaterialCount = materialItems.Count, SynthesizeSheet = sheets.GetSheet(), SynthesizeWeightSheet = sheets.GetSheet(), CostumeItemSheet = sheets.GetSheet(), @@ -123,7 +163,6 @@ public override IWorld Execute(IActionContext context) SkillSheet = sheets.GetSheet(), BlockIndex = context.BlockIndex, RandomObject = context.GetRandom(), - GradeDict = gradeDict, }); // Add synthesized items to inventory @@ -184,6 +223,8 @@ private long CalculateActionPoint(IWorld states, AvatarState avatarState, Sheets [MaterialsKey] = new List(MaterialIds.OrderBy(i => i).Select(i => i.Serialize())), [ChargeApKey] = ChargeAp.Serialize(), [AvatarAddressKey] = AvatarAddress.Serialize(), + [GradeKey] = (Integer)MaterialGradeId, + [ItemSubTypeKey] = (Integer)MaterialItemSubTypeId, } .ToImmutableDictionary(); @@ -192,6 +233,8 @@ protected override void LoadPlainValueInternal(IImmutableDictionary>; - /// /// Represents the result of the synthesis. /// @@ -33,25 +31,23 @@ public struct SynthesizeResult /// public static class SynthesizeSimulator { - private static readonly ItemType[] ValidItemType = - { - ItemType.Costume, - ItemType.Equipment, - }; - - private static readonly ItemSubType[] ValidItemSubType = - { - ItemSubType.FullCostume, - ItemSubType.Title, - ItemSubType.Grimoire, - ItemSubType.Aura, - }; - /// /// Simulate the synthesis of items. /// public struct InputData { + /// + /// The grade of the material item. + /// + public Grade Grade; + /// + /// The subtype of the material item. + /// + public ItemSubType ItemSubType; + /// + /// The number of materials. + /// + public int MaterialCount; /// /// The sheet that contains the synthesis information. /// @@ -101,10 +97,6 @@ public struct InputData /// Caution: Must have the same seed as when the action is executed /// public IRandom RandomObject; - /// - /// The grade dictionary of the material items. - /// - public GradeDict GradeDict; } private struct EquipmentData @@ -132,159 +124,131 @@ public static List Simulate(InputData inputData) var synthesizeSheet = inputData.SynthesizeSheet; var random = inputData.RandomObject; - var gradeDict = inputData.GradeDict; // Calculate the number of items to be synthesized based on materials - foreach (var gradeItem in gradeDict) + var gradeId = (int)inputData.Grade; + + if (!synthesizeSheet.TryGetValue(gradeId, out var synthesizeRow)) { - var gradeId = gradeItem.Key; - var subTypeDict = gradeItem.Value; + throw new SheetRowNotFoundException( + $"Aborted as the synthesize row for grade ({gradeId}) was failed to load in {nameof(SynthesizeSheet)}", gradeId + ); + } + + var itemSubType = inputData.ItemSubType; + var materialCount = inputData.MaterialCount; + + var requiredCount = synthesizeRow.RequiredCountDict[itemSubType].RequiredCount; + var succeedRate = synthesizeRow.RequiredCountDict[itemSubType].SucceedRate; + var synthesizeCount = materialCount / requiredCount; + var remainder = materialCount % requiredCount; + + if (synthesizeCount <= 0 || remainder != 0) + { + throw new NotEnoughMaterialException( + $"Aborted as the number of materials for grade {gradeId} and subtype {itemSubType} is not enough." + ); + } + + // Calculate success for each synthesis + for (var i = 0; i < synthesizeCount; i++) + { + // random value range is 0 ~ 9999 + // If the SucceedRate of the table is 0, use '<' for always to fail. + // and SucceedRate of the table is 10000, always success(because left value range is 0~9999) + var isSuccess = random.Next(SynthesizeSheet.SucceedRateMax) < succeedRate; + + var grade = (Grade)gradeId; + // Decide the item to add to inventory based on SynthesizeWeightSheet + var synthesizedItem = GetSynthesizedItem( + grade, + isSuccess, + inputData.SynthesizeWeightSheet, + inputData.CostumeItemSheet, + inputData.EquipmentItemSheet, + inputData.EquipmentItemRecipeSheet, + inputData.EquipmentItemSubRecipeSheetV2, + inputData.EquipmentItemOptionSheet, + inputData.SkillSheet, + inputData.BlockIndex, + random, + itemSubType, + out var equipmentData); - if (!synthesizeSheet.TryGetValue(gradeId, out var synthesizeRow)) + if (isSuccess && grade == (Grade)synthesizedItem.Grade) { - throw new SheetRowNotFoundException( - $"Aborted as the synthesize row for grade ({gradeId}) was failed to load in {nameof(SynthesizeSheet)}", gradeId - ); + // If there are no items in the data that are one above the current grade, they cannot succeed. + isSuccess = false; } - foreach (var subTypeItem in subTypeDict) + synthesizeResults.Add(new SynthesizeResult { - var itemSubType = subTypeItem.Key; - var materialCount = subTypeItem.Value; - - // TODO: subType별로 필요한 아이템 개수가 다를 수 있음 - var requiredCount = synthesizeRow.RequiredCountDict[itemSubType].RequiredCount; - var succeedRate = synthesizeRow.RequiredCountDict[itemSubType].SucceedRate; - var synthesizeCount = materialCount / requiredCount; - var remainder = materialCount % requiredCount; - - if (synthesizeCount <= 0 || remainder != 0) - { - throw new NotEnoughMaterialException( - $"Aborted as the number of materials for grade {gradeId} and subtype {itemSubType} is not enough." - ); - } - - // Calculate success for each synthesis - for (var i = 0; i < synthesizeCount; i++) - { - // random value range is 0 ~ 9999 - // If the SucceedRate of the table is 0, use '<' for always to fail. - // and SucceedRate of the table is 10000, always success(because left value range is 0~9999) - var isSuccess = random.Next(SynthesizeSheet.SucceedRateMax) < succeedRate; - - var grade = (Grade)gradeId; - // Decide the item to add to inventory based on SynthesizeWeightSheet - var synthesizedItem = GetSynthesizedItem( - grade, - isSuccess, - inputData.SynthesizeWeightSheet, - inputData.CostumeItemSheet, - inputData.EquipmentItemSheet, - inputData.EquipmentItemRecipeSheet, - inputData.EquipmentItemSubRecipeSheetV2, - inputData.EquipmentItemOptionSheet, - inputData.SkillSheet, - inputData.BlockIndex, - random, - itemSubType, - out var equipmentData); - - if (isSuccess && grade == (Grade)synthesizedItem.Grade) - { - // If there are no items in the data that are one above the current grade, they cannot succeed. - isSuccess = false; - } - - synthesizeResults.Add(new SynthesizeResult - { - ItemBase = synthesizedItem, - IsSuccess = isSuccess, - RecipeId = equipmentData.RecipeId, - SubRecipeId = equipmentData.SubRecipeId, - }); - } - } + ItemBase = synthesizedItem, + IsSuccess = isSuccess, + RecipeId = equipmentData.RecipeId, + SubRecipeId = equipmentData.SubRecipeId, + }); } return synthesizeResults; } - /// - /// Get the grade of the material items and return the dictionary of the grade and the number of items. - /// - /// material item ids - /// target avatar state - /// current block index - /// addresses in hex - /// material equipment list - /// material costume list - /// - /// - /// - public static GradeDict GetGradeDict(List materialIds, AvatarState avatarState, long blockIndex, - string addressesHex, out List materialEquipments, out List materialCostumes) + public static List GetMaterialList( + List materialIds, AvatarState avatarState, long blockIndex, + Grade grade, ItemSubType itemSubType, + string addressesHex) { - ItemSubType? cachedItemSubType = null; - var gradeDict = new GradeDict(); - materialEquipments = new List(); - materialCostumes = new List(); + return itemSubType switch + { + ItemSubType.FullCostume or ItemSubType.Title => GetCostumeMaterialList(materialIds, avatarState, grade, itemSubType, addressesHex), + ItemSubType.Aura or ItemSubType.Grimoire => GetEquipmentMaterialList(materialIds, avatarState, blockIndex, grade, itemSubType, addressesHex), + _ => throw new ArgumentException($"Invalid item sub type: {itemSubType}", nameof(itemSubType)), + }; + } + public static List GetEquipmentMaterialList( + List materialIds, AvatarState avatarState, long blockIndex, + Grade grade, ItemSubType itemSubType, + string addressesHex) + { + var materialEquipments = new List(); foreach (var materialId in materialIds) { - var materialEquipment = GetEquipmentFromId(materialId, avatarState, blockIndex, addressesHex, ref cachedItemSubType); - var materialCostume = GetCostumeFromId(materialId, avatarState, addressesHex, ref cachedItemSubType); - if (materialEquipment == null && materialCostume == null) + var materialEquipment = GetEquipmentFromId(materialId, avatarState, grade, itemSubType, blockIndex, addressesHex); + if (materialEquipment == null) { throw new InvalidMaterialException( - $"{addressesHex} Aborted as the material item is not a valid item type." + $"{addressesHex} Aborted as the material item is not a equipment item type." ); } - if (materialEquipment != null) - { - materialEquipments.Add(materialEquipment); - SetGradeDict(ref gradeDict, materialEquipment.Grade, materialEquipment.ItemSubType); - } - - if (materialCostume != null) - { - materialCostumes.Add(materialCostume); - SetGradeDict(ref gradeDict, materialCostume.Grade, materialCostume.ItemSubType); - } - } - - if (cachedItemSubType == null) - { - throw new InvalidOperationException("ItemSubType is not set."); + materialEquipments.Add(materialEquipment); } - return gradeDict; + return materialEquipments; } - private static void SetGradeDict(ref GradeDict gradeDict, int grade, ItemSubType itemSubType) + public static List GetCostumeMaterialList( + List materialIds, AvatarState avatarState, Grade grade, ItemSubType itemSubType, string addressesHex) { - if (gradeDict.ContainsKey(grade)) + var materialCostumes = new List(); + foreach (var materialId in materialIds) { - if (gradeDict[grade].ContainsKey(itemSubType)) - { - gradeDict[grade][itemSubType]++; - } - else + var materialEquipment = GetCostumeFromId(materialId, avatarState, grade, itemSubType, addressesHex); + if (materialEquipment == null) { - gradeDict[grade][itemSubType] = 1; + throw new InvalidMaterialException( + $"{addressesHex} Aborted as the material item is not a equipment item type." + ); } + + materialCostumes.Add(materialEquipment); } - else - { - gradeDict[grade] = new Dictionary - { - { itemSubType, 1 }, - }; - } + + return materialCostumes; } - private static Equipment? GetEquipmentFromId(Guid materialId, AvatarState avatarState, long blockIndex, string addressesHex, ref ItemSubType? cachedItemSubType) + private static Equipment? GetEquipmentFromId(Guid materialId, AvatarState avatarState, Grade grade, ItemSubType itemSubType, long blockIndex, string addressesHex) { if (!avatarState.inventory.TryGetNonFungibleItem(materialId, out Equipment materialEquipment)) { @@ -300,32 +264,31 @@ private static void SetGradeDict(ref GradeDict gradeDict, int grade, ItemSubType } // Validate item type - if (!ValidItemType.Contains(materialEquipment.ItemType)) + if (materialEquipment.ItemType != ItemType.Equipment) { throw new InvalidMaterialException( $"{addressesHex} Aborted as the material item is not a valid item type: {materialEquipment.ItemType}." ); } - if (!ValidItemSubType.Contains(materialEquipment.ItemSubType)) + if (materialEquipment.Grade != (int)grade) { throw new InvalidMaterialException( - $"{addressesHex} Aborted as the material item is not a valid item sub type: {materialEquipment.ItemSubType}." + $"{addressesHex} Aborted as the material item is not a valid grade: {materialEquipment.Grade}." ); } - cachedItemSubType ??= materialEquipment.ItemSubType; - if (materialEquipment.ItemSubType != cachedItemSubType) + if (materialEquipment.ItemSubType != itemSubType) { throw new InvalidMaterialException( - $"{addressesHex} Aborted as the material item is not a {cachedItemSubType}, but {materialEquipment.ItemSubType}." - ); + $"{addressesHex} Aborted as the material item is not a valid item sub type: {materialEquipment.ItemSubType}." + ); } return materialEquipment; } - private static Costume? GetCostumeFromId(Guid materialId, AvatarState avatarState, string addressesHex, ref ItemSubType? cachedItemSubType) + private static Costume? GetCostumeFromId(Guid materialId, AvatarState avatarState, Grade grade, ItemSubType itemSubType, string addressesHex) { if (!avatarState.inventory.TryGetNonFungibleItem(materialId, out Costume costumeItem)) { @@ -333,26 +296,25 @@ private static void SetGradeDict(ref GradeDict gradeDict, int grade, ItemSubType } // Validate item type - if (!ValidItemType.Contains(costumeItem.ItemType)) + if (costumeItem.ItemType != ItemType.Costume) { throw new InvalidMaterialException( $"{addressesHex} Aborted as the material item is not a valid item type: {costumeItem.ItemType}." ); } - if (!ValidItemSubType.Contains(costumeItem.ItemSubType)) + if (costumeItem.Grade != (int)grade) { throw new InvalidMaterialException( - $"{addressesHex} Aborted as the material item is not a valid item sub type: {costumeItem.ItemSubType}." + $"{addressesHex} Aborted as the material item is not a valid grade: {costumeItem.Grade}." ); } - cachedItemSubType ??= costumeItem.ItemSubType; - if (costumeItem.ItemSubType != cachedItemSubType) + if (costumeItem.ItemSubType != itemSubType) { throw new InvalidMaterialException( - $"{addressesHex} Aborted as the material item is not a {cachedItemSubType}, but {costumeItem.ItemSubType}." - ); + $"{addressesHex} Aborted as the material item is not a valid item sub type: {costumeItem.ItemSubType}." + ); } return costumeItem; @@ -403,7 +365,7 @@ private static ItemBase GetSynthesizedItem( private static ItemBase GetRandomCostume(Grade grade, bool isSuccess, ItemSubType itemSubType, SynthesizeWeightSheet weightSheet, CostumeItemSheet costumeItemSheet, IRandom random) { - HashSet? synthesizeResultPool = null; + HashSet? synthesizeResultPool; if (isSuccess) { synthesizeResultPool = GetSynthesizeResultPool(GetTargetGrade(grade), itemSubType, costumeItemSheet); @@ -459,7 +421,7 @@ private static ItemBase GetRandomEquipment( IRandom random, ref EquipmentData equipmentData) { - HashSet? synthesizeResultPool = null; + HashSet? synthesizeResultPool; if (isSuccess) { synthesizeResultPool = GetSynthesizeResultPool(GetTargetGrade(grade), itemSubType, equipmentItemSheet); From f617b804b0ef02113b1f84019c6bd2e2f93ec8f3 Mon Sep 17 00:00:00 2001 From: moreal Date: Fri, 6 Dec 2024 22:04:46 +0900 Subject: [PATCH 075/136] chore(js): bump `@types/node` package --- .../@planetarium/lib9c/package.json | 2 +- .../javascript/@planetarium/pnpm-lock.yaml | 52 +++++++++---------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/integrations/javascript/@planetarium/lib9c/package.json b/integrations/javascript/@planetarium/lib9c/package.json index 548aa722d6..29729cfc69 100644 --- a/integrations/javascript/@planetarium/lib9c/package.json +++ b/integrations/javascript/@planetarium/lib9c/package.json @@ -18,7 +18,7 @@ "license": "AGPL-3.0-only", "devDependencies": { "@biomejs/biome": "^1.7.1", - "@types/node": "^20.12.8", + "@types/node": "^22.10.1", "@types/uuid": "^9.0.8", "execa": "^8.0.1", "tsup": "^8.0.2", diff --git a/integrations/javascript/@planetarium/pnpm-lock.yaml b/integrations/javascript/@planetarium/pnpm-lock.yaml index d79b8e090d..a3ff3d83e3 100644 --- a/integrations/javascript/@planetarium/pnpm-lock.yaml +++ b/integrations/javascript/@planetarium/pnpm-lock.yaml @@ -31,8 +31,8 @@ importers: specifier: ^1.7.1 version: 1.7.1 '@types/node': - specifier: ^20.12.8 - version: 20.12.8 + specifier: ^22.10.1 + version: 22.10.1 '@types/uuid': specifier: ^9.0.8 version: 9.0.8 @@ -50,10 +50,10 @@ importers: version: 5.4.5 vitepress: specifier: ^1.2.2 - version: 1.2.2(@algolia/client-search@4.23.3)(@types/node@20.12.8)(postcss@8.4.38)(search-insights@2.14.0)(typescript@5.4.5) + version: 1.2.2(@algolia/client-search@4.23.3)(@types/node@22.10.1)(postcss@8.4.38)(search-insights@2.14.0)(typescript@5.4.5) vitest: specifier: ^1.5.2 - version: 1.5.2(@types/node@20.12.8) + version: 1.5.2(@types/node@22.10.1) packages: @@ -653,8 +653,8 @@ packages: '@types/mdurl@2.0.0': resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==} - '@types/node@20.12.8': - resolution: {integrity: sha512-NU0rJLJnshZWdE/097cdCBbyW1h4hEg0xpovcoAQYHl8dnEyp/NAOiE45pvc+Bd1Dt+2r94v2eGFpQJ4R7g+2w==} + '@types/node@22.10.1': + resolution: {integrity: sha512-qKgsUwfHZV2WCWLAnVP1JqnpE6Im6h3Y0+fYgMTasNQ7V++CBX5OT1as0g0f+OyubbFqhf6XVNIsmN4IIhEgGQ==} '@types/uuid@9.0.8': resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} @@ -1410,8 +1410,8 @@ packages: ufo@1.5.3: resolution: {integrity: sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==} - undici-types@5.26.5: - resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + undici-types@6.20.0: + resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} uuid@9.0.1: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} @@ -2013,17 +2013,17 @@ snapshots: '@types/mdurl@2.0.0': {} - '@types/node@20.12.8': + '@types/node@22.10.1': dependencies: - undici-types: 5.26.5 + undici-types: 6.20.0 '@types/uuid@9.0.8': {} '@types/web-bluetooth@0.0.20': {} - '@vitejs/plugin-vue@5.0.4(vite@5.2.12(@types/node@20.12.8))(vue@3.4.27(typescript@5.4.5))': + '@vitejs/plugin-vue@5.0.4(vite@5.2.12(@types/node@22.10.1))(vue@3.4.27(typescript@5.4.5))': dependencies: - vite: 5.2.12(@types/node@20.12.8) + vite: 5.2.12(@types/node@22.10.1) vue: 3.4.27(typescript@5.4.5) '@vitest/expect@1.5.2': @@ -2796,17 +2796,17 @@ snapshots: ufo@1.5.3: {} - undici-types@5.26.5: {} + undici-types@6.20.0: {} uuid@9.0.1: {} - vite-node@1.5.2(@types/node@20.12.8): + vite-node@1.5.2(@types/node@22.10.1): dependencies: cac: 6.7.14 debug: 4.3.4 pathe: 1.1.2 picocolors: 1.0.0 - vite: 5.2.10(@types/node@20.12.8) + vite: 5.2.10(@types/node@22.10.1) transitivePeerDependencies: - '@types/node' - less @@ -2817,32 +2817,32 @@ snapshots: - supports-color - terser - vite@5.2.10(@types/node@20.12.8): + vite@5.2.10(@types/node@22.10.1): dependencies: esbuild: 0.20.2 postcss: 8.4.38 rollup: 4.16.4 optionalDependencies: - '@types/node': 20.12.8 + '@types/node': 22.10.1 fsevents: 2.3.3 - vite@5.2.12(@types/node@20.12.8): + vite@5.2.12(@types/node@22.10.1): dependencies: esbuild: 0.20.2 postcss: 8.4.38 rollup: 4.16.4 optionalDependencies: - '@types/node': 20.12.8 + '@types/node': 22.10.1 fsevents: 2.3.3 - vitepress@1.2.2(@algolia/client-search@4.23.3)(@types/node@20.12.8)(postcss@8.4.38)(search-insights@2.14.0)(typescript@5.4.5): + vitepress@1.2.2(@algolia/client-search@4.23.3)(@types/node@22.10.1)(postcss@8.4.38)(search-insights@2.14.0)(typescript@5.4.5): dependencies: '@docsearch/css': 3.6.0 '@docsearch/js': 3.6.0(@algolia/client-search@4.23.3)(search-insights@2.14.0) '@shikijs/core': 1.6.1 '@shikijs/transformers': 1.6.1 '@types/markdown-it': 14.1.1 - '@vitejs/plugin-vue': 5.0.4(vite@5.2.12(@types/node@20.12.8))(vue@3.4.27(typescript@5.4.5)) + '@vitejs/plugin-vue': 5.0.4(vite@5.2.12(@types/node@22.10.1))(vue@3.4.27(typescript@5.4.5)) '@vue/devtools-api': 7.2.1(vue@3.4.27(typescript@5.4.5)) '@vue/shared': 3.4.27 '@vueuse/core': 10.10.0(vue@3.4.27(typescript@5.4.5)) @@ -2851,7 +2851,7 @@ snapshots: mark.js: 8.11.1 minisearch: 6.3.0 shiki: 1.6.1 - vite: 5.2.12(@types/node@20.12.8) + vite: 5.2.12(@types/node@22.10.1) vue: 3.4.27(typescript@5.4.5) optionalDependencies: postcss: 8.4.38 @@ -2882,7 +2882,7 @@ snapshots: - typescript - universal-cookie - vitest@1.5.2(@types/node@20.12.8): + vitest@1.5.2(@types/node@22.10.1): dependencies: '@vitest/expect': 1.5.2 '@vitest/runner': 1.5.2 @@ -2901,11 +2901,11 @@ snapshots: strip-literal: 2.1.0 tinybench: 2.8.0 tinypool: 0.8.4 - vite: 5.2.10(@types/node@20.12.8) - vite-node: 1.5.2(@types/node@20.12.8) + vite: 5.2.10(@types/node@22.10.1) + vite-node: 1.5.2(@types/node@22.10.1) why-is-node-running: 2.2.2 optionalDependencies: - '@types/node': 20.12.8 + '@types/node': 22.10.1 transitivePeerDependencies: - less - lightningcss From 75a74d1794c0e10d6c02581a1a7dbc2d4d4236be Mon Sep 17 00:00:00 2001 From: moreal Date: Fri, 6 Dec 2024 22:06:59 +0900 Subject: [PATCH 076/136] docs(js): correct Lib9c.Tools relative path --- integrations/javascript/@planetarium/lib9c/CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integrations/javascript/@planetarium/lib9c/CONTRIBUTING.md b/integrations/javascript/@planetarium/lib9c/CONTRIBUTING.md index e680cec222..93ae62bf23 100644 --- a/integrations/javascript/@planetarium/lib9c/CONTRIBUTING.md +++ b/integrations/javascript/@planetarium/lib9c/CONTRIBUTING.md @@ -23,7 +23,7 @@ pnpm build `@planetarium/lib9c` uses `Lib9c.Tools action analyze` command to check whether implemented actions make valid bencodex value. You should build the .NET project first. **If the .NET Lib9c project is changed, you must build `Lib9c.Tools` project again.** ``` -dotnet build ../../.Lib9c.Tools/Lib9c.Tools.csproj +dotnet build ../../../../.Lib9c.Tools/Lib9c.Tools.csproj ``` If the build proceeded successfully, you can run the below command. From 071445b8feead798ba3895a0c0860468fbd3e98c Mon Sep 17 00:00:00 2001 From: moreal Date: Fri, 6 Dec 2024 22:41:21 +0900 Subject: [PATCH 077/136] fix(js): handle big quantity FAV too --- .../lib9c/src/models/currencies.ts | 35 +++++++++++++---- .../lib9c/tests/models/currencies.test.ts | 39 +++++++++++++++++++ 2 files changed, 66 insertions(+), 8 deletions(-) create mode 100644 integrations/javascript/@planetarium/lib9c/tests/models/currencies.test.ts diff --git a/integrations/javascript/@planetarium/lib9c/src/models/currencies.ts b/integrations/javascript/@planetarium/lib9c/src/models/currencies.ts index a3b875cd9c..365bfeb1a1 100644 --- a/integrations/javascript/@planetarium/lib9c/src/models/currencies.ts +++ b/integrations/javascript/@planetarium/lib9c/src/models/currencies.ts @@ -61,18 +61,33 @@ export const GARAGE: Currency = { }; /** - * Creates a new `FungibleAssetValue` with the given `currency` and `number-like` values. If the `number-like` value is a `string`, it is parsed and multiplied by 10 times `currency.decimalPlaces` to return the FungibleAssetValue. The same is true if it is a `number`. However, in the case of `number`, beware of the possibility of an incorrect decimal value. The same is true for `bigint`, but due to the nature of the `bigint` type, decimals are not allowed. + * Creates a new `FungibleAssetValue` with the given `currency` and `number-like` values. + * If the `number-like` value is a `string`, it is parsed and multiplied by 10 times `currency.decimalPlaces` to return the FungibleAssetValue. + * The same is true if it is a `number`. However, in the case of `number`, beware of the possibility of an incorrect decimal value. + * The same is true for `bigint`, but due to the nature of the `bigint` type, decimals are not allowed. + * + * You must add `Decimal.set({ toExpPos: 900000000000000 });` line before using `fav()` function. * @param currency The currency of the value. * @param numberLike The amount of the given currency. * @returns * @example To create a FungibleAssetValue for 1 NCG: * ```typescript + * import { fav } from "@planetarium/lib9c"; + * import { Decimal } from "decimal.js"; + * + * Decimal.set({ toExpPos: 900000000000000 }); + * * fav(NCG, 1); // With number * fav(NCG, "1"); // With string * fav(NCG, 1n); // With bigint * ``` * @example To create a FungibleAssetValue for 1.23 NCG: * ```typescript + * import { fav } from "@planetarium/lib9c"; + * import { Decimal } from "decimal.js"; + * + * Decimal.set({ toExpPos: 900000000000000 }); + * * fav(NCG, 1.23); // With number * fav(NCG, "1.23"); // With string * // With bigint, it cannot be created because bigint doesn't represents decimal values. @@ -82,13 +97,17 @@ export function fav( currency: Currency, numberLike: string | number | bigint, ): FungibleAssetValue { - const rawValue = BigInt( - new Decimal( - typeof numberLike === "bigint" ? numberLike.toString() : numberLike, - ) - .mul(Decimal.pow(10, currency.decimalPlaces)) - .toString(), - ); + const intermediateValue = new Decimal( + typeof numberLike === "bigint" ? numberLike.toString() : numberLike, + ).mul(Decimal.pow(10, currency.decimalPlaces)); + if (!intermediateValue.isInteger()) { + throw new Error( + `The given 'numberLike' parameter seems to have more decimal places than the maximum supported by currency. (numberLike: type=${typeof numberLike} value=${numberLike}) (currency.decimalPlaces: ${ + currency.decimalPlaces + })`, + ); + } + const rawValue = BigInt(intermediateValue.toString()); return { currency, rawValue, diff --git a/integrations/javascript/@planetarium/lib9c/tests/models/currencies.test.ts b/integrations/javascript/@planetarium/lib9c/tests/models/currencies.test.ts new file mode 100644 index 0000000000..fa7c2c208f --- /dev/null +++ b/integrations/javascript/@planetarium/lib9c/tests/models/currencies.test.ts @@ -0,0 +1,39 @@ +import { Decimal } from "decimal.js"; +import { afterAll, beforeEach, describe, expect, it } from "vitest"; +import { MEAD, NCG, fav } from "../../src/index.js"; + +describe("fav", () => { + beforeEach(() => { + Decimal.set({ + defaults: true, + }); + }); + + afterAll(() => { + Decimal.set({ + defaults: true, + }); + }); + + it("should be able to handle FungibleAssetValue with big quantity", () => { + Decimal.set({ + toExpPos: 900000000000000, + }); + + expect(fav(MEAD, 100_0000n)).toStrictEqual({ + currency: MEAD, + rawValue: 1000000000000000000000000n, + }); + }); + + it("should throw error with parameter having more decimal places than the maximum supported by currency", () => { + Decimal.set({ + defaults: true, + }); + + // NCG.decimalPlaces = 2. + expect(() => fav(NCG, "0.001")).toThrowError( + /parameter seems to have more decimal places than the maximum supported by currency/, + ); + }); +}); From 790d735adf46f080c758c7ce91bb5bc78e6fc607 Mon Sep 17 00:00:00 2001 From: moreal Date: Fri, 6 Dec 2024 22:44:56 +0900 Subject: [PATCH 078/136] feat(js): introduce `mint_assets` action --- .../lib9c/src/actions/mint_assets.ts | 64 +++++++++++++ .../@planetarium/lib9c/src/index.ts | 4 + .../lib9c/tests/actions/mint_assets.test.ts | 90 +++++++++++++++++++ 3 files changed, 158 insertions(+) create mode 100644 integrations/javascript/@planetarium/lib9c/src/actions/mint_assets.ts create mode 100644 integrations/javascript/@planetarium/lib9c/tests/actions/mint_assets.test.ts diff --git a/integrations/javascript/@planetarium/lib9c/src/actions/mint_assets.ts b/integrations/javascript/@planetarium/lib9c/src/actions/mint_assets.ts new file mode 100644 index 0000000000..c7610efeb6 --- /dev/null +++ b/integrations/javascript/@planetarium/lib9c/src/actions/mint_assets.ts @@ -0,0 +1,64 @@ +import { Buffer } from "buffer"; +import type { Address } from "@planetarium/account"; +import type { Value } from "@planetarium/bencodex"; +import { type FungibleAssetValue, encodeCurrency } from "@planetarium/tx"; +import type { HashDigest } from "../models/hashdigest.js"; +import { PolymorphicAction } from "./common.js"; + +interface IFungibleAssetValues { + recipient: Address; + amount: FungibleAssetValue; +} + +type FungibleItemId = HashDigest<"SHA256">; +interface IFungibleItems { + recipient: Address; + fungibleItemId: FungibleItemId; + count: bigint; +} + +export type MintSpec = + | IFungibleAssetValues + | IFungibleItems + | (IFungibleAssetValues & IFungibleItems); +function encodeMintSpec(value: IFungibleAssetValues | IFungibleItems): Value { + if ((value as IFungibleAssetValues).amount !== undefined) { + const favs = value as IFungibleAssetValues; + return [ + favs.recipient.toBytes(), + [encodeCurrency(favs.amount.currency), favs.amount.rawValue], + null, + ]; + } + + // else + const fis = value as IFungibleItems; + return [ + fis.recipient.toBytes(), + null, + [fis.fungibleItemId.toBytes(), fis.count], + ]; +} + +export type MintAssetsArgs = { + mintSpecs: MintSpec[]; + memo: string | null; +}; + +export class MintAssets extends PolymorphicAction { + protected readonly type_id: string = "mint_assets"; + + private readonly mintSpecs: (IFungibleAssetValues | IFungibleItems)[]; + private readonly memo: string | null; + + constructor({ mintSpecs, memo }: MintAssetsArgs) { + super(); + + this.mintSpecs = mintSpecs; + this.memo = memo; + } + + protected plain_value(): Value { + return [this.memo, ...this.mintSpecs.map(encodeMintSpec)]; + } +} diff --git a/integrations/javascript/@planetarium/lib9c/src/index.ts b/integrations/javascript/@planetarium/lib9c/src/index.ts index 5d7a885627..0ce97809a9 100644 --- a/integrations/javascript/@planetarium/lib9c/src/index.ts +++ b/integrations/javascript/@planetarium/lib9c/src/index.ts @@ -68,4 +68,8 @@ export { MigrateDelegation, type MigrateDelegationArgs, } from "./actions/migrate_delegation.js"; +export { + MintAssets, + type MintAssetsArgs, +} from "./actions/mint_assets.js"; export { makeTx, type NetworkProvider } from "./tx.js"; diff --git a/integrations/javascript/@planetarium/lib9c/tests/actions/mint_assets.test.ts b/integrations/javascript/@planetarium/lib9c/tests/actions/mint_assets.test.ts new file mode 100644 index 0000000000..e3c3fd7126 --- /dev/null +++ b/integrations/javascript/@planetarium/lib9c/tests/actions/mint_assets.test.ts @@ -0,0 +1,90 @@ +import { afterEach } from "node:test"; +import { Decimal } from "decimal.js"; +import { afterAll, beforeEach, describe } from "vitest"; +import { MEAD, MintAssets, NCG, fav } from "../../src/index.js"; +import { runTests } from "./common.js"; +import { agentAddress, fungibleIdA } from "./fixtures.js"; + +describe("MintAssets", () => { + beforeEach(() => { + Decimal.set({ + toExpPos: 900000000000000, + }); + }); + + afterEach(() => { + Decimal.set({ + defaults: true, + }); + }); + + afterAll(() => { + Decimal.set({ + defaults: true, + }); + }); + + runTests("valid case", [ + new MintAssets({ + mintSpecs: [], + memo: null, + }), + new MintAssets({ + mintSpecs: [], + memo: "memo", + }), + new MintAssets({ + mintSpecs: [ + { + recipient: agentAddress, + amount: fav(MEAD, 100_0000n), + }, + ], + memo: "memo", + }), + new MintAssets({ + mintSpecs: [ + { + recipient: agentAddress, + amount: fav(NCG, 10n), + }, + ], + memo: "memo", + }), + new MintAssets({ + mintSpecs: [ + { + recipient: agentAddress, + fungibleItemId: fungibleIdA, + count: 1n, + }, + ], + memo: "memo", + }), + new MintAssets({ + mintSpecs: [ + { + recipient: agentAddress, + fungibleItemId: fungibleIdA, + count: 1n, + }, + { + recipient: agentAddress, + amount: fav(MEAD, 100_0000n), + }, + ], + memo: "memo", + }), + new MintAssets({ + mintSpecs: [ + { + recipient: agentAddress, + fungibleItemId: fungibleIdA, + count: 1n, + amount: fav(MEAD, 100_0000n), + }, + ], + memo: "memo", + }), + ]); +}); From 55ded212459e6c10ff96af297bdcdc5e5aba1f86 Mon Sep 17 00:00:00 2001 From: moreal Date: Sat, 7 Dec 2024 00:24:38 +0900 Subject: [PATCH 079/136] test(js): create action instance lazily --- .../lib9c/tests/actions/common.ts | 12 +- .../lib9c/tests/actions/mint_assets.test.ts | 114 +++++++++--------- 2 files changed, 69 insertions(+), 57 deletions(-) diff --git a/integrations/javascript/@planetarium/lib9c/tests/actions/common.ts b/integrations/javascript/@planetarium/lib9c/tests/actions/common.ts index 4ce1c88638..eda3839350 100644 --- a/integrations/javascript/@planetarium/lib9c/tests/actions/common.ts +++ b/integrations/javascript/@planetarium/lib9c/tests/actions/common.ts @@ -6,10 +6,18 @@ import { execa } from "execa"; import { expect, test } from "vitest"; import type { PolymorphicAction } from "../../src/index.js"; -export function runTests(name: string, cases: PolymorphicAction[]) { +type ActionFn = () => PolymorphicAction; + +export function runTests( + name: string, + cases: (PolymorphicAction | ActionFn)[], +) { for (let i = 0; i < cases.length; i++) { test(`${name} ${i}`, async () => { - const action = cases[i]; + const action = + typeof cases[i] === "function" + ? (cases[i] as ActionFn)() + : (cases[i] as PolymorphicAction); const bytes = action.serialize(); const command = "dotnet"; diff --git a/integrations/javascript/@planetarium/lib9c/tests/actions/mint_assets.test.ts b/integrations/javascript/@planetarium/lib9c/tests/actions/mint_assets.test.ts index e3c3fd7126..2cfe9a6c4b 100644 --- a/integrations/javascript/@planetarium/lib9c/tests/actions/mint_assets.test.ts +++ b/integrations/javascript/@planetarium/lib9c/tests/actions/mint_assets.test.ts @@ -1,6 +1,5 @@ -import { afterEach } from "node:test"; import { Decimal } from "decimal.js"; -import { afterAll, beforeEach, describe } from "vitest"; +import { afterAll, afterEach, beforeEach, describe } from "vitest"; import { MEAD, MintAssets, NCG, fav } from "../../src/index.js"; import { runTests } from "./common.js"; import { agentAddress, fungibleIdA } from "./fixtures.js"; @@ -33,58 +32,63 @@ describe("MintAssets", () => { mintSpecs: [], memo: "memo", }), - new MintAssets({ - mintSpecs: [ - { - recipient: agentAddress, - amount: fav(MEAD, 100_0000n), - }, - ], - memo: "memo", - }), - new MintAssets({ - mintSpecs: [ - { - recipient: agentAddress, - amount: fav(NCG, 10n), - }, - ], - memo: "memo", - }), - new MintAssets({ - mintSpecs: [ - { - recipient: agentAddress, - fungibleItemId: fungibleIdA, - count: 1n, - }, - ], - memo: "memo", - }), - new MintAssets({ - mintSpecs: [ - { - recipient: agentAddress, - fungibleItemId: fungibleIdA, - count: 1n, - }, - { - recipient: agentAddress, - amount: fav(MEAD, 100_0000n), - }, - ], - memo: "memo", - }), - new MintAssets({ - mintSpecs: [ - { - recipient: agentAddress, - fungibleItemId: fungibleIdA, - count: 1n, - amount: fav(MEAD, 100_0000n), - }, - ], - memo: "memo", - }), + () => + new MintAssets({ + mintSpecs: [ + { + recipient: agentAddress, + amount: fav(MEAD, 100_0000n), + }, + ], + memo: "memo", + }), + () => + new MintAssets({ + mintSpecs: [ + { + recipient: agentAddress, + amount: fav(NCG, 10n), + }, + ], + memo: "memo", + }), + () => + new MintAssets({ + mintSpecs: [ + { + recipient: agentAddress, + fungibleItemId: fungibleIdA, + count: 1n, + }, + ], + memo: "memo", + }), + () => + new MintAssets({ + mintSpecs: [ + { + recipient: agentAddress, + fungibleItemId: fungibleIdA, + count: 1n, + }, + { + recipient: agentAddress, + amount: fav(MEAD, 100_0000n), + }, + ], + memo: "memo", + }), + () => + new MintAssets({ + mintSpecs: [ + { + recipient: agentAddress, + fungibleItemId: fungibleIdA, + count: 1n, + amount: fav(MEAD, 100_0000n), + }, + ], + memo: "memo", + }), ]); }); From 4730b50837c6f1055dd31e37119e1a0d08d7a886 Mon Sep 17 00:00:00 2001 From: moreal Date: Mon, 9 Dec 2024 11:35:14 +0900 Subject: [PATCH 080/136] =?UTF-8?q?feat(js):=20rename=20`NetworkProvider`?= =?UTF-8?q?=20=E2=86=92=20`TxMetadataProvider`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- integrations/javascript/@planetarium/lib9c/src/index.ts | 2 +- integrations/javascript/@planetarium/lib9c/src/tx.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/integrations/javascript/@planetarium/lib9c/src/index.ts b/integrations/javascript/@planetarium/lib9c/src/index.ts index 0ce97809a9..115b566391 100644 --- a/integrations/javascript/@planetarium/lib9c/src/index.ts +++ b/integrations/javascript/@planetarium/lib9c/src/index.ts @@ -72,4 +72,4 @@ export { MintAssets, type MintAssetsArgs, } from "./actions/mint_assets.js"; -export { makeTx, type NetworkProvider } from "./tx.js"; +export { makeTx, type TxMetadataProvider } from "./tx.js"; diff --git a/integrations/javascript/@planetarium/lib9c/src/tx.ts b/integrations/javascript/@planetarium/lib9c/src/tx.ts index 0d231a1eb8..c68aba086c 100644 --- a/integrations/javascript/@planetarium/lib9c/src/tx.ts +++ b/integrations/javascript/@planetarium/lib9c/src/tx.ts @@ -6,14 +6,14 @@ import { TransferAsset } from "./actions/transfer_asset.js"; import { TransferAssets } from "./actions/transfer_assets.js"; import { MEAD, fav } from "./models/currencies.js"; -export interface NetworkProvider { +export interface TxMetadataProvider { getNextNonce(address: Address): Promise; getGenesisHash(): Promise; } export async function makeTx( account: Account, - provider: NetworkProvider, + provider: TxMetadataProvider, action: PolymorphicAction, ): Promise { const publicKey = await account.getPublicKey(); From 40a0bb7f20611dc61678ea06c7950e19cc5c2737 Mon Sep 17 00:00:00 2001 From: moreal Date: Mon, 9 Dec 2024 12:07:17 +0900 Subject: [PATCH 081/136] =?UTF-8?q?refactor(js):=20move=20`tx.ts`=20?= =?UTF-8?q?=E2=86=92=20`tx/common.ts`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- integrations/javascript/@planetarium/lib9c/src/index.ts | 2 +- .../@planetarium/lib9c/src/{tx.ts => tx/common.ts} | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) rename integrations/javascript/@planetarium/lib9c/src/{tx.ts => tx/common.ts} (81%) diff --git a/integrations/javascript/@planetarium/lib9c/src/index.ts b/integrations/javascript/@planetarium/lib9c/src/index.ts index 115b566391..b2b3cbdc85 100644 --- a/integrations/javascript/@planetarium/lib9c/src/index.ts +++ b/integrations/javascript/@planetarium/lib9c/src/index.ts @@ -72,4 +72,4 @@ export { MintAssets, type MintAssetsArgs, } from "./actions/mint_assets.js"; -export { makeTx, type TxMetadataProvider } from "./tx.js"; +export { makeTx, type TxMetadataProvider } from "./tx/common.js"; diff --git a/integrations/javascript/@planetarium/lib9c/src/tx.ts b/integrations/javascript/@planetarium/lib9c/src/tx/common.ts similarity index 81% rename from integrations/javascript/@planetarium/lib9c/src/tx.ts rename to integrations/javascript/@planetarium/lib9c/src/tx/common.ts index c68aba086c..9057c6b76a 100644 --- a/integrations/javascript/@planetarium/lib9c/src/tx.ts +++ b/integrations/javascript/@planetarium/lib9c/src/tx/common.ts @@ -1,10 +1,10 @@ import { Buffer } from "buffer"; import { type Account, Address } from "@planetarium/account"; import type { UnsignedTx } from "@planetarium/tx"; -import type { PolymorphicAction } from "./actions/common.js"; -import { TransferAsset } from "./actions/transfer_asset.js"; -import { TransferAssets } from "./actions/transfer_assets.js"; -import { MEAD, fav } from "./models/currencies.js"; +import type { PolymorphicAction } from "../actions/common.js"; +import { TransferAsset } from "../actions/transfer_asset.js"; +import { TransferAssets } from "../actions/transfer_assets.js"; +import { MEAD, fav } from "../models/currencies.js"; export interface TxMetadataProvider { getNextNonce(address: Address): Promise; From b6370d21751388e20fdb23d83e422fd484b5fcd0 Mon Sep 17 00:00:00 2001 From: moreal Date: Mon, 9 Dec 2024 12:09:50 +0900 Subject: [PATCH 082/136] feat(js): change return type of `TxMetadataProvider.getGenesisHash` --- integrations/javascript/@planetarium/lib9c/src/tx/common.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/integrations/javascript/@planetarium/lib9c/src/tx/common.ts b/integrations/javascript/@planetarium/lib9c/src/tx/common.ts index 9057c6b76a..a805c618f5 100644 --- a/integrations/javascript/@planetarium/lib9c/src/tx/common.ts +++ b/integrations/javascript/@planetarium/lib9c/src/tx/common.ts @@ -1,4 +1,3 @@ -import { Buffer } from "buffer"; import { type Account, Address } from "@planetarium/account"; import type { UnsignedTx } from "@planetarium/tx"; import type { PolymorphicAction } from "../actions/common.js"; @@ -8,7 +7,7 @@ import { MEAD, fav } from "../models/currencies.js"; export interface TxMetadataProvider { getNextNonce(address: Address): Promise; - getGenesisHash(): Promise; + getGenesisHash(): Promise; } export async function makeTx( @@ -28,7 +27,7 @@ export async function makeTx( return { nonce, - genesisHash: Buffer.from(genesisHash, "hex"), + genesisHash: genesisHash, signer: signer.toBytes(), updatedAddresses: new Set(), actions: [action.bencode()], From 8385730eead035a10de0798a3be575288741653a Mon Sep 17 00:00:00 2001 From: moreal Date: Mon, 9 Dec 2024 15:31:19 +0900 Subject: [PATCH 083/136] fix(js): correct `package.json` `exports` field --- .../javascript/@planetarium/lib9c/package.json | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/integrations/javascript/@planetarium/lib9c/package.json b/integrations/javascript/@planetarium/lib9c/package.json index 29729cfc69..8b9f4f54a8 100644 --- a/integrations/javascript/@planetarium/lib9c/package.json +++ b/integrations/javascript/@planetarium/lib9c/package.json @@ -3,6 +3,17 @@ "version": "0.4.0", "description": "", "module": "dist/index.js", + "exports": { + ".": { + "node": { + "types": "./dist/index.d.cts", + "import": "./dist/index.js", + "require": "./dist/index.cjs" + }, + "browser": "./dist/index.js", + "default": "./dist/index.js" + } + }, "type": "module", "scripts": { "build": "tsup", From 7d4385a110895ace5e215089234207dbb6923129 Mon Sep 17 00:00:00 2001 From: moreal Date: Mon, 9 Dec 2024 16:22:33 +0900 Subject: [PATCH 084/136] feat(js): introduce `@planetarium/9c-headless-provider` --- integrations/javascript/.vscode/settings.json | 3 + .../9c-headless-provider/.gitignore | 1 + .../@planetarium/9c-headless-provider/LICENSE | 661 ++++ .../9c-headless-provider/README.md | 5 + .../9c-headless-provider/biome.json | 21 + .../graphql/headless/api.graphql | 15 + .../graphql/headless/codegen.yml | 9 + .../graphql/headless/schema.graphql | 2804 ++++++++++++++ .../9c-headless-provider/jsr.json | 14 + .../9c-headless-provider/package.json | 37 + .../9c-headless-provider/src/index.ts | 42 + .../9c-headless-provider/tsconfig.json | 114 + .../9c-headless-provider/tsup.config.ts | 7 + .../9c-headless-provider/vite.config.ts | 7 + .../javascript/@planetarium/lib9c/README.md | 2 +- .../javascript/@planetarium/pnpm-lock.yaml | 3341 ++++++++++++++++- .../@planetarium/pnpm-workspace.yaml | 1 + 17 files changed, 6997 insertions(+), 87 deletions(-) create mode 100644 integrations/javascript/.vscode/settings.json create mode 100644 integrations/javascript/@planetarium/9c-headless-provider/.gitignore create mode 100644 integrations/javascript/@planetarium/9c-headless-provider/LICENSE create mode 100644 integrations/javascript/@planetarium/9c-headless-provider/README.md create mode 100644 integrations/javascript/@planetarium/9c-headless-provider/biome.json create mode 100644 integrations/javascript/@planetarium/9c-headless-provider/graphql/headless/api.graphql create mode 100644 integrations/javascript/@planetarium/9c-headless-provider/graphql/headless/codegen.yml create mode 100644 integrations/javascript/@planetarium/9c-headless-provider/graphql/headless/schema.graphql create mode 100644 integrations/javascript/@planetarium/9c-headless-provider/jsr.json create mode 100644 integrations/javascript/@planetarium/9c-headless-provider/package.json create mode 100644 integrations/javascript/@planetarium/9c-headless-provider/src/index.ts create mode 100644 integrations/javascript/@planetarium/9c-headless-provider/tsconfig.json create mode 100644 integrations/javascript/@planetarium/9c-headless-provider/tsup.config.ts create mode 100644 integrations/javascript/@planetarium/9c-headless-provider/vite.config.ts diff --git a/integrations/javascript/.vscode/settings.json b/integrations/javascript/.vscode/settings.json new file mode 100644 index 0000000000..f7f606b339 --- /dev/null +++ b/integrations/javascript/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "deno.enable": true +} diff --git a/integrations/javascript/@planetarium/9c-headless-provider/.gitignore b/integrations/javascript/@planetarium/9c-headless-provider/.gitignore new file mode 100644 index 0000000000..c83f90a2b0 --- /dev/null +++ b/integrations/javascript/@planetarium/9c-headless-provider/.gitignore @@ -0,0 +1 @@ +src/generated diff --git a/integrations/javascript/@planetarium/9c-headless-provider/LICENSE b/integrations/javascript/@planetarium/9c-headless-provider/LICENSE new file mode 100644 index 0000000000..be3f7b28e5 --- /dev/null +++ b/integrations/javascript/@planetarium/9c-headless-provider/LICENSE @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/integrations/javascript/@planetarium/9c-headless-provider/README.md b/integrations/javascript/@planetarium/9c-headless-provider/README.md new file mode 100644 index 0000000000..3e80d512db --- /dev/null +++ b/integrations/javascript/@planetarium/9c-headless-provider/README.md @@ -0,0 +1,5 @@ +# @planetarium/9c-headless-provider + +A library to provide `TxMetadataProvider` implementation for [NineChronicles.Headless][9c-headless]. + +[9c-headless]: https://github.com/planetarium/NineChronicles diff --git a/integrations/javascript/@planetarium/9c-headless-provider/biome.json b/integrations/javascript/@planetarium/9c-headless-provider/biome.json new file mode 100644 index 0000000000..b64575a36d --- /dev/null +++ b/integrations/javascript/@planetarium/9c-headless-provider/biome.json @@ -0,0 +1,21 @@ +{ + "$schema": "./node_modules/@biomejs/biome/configuration_schema.json", + "linter": { + "enabled": true, + "rules": { + "style": { + "useNodejsImportProtocol": "off" + } + } + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 2 + }, + "files": { + "ignore": [ + "src/generated" + ] + } +} diff --git a/integrations/javascript/@planetarium/9c-headless-provider/graphql/headless/api.graphql b/integrations/javascript/@planetarium/9c-headless-provider/graphql/headless/api.graphql new file mode 100644 index 0000000000..7370439be4 --- /dev/null +++ b/integrations/javascript/@planetarium/9c-headless-provider/graphql/headless/api.graphql @@ -0,0 +1,15 @@ +mutation StageTransaction($tx: String!) { + stageTransaction(payload: $tx) +} + +query GetNextNonce($address: Address!) { + nextTxNonce(address: $address) +} + +query GetGenesisHash { + nodeStatus { + genesis { + hash + } + } +} \ No newline at end of file diff --git a/integrations/javascript/@planetarium/9c-headless-provider/graphql/headless/codegen.yml b/integrations/javascript/@planetarium/9c-headless-provider/graphql/headless/codegen.yml new file mode 100644 index 0000000000..e416615e27 --- /dev/null +++ b/integrations/javascript/@planetarium/9c-headless-provider/graphql/headless/codegen.yml @@ -0,0 +1,9 @@ +overwrite: true +schema: ./graphql/headless/schema.graphql +documents: ./graphql/headless/api.graphql +generates: + src/generated/headless/graphql-request.ts: + plugins: + - typescript + - typescript-operations + - typescript-graphql-request diff --git a/integrations/javascript/@planetarium/9c-headless-provider/graphql/headless/schema.graphql b/integrations/javascript/@planetarium/9c-headless-provider/graphql/headless/schema.graphql new file mode 100644 index 0000000000..10598a237f --- /dev/null +++ b/integrations/javascript/@planetarium/9c-headless-provider/graphql/headless/schema.graphql @@ -0,0 +1,2804 @@ +schema { + query: StandaloneQuery + mutation: StandaloneMutation + subscription: StandaloneSubscription +} + +type StandaloneQuery { + stateQuery( + # Offset block hash for query. + hash: ByteString + ): StateQuery! + state( + # The hash of the block used to fetch state from chain. + hash: ByteString + + # The address of account to fetch from the chain. + accountAddress: Address! + + # The address of state to fetch from the account. + address: Address! + ): ByteString + transferNCGHistories( + blockHash: ByteString! + recipient: Address + ): [TransferNCGHistoryType!]! + keyStore: KeyStoreType + @deprecated( + reason: "Use `planet key` command instead. https://www.npmjs.com/package/@planetarium/cli" + ) + nodeStatus: NodeStatusType! + chainQuery: ExplorerQuery! @deprecated(reason: "Use /graphql/explorer") + + # The validation method provider for Libplanet types. + validation: ValidationQuery! + + # Check if the provided address is activated. + activationStatus: ActivationStatusQuery! + @deprecated(reason: "Since NCIP-15, it doesn't care account activation.") + + # Get the peer's block chain state + peerChainState: PeerChainStateQuery! + goldBalance( + # Target address to query + address: Address! + + # Offset block hash for query. + hash: ByteString + ): String! + nextTxNonce( + # Target address to query + address: Address! + ): Long! + @deprecated( + reason: "The root query is not the best place for nextTxNonce so it was moved. Use transaction.nextTxNonce()" + ) + getTx( + # transaction id. + txId: TxId! + ): TransactionType + @deprecated( + reason: "The root query is not the best place for getTx so it was moved. Use transaction.getTx()" + ) + + # Address of current node. + minerAddress: Address + + # Get monster collection status by address. + monsterCollectionStatus( + # agent address. + address: Address + ): MonsterCollectionStatusType + + # Query for transaction. + transaction: TransactionHeadlessQuery! + activated(invitationCode: String!): Boolean! + @deprecated(reason: "Since NCIP-15, it doesn't care account activation.") + activationKeyNonce(invitationCode: String!): String! + @deprecated(reason: "Since NCIP-15, it doesn't care account activation.") + + # Query for rpc mode information. + rpcInformation: RpcInformationQuery! + + # Query to create action transaction. + actionQuery: ActionQuery! + actionTxQuery( + # The hexadecimal string of public key for Transaction. + publicKey: String! + + # The nonce for Transaction. + nonce: Long + + # The time this transaction is created. + timestamp: DateTimeOffset + maxGasPrice: FungibleAssetValueInputType = { + quantity: 1000000000000000000 + ticker: "Mead" + decimalPlaces: 18 + minters: null + } + ): ActionTxQuery! + + # Query to get derived address. + addressQuery: AddressQuery! +} + +type StateQuery { + # State for avatar. + avatar( + # Address of avatar. + avatarAddress: Address! + ): AvatarStateType + + # Avatar states having some order as addresses + avatars( + # Addresses of avatars to query. + addresses: [Address!]! + ): [AvatarStateType]! + + # State for avatar EXP record. + rankingMap( + # RankingMapState index. 0 ~ 99 + index: Int! + ): RankingMapStateType + + # State for shop. + shop: ShopStateType + @deprecated( + reason: "Shop is migrated to ShardedShop and not using now. Use shardedShop() instead." + ) + + # State for sharded shop. + shardedShop( + # ItemSubType for shard. see from https://github.com/planetarium/lib9c/blob/main/Lib9c/Model/Item/ItemType.cs#L13 + itemSubType: ItemSubType! + + # Nonce for shard. It's not considered if itemSubtype is kind of costume or title. 0 ~ 15 + nonce: Int! + ): ShardedShopStateV2Type + + # State for weekly arena. + weeklyArena( + # WeeklyArenaState index. It increases every 56,000 blocks. + index: Int! + ): WeeklyArenaStateType + + # List of arena information of requested arena and avatar list + arenaInformation( + # Championship ID to get arena information + championshipId: Int! + + # Round of championship to get arena information + round: Int! + + # List of avatar address to get arena information + avatarAddresses: [Address!]! + ): [ArenaInformationType!]! + + # State for agent. + agent( + # Address of agent. + address: Address! + ): AgentStateType + + # State for staking. + stakeState( + # Address of agent who staked. + address: Address! + ): StakeStateType + + # Staking states having same order as addresses + stakeStates( + # Addresses of agent who staked. + addresses: [Address]! + ): [StakeStateType]! + + # State for monster collection. + monsterCollectionState( + # Address of agent. + agentAddress: Address! + ): MonsterCollectionStateType + monsterCollectionSheet: MonsterCollectionSheetType + + # The latest stake rewards based on StakePolicySheet. + latestStakeRewards: StakeRewardsType + stakeRewards: StakeRewardsType + @deprecated( + reason: "Since stake3, claim_stake_reward9 actions, each stakers have their own contracts." + ) + crystalMonsterCollectionMultiplierSheet: CrystalMonsterCollectionMultiplierSheetType + + # List of unlocked equipment recipe sheet row ids. + unlockedRecipeIds( + # Address of avatar. + avatarAddress: Address! + ): [Int] + + # List of unlocked world sheet row ids. + unlockedWorldIds( + # Address of avatar. + avatarAddress: Address! + ): [Int] + + # world boss season user information. + raiderState( + # address of world boss season. + raiderAddress: Address! + ): RaiderStateType + + # world boss season id by block index. + raidId( + blockIndex: Long! + + # find previous raid id. + prev: Boolean = false + ): Int! + + # world boss season boss information. + worldBossState(bossAddress: Address!): WorldBossStateType + + # user boss kill reward record by world boss season. + worldBossKillRewardRecord( + worldBossKillRewardRecordAddress: Address! + ): WorldBossKillRewardRecordType + + # asset balance by currency. + balance( + address: Address! + currency: CurrencyInput! + ): FungibleAssetValueWithCurrencyType! + + # raider address list by world boss season. + raiderList(raiderListAddress: Address!): [Address!] + orderDigestList(avatarAddress: Address!): OrderDigestListStateType + pledge(agentAddress: Address!): MeadPledgeType! + + # Get balances and fungible items in garages. + # Use either `currencyEnums` or `currencyTickers` to get balances. + garages( + # Agent address to get balances and fungible items in garages + agentAddr: Address! + + # List of currency enums to get balances in garages + currencyEnums: [CurrencyEnum!] + + # List of currency tickers to get balances in garages + currencyTickers: [String!] + + # List of fungible item IDs to get fungible item in garages + fungibleItemIds: [String!] + ): GaragesType + arenaParticipants( + avatarAddress: Address! + filterBounds: Boolean! = true + ): [ArenaParticipantType]! + cachedSheet(tableName: String!): String +} + +type AvatarStateType { + # Address of avatar. + address: Address! + + # Block index at the latest executed action. + blockIndex: Int! + + # Character ID from CharacterSheet. + characterId: Int! + + # Block index at the DailyReward execution. + dailyRewardReceivedIndex: Long! + + # Address of agent. + agentAddress: Address! + + # The index of this avatar state among its agent's avatar addresses. + index: Int! + + # Block index at the latest executed action. + updatedAt: Long! + + # Avatar name. + name: String! + + # Avatar total EXP. + exp: Int! + + # Avatar Level. + level: Int! + + # Current ActionPoint. + actionPoint: Int! + + # Index of ear color. + ear: Int! + + # Index of hair color. + hair: Int! + + # Index of eye color. + lens: Int! + + # Index of tail color. + tail: Int! + + # Avatar inventory. + inventory: InventoryType! + + # Avatar inventory address. + inventoryAddress: Address! + + # Rune list of avatar + runes: [RuneStateType!]! + + # Address list of combination slot. + combinationSlotAddresses: [Address!]! + + # Combination slots. + combinationSlots: [CombinationSlotStateType!]! + + # List of acquired item ID. + itemMap: CollectionMapType! + + # List of quest event ID. + eventMap: CollectionMapType! + + # List of defeated monster ID. + monsterMap: CollectionMapType! + + # List of cleared stage ID. + stageMap: CollectionMapType! + + # List of quest. + questList: QuestListType! + + # List of mail. + mailBox: MailBoxType! + + # World & Stage information. + worldInformation: WorldInformationType! +} + +scalar Address + +scalar Long + +type InventoryType { + # List of Consumables. + consumables: [ConsumableType!]! + + # List of Materials. + materials: [MaterialType!]! + + # List of Equipments. + equipments( + # filter equipped inventory item + equipped: Boolean + + # An item subtype for fetching only equipment where its subtype is the same. If it wasn't given, you'll get all equipment without relationship to the subtype. + itemSubType: ItemSubType + + # ItemIds for fetching only equipment where itemId (Guid) is in the given argument. + itemIds: [Guid!] + + # Ids for fetching only equipment where id (number) is in the given argument. + ids: [Int!] + ): [EquipmentType!]! + + # List of Costumes. + costumes: [CostumeType!]! + + # List of Inventory Item. + items( + # An Id to find Inventory Item + inventoryItemId: Int + + # filter locked Inventory Item + locked: Boolean + ): [InventoryItemType!]! +} + +type ConsumableType { + # Grade from ItemSheet. + grade: Int! + + # ID from ItemSheet. + id: Int! + + # Item category. + itemType: ItemType! + + # Item subcategory. + itemSubType: ItemSubType! + + # Item elemental. + elementalType: ElementalType! + requiredBlockIndex: Long + itemId: Guid! + mainStat: StatType! +} + +enum ItemType { + CONSUMABLE + COSTUME + EQUIPMENT + MATERIAL +} + +enum ItemSubType { + FOOD + FULL_COSTUME + HAIR_COSTUME + EAR_COSTUME + EYE_COSTUME + TAIL_COSTUME + WEAPON + ARMOR + BELT + NECKLACE + RING + EQUIPMENT_MATERIAL + FOOD_MATERIAL + MONSTER_PART + NORMAL_MATERIAL + HOURGLASS + AP_STONE + CHEST + @deprecated( + reason: "ItemSubType.Chest has never been used outside the MaterialItemSheet. And we won't use it in the future until we have a specific reason." + ) + TITLE + AURA +} + +enum ElementalType { + NORMAL + FIRE + WATER + LAND + WIND +} + +scalar Guid + +enum StatType { + NONE + HP + ATK + DEF + CRI + HIT + SPD + DRV + DRR + CDMG + ARMOR_PENETRATION + THORN +} + +type MaterialType { + # Grade from ItemSheet. + grade: Int! + + # ID from ItemSheet. + id: Int! + + # Item category. + itemType: ItemType! + + # Item subcategory. + itemSubType: ItemSubType! + + # Item elemental. + elementalType: ElementalType! + requiredBlockIndex: Long + itemId: ByteString! +} + +scalar ByteString + +type EquipmentType { + # Grade from ItemSheet. + grade: Int! + + # ID from ItemSheet. + id: Int! + + # Item category. + itemType: ItemType! + + # Item subcategory. + itemSubType: ItemSubType! + + # Item elemental. + elementalType: ElementalType! + requiredBlockIndex: Long + setId: Int! + stat: DecimalStatType! + equipped: Boolean! + itemId: Guid! + level: Int! + exp: Int! + skills: [SkillType] + buffSkills: [SkillType] + statsMap: StatsMapType! +} + +type DecimalStatType { + statType: StatType! + baseValue: Decimal! + additionalValue: Decimal! + totalValue: Decimal! +} + +scalar Decimal + +type SkillType { + id: Int! + elementalType: ElementalType! + power: Int! + chance: Int! + statPowerRatio: Int! + referencedStatType: StatType! +} + +type StatsMapType { + hP: Int! + aTK: Int! + dEF: Int! + cRI: Int! + hIT: Int! + sPD: Int! +} + +type CostumeType { + # Grade from ItemSheet. + grade: Int! + + # ID from ItemSheet. + id: Int! + + # Item category. + itemType: ItemType! + + # Item subcategory. + itemSubType: ItemSubType! + + # Item elemental. + elementalType: ElementalType! + requiredBlockIndex: Long + + # Guid of costume. + itemId: Guid! + + # Status of Avatar equipped. + equipped: Boolean! +} + +type InventoryItemType { + # A count of item + count: Int! + + # An Id of item + id: Int! + + # An ItemType of item + itemType: ItemType! + locked: Boolean! + lockId: Guid + tradableId: Guid + fungibleItemId: String +} + +type RuneStateType { + # ID of rune. + runeId: Int! + + # Level of this rune. + level: Int! +} + +type CombinationSlotStateType { + # Address of combination slot. + address: Address! + + # Block index at the combination slot can be usable. + unlockBlockIndex: Long! + + # Stage id at the combination slot unlock. + unlockStage: Int! + + # Block index at the combination started. + startBlockIndex: Long! + + # Pet id used in equipment + petId: Int +} + +type CollectionMapType { + count: Int! + pairs: [[Int]!]! +} + +type QuestListType { + completedQuestIds: [Int!]! +} + +type MailBoxType { + count: Int! + mails: [MailType!]! +} + +type MailType { + id: Guid! + requiredBlockIndex: Long! + blockIndex: Long! +} + +type WorldInformationType { + isStageCleared(stageId: Int!): Boolean! + isWorldUnlocked(worldId: Int!): Boolean! + world(worldId: Int!): WorldType! +} + +type WorldType { + id: Int! + name: String! + isUnlocked: Boolean! + isStageCleared: Boolean! + unlockedBlockIndex: Long! + stageClearedBlockIndex: Long! + stageBegin: Int! + stageEnd: Int! + stageClearedId: Int! +} + +type RankingMapStateType { + # Address of RankingMapState. + address: Address! + + # RankingMapState Capacity. + capacity: Int! + + # List of RankingInfo. + rankingInfos: [RankingInfoType!]! +} + +type RankingInfoType { + # Avatar total EXP. + exp: Long! + + # Avatar Level. + level: Int! + + # Equipped Armor ID from EquipmentItemSheet. + armorId: Int! + + # Block index at RankingInfo update. + updatedAt: Long! + + # Block index at Latest stage cleared. + stageClearedBlockIndex: Long! + + # Address of agent. + agentAddress: Address! + + # Address of avatar. + avatarAddress: Address! + + # Avatar name. + avatarName: String! +} + +type ShopStateType { + # Address of shop. + address: Address! + + # List of ShopItem. + products( + # Filter for item id. + id: Int + + # Filter for ItemSubType. see from https://github.com/planetarium/lib9c/blob/main/Lib9c/Model/Item/ItemType.cs#L13 + itemSubType: ItemSubType + + # Filter for item maximum price. + maximumPrice: Int + ): [ShopItemType]! +} + +type ShopItemType { + # Address of seller agent. + sellerAgentAddress: Address! + + # Address of seller avatar. + sellerAvatarAddress: Address! + + # Guid of product registered. + productId: Guid! + + # Item price. + price: String! + + # Equipment / Consumable information. + itemUsable: ItemUsableType + + # Costume information. + costume: CostumeType +} + +type ItemUsableType { + # Grade from ItemSheet. + grade: Int! + + # ID from ItemSheet. + id: Int! + + # Item category. + itemType: ItemType! + + # Item subcategory. + itemSubType: ItemSubType! + + # Item elemental. + elementalType: ElementalType! + requiredBlockIndex: Long + + # Guid of item. + itemId: Guid! +} + +type ShardedShopStateV2Type { + # Address of sharded shop. + address: Address! + + # List of OrderDigest. + orderDigestList( + # Filter for item id. + id: Int + + # Filter for item maximum price. + maximumPrice: Int + ): [OrderDigestType]! +} + +type OrderDigestType { + # Guid of order. + orderId: Guid! + + # Tradable guid of order. + tradableId: Guid! + + # Block index order started. + startedBlockIndex: Int! + + # Block index order expired. + expiredBlockIndex: Int! + + # Address of seller agent. + sellerAgentAddress: Address! + + # Order price. + price: String! + combatPoint: Int! + level: Int! + + # Id of item. + itemId: Int! + + # Count of item. + itemCount: Int! +} + +type WeeklyArenaStateType { + address: Address! + ended: Boolean! + orderedArenaInfos: [ArenaInfoType]! +} + +type ArenaInfoType { + agentAddress: Address! + avatarAddress: Address! + avatarName: String! + arenaRecord: ArenaRecordType! + active: Boolean! + dailyChallengeCount: Int! + score: Int! +} + +type ArenaRecordType { + win: Int + lose: Int + draw: Int +} + +type ArenaInformationType { + avatarAddress: Address! + address: Address! + win: Int! + lose: Int! + ticket: Int! + ticketResetCount: Int! + purchasedTicketCount: Int! + score: Int! +} + +type AgentStateType { + # Address of agent. + address: Address! + + # List of avatar. + avatarStates: [AvatarStateType!] + + # Current NCG. + gold: String! + + # Monster collection round of agent. + monsterCollectionRound: Long! + + # Current monster collection level. + monsterCollectionLevel: Long! + hasTradedItem: Boolean! + + # Current CRYSTAL. + crystal: String! + + # mead pledge information. + pledge: MeadPledgeType! +} + +type MeadPledgeType { + patronAddress: Address + approved: Boolean! + mead: Int! +} + +type StakeStateType { + # The address of current state. + address: Address! + + # The staked amount. + deposit: String! + + # The block index the user started to stake. + startedBlockIndex: Long! + + # The block index the user received rewards. + receivedBlockIndex: Long! + + # The block index the user can cancel the staking. + cancellableBlockIndex: Long! + + # The block index the user can claim rewards. + claimableBlockIndex: Long! + + # The staking achievements. + achievements: StakeAchievementsType + @deprecated(reason: "Since StakeStateV2, the achievement became removed.") + stakeRewards: StakeRewardsType! +} + +type StakeAchievementsType { + # The address of current state. + achievementsByLevel(level: Int!): Int! +} + +type StakeRewardsType { + orderedList: [StakeRegularRewardsType!]! +} + +type StakeRegularRewardsType { + level: Int! + requiredGold: Long! + rewards: [StakeRegularRewardInfoType!]! + bonusRewards: [StakeRegularFixedRewardInfoType!]! +} + +type StakeRegularRewardInfoType { + itemId: Int! + rate: Int! + type: StakeRewardType! + currencyTicker: String + currencyDecimalPlaces: Int + decimalRate: Decimal! +} + +enum StakeRewardType { + ITEM + RUNE + CURRENCY +} + +type StakeRegularFixedRewardInfoType { + itemId: Int! + count: Int! +} + +type MonsterCollectionStateType { + address: Address! + level: Long! + expiredBlockIndex: Long! + startedBlockIndex: Long! + receivedBlockIndex: Long! + rewardLevel: Long! + claimableBlockIndex: Long! +} + +type MonsterCollectionSheetType { + orderedList: [MonsterCollectionRowType] +} + +type MonsterCollectionRowType { + level: Int! + requiredGold: Int! + rewards: [MonsterCollectionRewardInfoType]! +} + +type MonsterCollectionRewardInfoType { + itemId: Int! + quantity: Int! +} + +type CrystalMonsterCollectionMultiplierSheetType { + orderedList: [CrystalMonsterCollectionMultiplierRowType!]! +} + +type CrystalMonsterCollectionMultiplierRowType { + level: Int! + multiplier: Int! +} + +type RaiderStateType { + # season total score. + totalScore: Int! + + # season high score. + highScore: Int! + + # season total challenge count. + totalChallengeCount: Int! + + # remain challenge count before refill. + remainChallengeCount: Int! + + # latest reward claimed season rank. + latestRewardRank: Int! + + # challenge ticket purchase count. + purchaseCount: Int! + + # combat point of avatar state. + cp: Int! + + # level of avatar state. + level: Int! + + # icon id for ranking portrait. + iconId: Int! + + # latest challenge boss level. + latestBossLevel: Int! + + # rank reward claimed block index. + claimedBlockIndex: Long! + + # ticket refilled block index. + refillBlockIndex: Long! + + # address of avatar state. + avatarAddress: Address! + + # name of avatar state. + avatarName: String! +} + +type WorldBossStateType { + # world boss season id. + id: Int! + + # world boss current level. + level: Int! + + # world boss current hp. + currentHp: BigInt! + + # world boss season started block index. + startedBlockIndex: Long! + + # world boss season ended block index. + endedBlockIndex: Long! +} + +scalar BigInt + +type WorldBossKillRewardRecordType { + map: [WorldBossKillRewardRecordMapType!]! +} + +type WorldBossKillRewardRecordMapType { + bossLevel: Int! + + # check reward already claimed. if already claimed return true. + claimed: Boolean! +} + +type FungibleAssetValueWithCurrencyType { + currency: CurrencyType! + quantity(minerUnit: Boolean = false): String! +} + +type CurrencyType { + ticker: String! + decimalPlaces: Byte! + minters: [Address] +} + +scalar Byte + +input CurrencyInput { + # The ticker symbol, e.g., USD. + ticker: String! + + # The number of digits to treat as minor units (i.e., exponents). + decimalPlaces: Byte! + + # The addresses who can mint this currency. If this is null anyone can mint the currency. On the other hand, unlike null, an empty set means no one can mint the currency. + minters: [Address!] + maximumSupplyMajorUnit: BigInt + maximumSupplyMinorUnit: BigInt + + # Whether the total supply of this currency is trackable. + totalSupplyTrackable: Boolean +} + +type OrderDigestListStateType { + address: Address + orderDigestList: [OrderDigestType]! +} + +type GaragesType { + agentAddr: Address + garageBalancesAddr: Address + garageBalances: [FungibleAssetValue] + fungibleItemGarages: [FungibleItemGarageWithAddressType] +} + +# Holds a fungible asset value which holds its currency together. +type FungibleAssetValue { + # The currency of the fungible asset. + currency: Currency! + + # Gets a number that indicates the sign (-1: negative, 1: positive, or 0: zero) of the value. + sign: Int! + majorUnit: BigInt! + minorUnit: BigInt! + + # The value quantity without its currency in string, e.g., "123.45". + quantity: String! + + # The value quantity with its currency in string, e.g., "123.45 ABC". + string: String! +} + +type Currency { + # The ticker symbol, e.g., USD. + ticker: String! + + # The number of digits to treat as minor units (i.e., exponents). + decimalPlaces: Byte! + + # The addresses who can mint this currency. If this is null anyone can mint the currency. On the other hand, unlike null, an empty set means no one can mint the currency. + minters: [Address!] + + # The uppermost quantity of currency allowed to exist. null means unlimited supply. + maximumSupply: FungibleAssetValue + + # Whether the total supply of this currency is trackable. + totalSupplyTrackable: Boolean! + + # The deterministic hash derived from other fields. + hash: ByteString! +} + +type FungibleItemGarageWithAddressType { + fungibleItemId: String + addr: Address + item: FungibleItemType + count: Int +} + +type FungibleItemType { + # Item category. + itemType: ItemType! + + # Item sub category. + itemSubType: ItemSubType! + fungibleItemId: String! +} + +# The currency type. +enum CurrencyEnum { + CRYSTAL + NCG + GARAGE + MEAD +} + +type ArenaParticipantType { + # Address of avatar. + avatarAddr: Address! + + # Arena score of avatar. + score: Int! + + # Arena rank of avatar. + rank: Int! + + # Score for victory. + winScore: Int! + + # Score for defeat. + loseScore: Int! + + # Cp of avatar. + cp: Int! + + # Portrait icon id. + portraitId: Int! + + # Level of avatar. + level: Int! + + # Name of avatar. + nameWithHash: String! +} + +type TransferNCGHistoryType { + blockHash: ByteString! + txId: ByteString! + sender: Address! + recipient: Address! + amount: String! + memo: String +} + +type KeyStoreType { + protectedPrivateKeys: [ProtectedPrivateKeyType!]! + decryptedPrivateKey(address: Address!, passphrase: String!): ByteString! + + # An API to provide conversion to public-key, address. + privateKey( + # A representation of public-key with hexadecimal format. + hex: ByteString! + ): PrivateKeyType! +} + +type ProtectedPrivateKeyType { + address: Address! +} + +type PrivateKeyType { + # A representation of private-key with hexadecimal format. + hex: ByteString! + + # A public-key derived from the private-key. + publicKey: PublicKeyType! +} + +type PublicKeyType { + # A representation of public-key with hexadecimal format. + hex( + # A flag to determine whether to compress public-key. + compress: Boolean + ): ByteString! + + # An address derived from the public-key. + address: Address! +} + +type NodeStatusType { + # Whether the current libplanet node has ended bootstrapping. + bootstrapEnded: Boolean! + + # Whether the current libplanet node has ended preloading. + preloadEnded: Boolean! + + # Block header of the tip block from the current canonical chain. + tip: BlockHeader! + + # The topmost blocks from the current node. + topmostBlocks( + # The number of blocks to get. + limit: Int! + + # The number of blocks to skip from tip. + offset: Int = 0 + + # List only blocks mined by the given address. (List everything if omitted.) + miner: Address + ): [BlockHeader]! + + # Ids of staged transactions from the current node. + stagedTxIds( + # Target address to query + address: Address + ): [TxId] + + # The number of ids of staged transactions from the current node. + stagedTxIdsCount: Int + + # Block header of the genesis block from the current chain. + genesis: BlockHeader! + + # Whether the current node is mining. + isMining: Boolean! + appProtocolVersion: AppProtocolVersionType + + # A list of subscribers' address + subscriberAddresses: [Address] + + # The number of a list of subscribers' address + subscriberAddressesCount: Int + + # A version of NineChronicles.Headless + productVersion: String + + # A informational version (a.k.a. version suffix) of NineChronicles.Headless + informationalVersion: String +} + +type BlockHeader { + index: Int! + id: ID! + hash: String! + miner: Address +} + +scalar TxId + +type AppProtocolVersionType { + version: Int! + signer: Address! + signature: ByteString! + extra: ByteString +} + +type ExplorerQuery { + blockQuery: BlockQuery + transactionQuery: TransactionQuery + stateQuery: LibplanetStateQuery + nodeState: NodeState! + helperQuery: HelperQuery +} + +type BlockQuery { + blocks( + desc: Boolean = false + offset: Int = 0 + limit: Int + excludeEmptyTxs: Boolean = false + miner: Address + ): [Block!]! + block(hash: ID, index: ID): Block +} + +type Block { + # A block's hash. + hash: ID! + + # The height of the block. + index: Long! + + # The address of the miner. + miner: Address! + + # The public key of the Miner. + publicKey: PublicKey + + # The previous block. If it's a genesis block (i.e., its index is 0) this must be null. + previousBlock: Block + timestamp: DateTimeOffset! + + # The hash of the resulting states after evaluating transactions and a block action (if exists) + stateRootHash: ByteString! + + # The digital signature of the whole block content (except for hash, which is derived from the signature and other contents) + signature: ByteString + + # Transactions belonging to the block. + transactions: [Transaction!]! + + # The LastCommit of the block. + lastCommit: BlockCommit + + # The mining difficulty that the block's nonce has to satisfy. + difficulty: Long! + @deprecated(reason: "Block does not have Difficulty field in PBFT.") + + # The total mining difficulty since the genesis including the block's difficulty. + totalDifficulty: BigInt! + @deprecated(reason: "Block does not have TotalDifficulty field in PBFT.") + + # The proof-of-work nonce which satisfies the required difficulty. + nonce: ByteString! + @deprecated(reason: "Block does not have Nonce field in PBFT.") + + # The hash of PreEvaluationBlock. + preEvaluationHash: ByteString! +} + +scalar PublicKey + +# The `DateTimeOffset` scalar type represents a date, time and offset from UTC. `DateTimeOffset` expects timestamps to be formatted in accordance with the [ISO-8601](https://en.wikipedia.org/wiki/ISO_8601) standard. +scalar DateTimeOffset + +type Transaction { + # A unique identifier derived from this transaction content. + id: ID! + + # The number of previous transactions committed by the signer of this tx. + nonce: Long! + + # An address of the account who signed this transaction. + signer: Address! + + # A PublicKey of the account who signed this transaction. + publicKey: ByteString! + + # Addresses whose states were affected by Actions. + updatedAddresses: [Address!]! + + # A digital signature of the content of this transaction. + signature: ByteString! + + # The time this transaction was created and signed. + timestamp: DateTimeOffset! + + # A list of actions in this transaction. + actions: [Action!]! + + # A serialized tx payload in base64 string. + serializedPayload: String! + + # The block including the transaction. + blockRef: Block! +} + +type Action { + # Raw Action data ('hex' or 'base64' encoding available.) + raw(encode: String = "hex"): String! + + # A readable representation for debugging. + inspection: String! + + # A JSON representation of action data + json: String! +} + +type BlockCommit { + # The height of the block commit. + height: Long! + + # The round of the block commit. + round: Int! + + # The hash of the block which contains block commit. + blockHash: ID! + + # Total votes of the block commit. + votes: [Vote!]! +} + +type Vote { + # Height of the consensus voted. + height: Long! + + # Round of the consensus voted. + round: Int! + + # Hash of the block voted. + blockHash: String! + + # The time this vote was created and signed. + timestamp: DateTimeOffset! + + # Public key of the validator which is subject of the vote. + validatorPublicKey: PublicKey! + + # Flag of the vote + flag: VoteFlag! + + # A digital signature of the content of this vote. + signature: ByteString! +} + +scalar VoteFlag + +type TransactionQuery { + transactions( + signer: Address + involvedAddress: Address + desc: Boolean = false + offset: Int = 0 + limit: Int + ): [Transaction!]! + stagedTransactions( + signer: Address + involvedAddress: Address + desc: Boolean = false + offset: Int = 0 + limit: Int + ): [Transaction!]! + transaction(id: ID): Transaction + unsignedTransaction( + # The hexadecimal string of public key for Transaction. + publicKey: String! + + # The hexadecimal string of plain value for Action. + plainValue: String! + + # The nonce for Transaction. + nonce: Long + ): ByteString! + nextNonce( + # Address of the account to get the next tx nonce. + address: Address! + ): Long! + + # Attach the given signature to the given transaction and return tx as hexadecimal + bindSignature( + # The hexadecimal string of unsigned transaction to attach the given signature. + unsignedTransaction: String! + + # The hexadecimal string of the given unsigned transaction. + signature: String! + ): String! + transactionResult( + # transaction id. + txId: ID! + ): TxResultType! +} + +type TxResultType { + # The transaction status. + txStatus: TxStatus! + + # The block index which the target transaction executed. + blockIndex: Long + + # The block hash which the target transaction executed. + blockHash: String + + # The input state's root hash which the target transaction executed. + inputState: HashDigest_SHA256 + + # The output state's root hash which the target transaction executed. + outputState: HashDigest_SHA256 + + # The name of exception. (when only failed) + exceptionNames: [String] +} + +enum TxStatus { + INVALID + STAGING + SUCCESS + FAILURE + INCLUDED +} + +scalar HashDigest_SHA256 + +type LibplanetStateQuery { + world(blockHash: BlockHash, stateRootHash: HashDigestSHA256): WorldState! + + # Retrieves states from the legacy account. + states( + addresses: [Address!]! + offsetBlockHash: ID + offsetStateRootHash: HashDigest_SHA256 + ): [BencodexValue]! + + # Retrieves balance from the legacy account. + balance( + owner: Address! + currency: CurrencyInput! + offsetBlockHash: ID + offsetStateRootHash: HashDigest_SHA256 + ): FungibleAssetValue! + + # Retrieves total supply from the legacy account. + totalSupply( + currency: CurrencyInput! + offsetBlockHash: ID + offsetStateRootHash: HashDigest_SHA256 + ): FungibleAssetValue + + # Retrieves validator set from the legacy account. + validators( + offsetBlockHash: ID + offsetStateRootHash: HashDigest_SHA256 + ): [Validator!] +} + +type WorldState { + # The state root hash of the world state. + stateRootHash: HashDigestSHA256! + + # The legacy flag of the world state. + legacy: Boolean! + + # Gets the account associated with given address. + account( + # The address of an account to retrieve. + address: Address! + ): AccountState! + + # Gets the accounts associated with given addresses. + accounts( + # The list of addresses of accounts to retrieve. + addresses: [Address!]! + ): [AccountState!]! +} + +scalar HashDigestSHA256 + +# Represents a raw account state. This is meant to represent a raw storage state void of any application layer context and/or logic. In particular, this does not deal with currency or fungible asset value directly, which requires additional information on currency such as its ticker and possible minters, etc. while interpreting the data retrieved with the provided contextual information. The same is true for validator sets. +type AccountState { + # The state root hash associated with this account state. + stateRootHash: HashDigestSHA256! + + # The state at given address. + state( + # The address to look up. + address: Address! + ): IValue + + # The states at given addresses. + states( + # The list of addresses to look up. + addresses: [Address!]! + ): [IValue]! + + # Balance at given address and currency hash pair. + balance( + # The address to look up. + address: Address! + + # The currency hash to look up. + currencyHash: HashDigestSHA1! + ): IValue + + # Balances at given addresses and currency hash pair. + balances( + # The list of addresses to look up. + addresses: [Address!]! + + # The currency hash to look up. + currencyHash: HashDigestSHA1! + ): [IValue]! + + # Total supply in circulation, if recorded, for given currency hash. + totalSupply( + # The currency hash to look up. + currencyHash: HashDigestSHA1! + ): IValue + + # The validator set. + validatorSet: IValue +} + +type IValue { + # A hexadecimal representation of the bencodex value encoded as byte array. + hex: String! + + # A base64 representation of the bencodex value encoded to byte array. + base64: String! + + # A human readable representation of the bencodex value. + inspection: String! + + # A JSON representation of the bencodex value + json: String! +} + +scalar HashDigestSHA1 + +scalar BlockHash + +scalar BencodexValue + +# A data type holds validator's public key and its voting power. +type Validator { + # The public key of the validator. + publicKey: PublicKey! + + # Gets the voting power of the validator. + power: BigInt! +} + +type NodeState { + preloaded: Boolean! + peers: [BoundPeer!] + validators: [BoundPeer!] +} + +type BoundPeer { + # The public key of the peer. + publicKey: PublicKey! + + # The endpoint of the peer. + endPoint: String! + + # The address of the miner. + publicIpAddress: String +} + +# A number of queries for convenience. +type HelperQuery { + # Converts currency info to currency hash. + currencyHash( + # The currency to convert. + currency: CurrencyInput! + ): HashDigestSHA1! + + # Decodes hex encoded bencodex value + bencodexValue( + # The byte array in hex representation to decode. + hex: String! + ): IValue! +} + +type ValidationQuery { + metadata( + # The raw value of json metadata. + raw: String! + ): Boolean! + privateKey( + # The raw value of private-key, presented as hexadecimal. + hex: ByteString! + ): Boolean! + publicKey( + # The raw value of public-key, presented as hexadecimal. + hex: ByteString! + ): Boolean! +} + +type ActivationStatusQuery { + activated: Boolean! + @deprecated(reason: "Since NCIP-15, it doesn't care account activation.") + addressActivated(address: Address!): Boolean! + @deprecated(reason: "Since NCIP-15, it doesn't care account activation.") +} + +type PeerChainStateQuery { + # Summary of other peers connected to this node. It consists of address, chain height, and total difficulty. + state: [String]! +} + +type TransactionType { + # A unique identifier derived from this transaction content. + id: TxId! + + # The number of previous transactions committed by the signer of this transaction. + nonce: Long! + + # A PublicKey of the account who signed this transaction. + publicKey: PublicKeyType! + + # A digital signature of the content of this transaction. + signature: ByteString! + + # An address of the account who signed this transaction. + signer: Address! + + # The time this transaction was created and signed. + timestamp: String! + + # Addresses whose states were affected by Actions. + updatedAddresses: [Address]! + + # A list of actions in this transaction. + actions: [Action]! + + # A serialized tx payload in base64 string. + serializedPayload: String! +} + +type MonsterCollectionStatusType { + fungibleAssetValue: FungibleAssetValueType! + rewardInfos: [MonsterCollectionRewardInfoType] + tipIndex: Long! + lockup: Boolean! +} + +type FungibleAssetValueType { + currency: String! + quantity: String! +} + +type TransactionHeadlessQuery { + nextTxNonce( + # Target address to query + address: Address! + ): Long! + getTx( + # transaction id. + txId: TxId! + ): TransactionType + ncTransactions( + # start block index for query tx. + startingBlockIndex: Long! + + # number of block to query. + limit: Long! + + # filter tx by having actions' type. It is regular expression. + actionType: String! + ): [TransactionType] + createUnsignedTx( + # The base64-encoded public key for Transaction. + publicKey: String! + + # The base64-encoded plain value of action for Transaction. + plainValue: String! + + # The nonce for Transaction. + nonce: Long + ): String! + @deprecated(reason: "API update with action query. use unsignedTransaction") + attachSignature( + # The base64-encoded unsigned transaction to attach the given signature. + unsignedTransaction: String! + + # The base64-encoded signature of the given unsigned transaction. + signature: String! + ): String! @deprecated(reason: "Use signTransaction") + transactionResult( + # transaction id. + txId: TxId! + ): TxResultType! + transactionResults( + # transaction ids. + txIds: [TxId]! + ): [TxResultType]! + unsignedTransaction( + # The hexadecimal string of public key for Transaction. + publicKey: String! + + # The hexadecimal string of plain value for Action. + plainValue: String! + + # The nonce for Transaction. + nonce: Long + maxGasPrice: FungibleAssetValueInputType = { + quantity: 1000000000000000000 + ticker: "Mead" + decimalPlaces: 18 + minters: null + } + ): ByteString! + signTransaction( + # The hexadecimal string of unsigned transaction to attach the given signature. + unsignedTransaction: String! + + # The hexadecimal string of signature of the given unsigned transaction. + signature: String! + ): ByteString! +} + +input FungibleAssetValueInputType { + quantity: BigInt! + ticker: String! + decimalPlaces: Byte! + minters: [Address!] +} + +type RpcInformationQuery { + # total count by connected to this node. + totalCount: Int! + + # List of address connected to this node. + clients: [Address]! + + # total count by connected to this node. + totalCountByDevice(device: String!): Int! + + # clients connected to this node by device. + clientsByDevice(device: String!): [Address!]! + + # clients count connected to this node grouped by Ip addresses. + clientsCountByIps(minimum: Int!): Int! + + # clients connected to this node grouped by Ip addresses. + clientsByIps(minimum: Int!): [MultiAccountInfo]! +} + +type MultiAccountInfo { + key: String! + ips: [String] + agents: [String] + ipsCount: Int + agentsCount: Int +} + +type ActionQuery { + stake( + # An amount to stake. + amount: BigInt + ): ByteString + claimStakeReward( + # The avatar address to receive staking rewards. + avatarAddress: Address + ): ByteString + migrateMonsterCollection( + # The avatar address to receive monster collection rewards. + avatarAddress: Address + ): ByteString! + grinding( + # Address of avatar. + avatarAddress: Address! + + # List of equipment ItemId. + equipmentIds: [Guid]! + + # Flag to Charge Action Point. + chargeAp: Boolean + ): ByteString + unlockEquipmentRecipe( + # Address of avatar. + avatarAddress: Address! + + # List of EquipmentRecipeSheet row ids to unlock. + recipeIds: [Int]! + ): ByteString + unlockWorld( + # Address of avatar. + avatarAddress: Address! + + # List of WorldUnlockSheet row world_id_to_unlock. + worldIds: [Int]! + ): ByteString + transferAsset( + # Address of sender. + sender: Address! + + # Address of recipient. + recipient: Address! + + # A string value to be transferred. + amount: String! + + # A enum value of currency to be transferred. + currency: CurrencyEnum + + # A currency to be transferred. + rawCurrency: CurrencyInput + + # A 80-max length string to note. + memo: String + ): ByteString + patchTableSheet( + # name of table sheet. + tableName: String! + + # table data. + tableCsv: String! + ): ByteString! + raid( + # address of avatar state. + avatarAddress: Address! + + # list of equipment id. + equipmentIds: [Guid] = [] + + # list of costume id. + costumeIds: [Guid] = [] + + # list of food id. + foodIds: [Guid] = [] + + # refill ticket by NCG. + payNcg: Boolean = false + + # list of rune slot + runeSlotInfos: [RuneSlotInfoInputType!] = [] + ): ByteString! + claimRaidReward( + # address of avatar state to receive reward. + avatarAddress: Address! + ): ByteString! + claimWorldBossKillReward( + # address of avatar state to receive reward. + avatarAddress: Address! + ): ByteString! + prepareRewardAssets( + # address of reward pool for charge reward. + rewardPoolAddress: Address! + + # list of FungibleAssetValue for charge reward. + assets: [FungibleAssetValueInputType!]! + ): ByteString! + transferAssets( + # Address of sender. + sender: Address! + + # List of tuples that recipients' address and asset amount to be sent + recipients: [RecipientsInputType!]! + + # A 80-max length string to note. + memo: String + ): ByteString! + activateAccount( + # Activation code that you've get. + activationCode: String! + ): ByteString! + @deprecated(reason: "Since NCIP-15, it doesn't care account activation.") + createAvatar( + # index of avatar in `AgentState.avatarAddresses`.(0~2) + index: Int! + + # name of avatar.(2~20 characters) + name: String! + + # hair index of avatar. + hair: Int = 0 + + # lens index of avatar. + lens: Int = 0 + + # ear index of avatar. + ear: Int = 0 + + # tail index of avatar. + tail: Int = 0 + ): ByteString! + runeEnhancement( + # The avatar address to enhance rune. + avatarAddress: Address! + + # Rune ID to enhance. + runeId: Int! + + # The try count to enhance rune + tryCount: Int + ): ByteString! + hackAndSlash( + # Avatar address. + avatarAddress: Address! + + # World ID containing the stage ID. + worldId: Int! + + # Stage ID. + stageId: Int! + + # List of costume id for equip. + costumeIds: [Guid] + + # List of equipment id for equip. + equipmentIds: [Guid] + + # List of consumable id for use. + consumableIds: [Guid] + + # List of rune slot info for equip. + runeSlotInfos: [RuneSlotInfoInputType!] = [] + + # Buff ID for this stage + stageBuffId: Int + ): ByteString! + hackAndSlashSweep( + # Avatar address. + avatarAddress: Address! + + # World ID containing the stage ID. + worldId: Int! + + # Stage ID. + stageId: Int! + + # List of costume id for equip. + costumeIds: [Guid] + + # List of equipment id for equip. + equipmentIds: [Guid] + + # List of rune slot info for equip. + runeSlotInfos: [RuneSlotInfoInputType!] = [] + + # Action point usage to sweep + actionPoint: Int! + + # AP stone usage to sweep + apStoneCount: Int + ): ByteString! + dailyReward( + # Avatar address to get daily reward + avatarAddress: Address! + ): ByteString! + combinationEquipment( + # Avatar address to combine equipment + avatarAddress: Address! + + # Slot index to combine equipment + slotIndex: Int! + + # Combination recipe ID + recipeId: Int! + + # Sub-recipe ID of this combination + subRecipeId: Int + + # Pay crystal co combine equipment? + payByCrystal: Boolean + + # Use hammer point to combine equipment? + useHammerPoint: Boolean + ): ByteString! + itemEnhancement( + # Avatar address to enhance item + avatarAddress: Address! + + # Slot index to enhance item + slotIndex: Int! + + # Target item ID to enhance + itemId: Guid! + + # Material IDs to enhance + materialIds: [Guid!]! + ): ByteString! + rapidCombination( + # Avatar address to execute rapid combination + avatarAddress: Address! + + # Slot index to execute rapid + slotIndex: Int! + ): ByteString! + combinationConsumable( + # Avatar address to combine consumable + avatarAddress: Address! + + # Slot index to combine + slotIndex: Int! + + # Recipe ID to combine consumable + recipeId: Int! + ): ByteString! + requestPledge(agentAddress: Address!, mead: Int = 4): ByteString! + approvePledge(patronAddress: Address!): ByteString! + endPledge(agentAddress: Address!): ByteString! + createPledge( + patronAddress: Address! + agentAddresses: [Address!]! + mead: Int = 4 + ): ByteString! + loadIntoMyGarages( + # Array of balance address and currency ticker and quantity. + fungibleAssetValues: [BalanceInput!] + + # Inventory Address + inventoryAddr: Address + + # Array of fungible ID and count + fungibleIdAndCounts: [FungibleIdAndCountInput!] + + # Memo + memo: String + ): ByteString! + deliverToOthersGarages( + # Recipient agent address + recipientAgentAddr: Address! + + # Array of currency ticket and quantity to deliver. + fungibleAssetValues: [SimplifyFungibleAssetValueInput!] + + # Array of Fungible ID and count to deliver. + fungibleIdAndCounts: [FungibleIdAndCountInput!] + + # Memo + memo: String + ): ByteString! + unloadFromMyGarages( + # Recipient avatar address + recipientAvatarAddr: Address! + + # Array of balance address and currency ticker and quantity to send. + fungibleAssetValues: [BalanceInput!] + + # Array of fungible ID and count to send. + fungibleIdAndCounts: [FungibleIdAndCountInput!] + + # Memo + memo: String + ): ByteString! + auraSummon( + # Avatar address to get summoned items + avatarAddress: Address! + + # Summon group id + groupId: Int! + + # Count to summon. Must between 1 and 10. + summonCount: Int! + ): ByteString! + runeSummon( + # Avatar address to get summoned items + avatarAddress: Address! + + # Summon group id + groupId: Int! + + # Count to summon. Must between 1 and 10. + summonCount: Int! + ): ByteString! + claimItems( + # List of pair of avatar address, List to claim. + claimData: [ClaimDataInputType!]! + + # Memo to attach to this action. + memo: String + ): ByteString! + + # Query to craft/enhance items/foods + craftQuery: CraftQuery! +} + +input RuneSlotInfoInputType { + slotIndex: Int! + runeId: Int! +} + +input RecipientsInputType { + recipient: Address! + amount: FungibleAssetValueInputType! +} + +input BalanceInput { + # Balance Address. + balanceAddr: Address + + # Fungible asset value ticker and amount. + value: SimplifyFungibleAssetValueInput +} + +# A fungible asset value ticker and amount.You can specify either currencyEnum or currencyTicker. +input SimplifyFungibleAssetValueInput { + # A currency type to be loaded. + currencyEnum: CurrencyEnum + + # A currency ticker to be loaded. + currencyTicker: String + + # A numeric string to parse. Can consist of digits, plus (+), minus (-), and decimal separator (.). + value: String! +} + +input FungibleIdAndCountInput { + # Fungible ID + fungibleId: String! + + # Count + count: Int! +} + +input ClaimDataInputType { + avatarAddress: Address! + fungibleAssetValues: [FungibleAssetValueInputType!]! +} + +type CraftQuery { + eventConsumableItemCrafts( + # Avatar address to craft event item + avatarAddress: Address! + + # The ID of event schedule + eventScheduleId: Int! + + # Recipe ID of event item to craft + eventConsumableItemRecipeId: Int! + + # Target slot index to craft item + slotIndex: Int! + ): ByteString! + eventMaterialItemCrafts( + # Avatar address to craft item + avatarAddress: Address! + + # The ID of event schedule + eventScheduleId: Int! + + # Recipe ID of event item to craft + eventMaterialItemRecipeId: Int! + + # Materials to be used to craft + materialsToUse: [MaterialsToUseInputType!]! + ): ByteString! +} + +input MaterialsToUseInputType { + # Material ID to be used. + materialId: Int! + + # Item quantity to be used. + quantity: Int! +} + +type ActionTxQuery { + stake( + # An amount to stake. + amount: BigInt + ): ByteString + claimStakeReward( + # The avatar address to receive staking rewards. + avatarAddress: Address + ): ByteString + migrateMonsterCollection( + # The avatar address to receive monster collection rewards. + avatarAddress: Address + ): ByteString! + grinding( + # Address of avatar. + avatarAddress: Address! + + # List of equipment ItemId. + equipmentIds: [Guid]! + + # Flag to Charge Action Point. + chargeAp: Boolean + ): ByteString + unlockEquipmentRecipe( + # Address of avatar. + avatarAddress: Address! + + # List of EquipmentRecipeSheet row ids to unlock. + recipeIds: [Int]! + ): ByteString + unlockWorld( + # Address of avatar. + avatarAddress: Address! + + # List of WorldUnlockSheet row world_id_to_unlock. + worldIds: [Int]! + ): ByteString + transferAsset( + # Address of sender. + sender: Address! + + # Address of recipient. + recipient: Address! + + # A string value to be transferred. + amount: String! + + # A enum value of currency to be transferred. + currency: CurrencyEnum + + # A currency to be transferred. + rawCurrency: CurrencyInput + + # A 80-max length string to note. + memo: String + ): ByteString + patchTableSheet( + # name of table sheet. + tableName: String! + + # table data. + tableCsv: String! + ): ByteString! + raid( + # address of avatar state. + avatarAddress: Address! + + # list of equipment id. + equipmentIds: [Guid] = [] + + # list of costume id. + costumeIds: [Guid] = [] + + # list of food id. + foodIds: [Guid] = [] + + # refill ticket by NCG. + payNcg: Boolean = false + + # list of rune slot + runeSlotInfos: [RuneSlotInfoInputType!] = [] + ): ByteString! + claimRaidReward( + # address of avatar state to receive reward. + avatarAddress: Address! + ): ByteString! + claimWorldBossKillReward( + # address of avatar state to receive reward. + avatarAddress: Address! + ): ByteString! + prepareRewardAssets( + # address of reward pool for charge reward. + rewardPoolAddress: Address! + + # list of FungibleAssetValue for charge reward. + assets: [FungibleAssetValueInputType!]! + ): ByteString! + transferAssets( + # Address of sender. + sender: Address! + + # List of tuples that recipients' address and asset amount to be sent + recipients: [RecipientsInputType!]! + + # A 80-max length string to note. + memo: String + ): ByteString! + activateAccount( + # Activation code that you've get. + activationCode: String! + ): ByteString! + @deprecated(reason: "Since NCIP-15, it doesn't care account activation.") + createAvatar( + # index of avatar in `AgentState.avatarAddresses`.(0~2) + index: Int! + + # name of avatar.(2~20 characters) + name: String! + + # hair index of avatar. + hair: Int = 0 + + # lens index of avatar. + lens: Int = 0 + + # ear index of avatar. + ear: Int = 0 + + # tail index of avatar. + tail: Int = 0 + ): ByteString! + runeEnhancement( + # The avatar address to enhance rune. + avatarAddress: Address! + + # Rune ID to enhance. + runeId: Int! + + # The try count to enhance rune + tryCount: Int + ): ByteString! + hackAndSlash( + # Avatar address. + avatarAddress: Address! + + # World ID containing the stage ID. + worldId: Int! + + # Stage ID. + stageId: Int! + + # List of costume id for equip. + costumeIds: [Guid] + + # List of equipment id for equip. + equipmentIds: [Guid] + + # List of consumable id for use. + consumableIds: [Guid] + + # List of rune slot info for equip. + runeSlotInfos: [RuneSlotInfoInputType!] = [] + + # Buff ID for this stage + stageBuffId: Int + ): ByteString! + hackAndSlashSweep( + # Avatar address. + avatarAddress: Address! + + # World ID containing the stage ID. + worldId: Int! + + # Stage ID. + stageId: Int! + + # List of costume id for equip. + costumeIds: [Guid] + + # List of equipment id for equip. + equipmentIds: [Guid] + + # List of rune slot info for equip. + runeSlotInfos: [RuneSlotInfoInputType!] = [] + + # Action point usage to sweep + actionPoint: Int! + + # AP stone usage to sweep + apStoneCount: Int + ): ByteString! + dailyReward( + # Avatar address to get daily reward + avatarAddress: Address! + ): ByteString! + combinationEquipment( + # Avatar address to combine equipment + avatarAddress: Address! + + # Slot index to combine equipment + slotIndex: Int! + + # Combination recipe ID + recipeId: Int! + + # Sub-recipe ID of this combination + subRecipeId: Int + + # Pay crystal co combine equipment? + payByCrystal: Boolean + + # Use hammer point to combine equipment? + useHammerPoint: Boolean + ): ByteString! + itemEnhancement( + # Avatar address to enhance item + avatarAddress: Address! + + # Slot index to enhance item + slotIndex: Int! + + # Target item ID to enhance + itemId: Guid! + + # Material IDs to enhance + materialIds: [Guid!]! + ): ByteString! + rapidCombination( + # Avatar address to execute rapid combination + avatarAddress: Address! + + # Slot index to execute rapid + slotIndex: Int! + ): ByteString! + combinationConsumable( + # Avatar address to combine consumable + avatarAddress: Address! + + # Slot index to combine + slotIndex: Int! + + # Recipe ID to combine consumable + recipeId: Int! + ): ByteString! + requestPledge(agentAddress: Address!, mead: Int = 4): ByteString! + approvePledge(patronAddress: Address!): ByteString! + endPledge(agentAddress: Address!): ByteString! + createPledge( + patronAddress: Address! + agentAddresses: [Address!]! + mead: Int = 4 + ): ByteString! + loadIntoMyGarages( + # Array of balance address and currency ticker and quantity. + fungibleAssetValues: [BalanceInput!] + + # Inventory Address + inventoryAddr: Address + + # Array of fungible ID and count + fungibleIdAndCounts: [FungibleIdAndCountInput!] + + # Memo + memo: String + ): ByteString! + deliverToOthersGarages( + # Recipient agent address + recipientAgentAddr: Address! + + # Array of currency ticket and quantity to deliver. + fungibleAssetValues: [SimplifyFungibleAssetValueInput!] + + # Array of Fungible ID and count to deliver. + fungibleIdAndCounts: [FungibleIdAndCountInput!] + + # Memo + memo: String + ): ByteString! + unloadFromMyGarages( + # Recipient avatar address + recipientAvatarAddr: Address! + + # Array of balance address and currency ticker and quantity to send. + fungibleAssetValues: [BalanceInput!] + + # Array of fungible ID and count to send. + fungibleIdAndCounts: [FungibleIdAndCountInput!] + + # Memo + memo: String + ): ByteString! + auraSummon( + # Avatar address to get summoned items + avatarAddress: Address! + + # Summon group id + groupId: Int! + + # Count to summon. Must between 1 and 10. + summonCount: Int! + ): ByteString! + runeSummon( + # Avatar address to get summoned items + avatarAddress: Address! + + # Summon group id + groupId: Int! + + # Count to summon. Must between 1 and 10. + summonCount: Int! + ): ByteString! + claimItems( + # List of pair of avatar address, List to claim. + claimData: [ClaimDataInputType!]! + + # Memo to attach to this action. + memo: String + ): ByteString! + + # Query to craft/enhance items/foods + craftQuery: CraftQuery! +} + +type AddressQuery { + # user information address by world boss season. + raiderAddress( + # address of avatar state. + avatarAddress: Address! + + # world boss season id. + raidId: Int! + ): Address! + + # boss information address by world boss season. + worldBossAddress( + # world boss season id. + raidId: Int! + ): Address! + + # user boss kill reward record address by world boss season. + worldBossKillRewardRecordAddress( + # address of avatar state. + avatarAddress: Address! + + # world boss season id. + raidId: Int! + ): Address! + + # raider list address by world boss season. + raiderListAddress( + # world boss season id. + raidId: Int! + ): Address! + + # currency minters address. + currencyMintersAddress( + # A currency type. see also: https://github.com/planetarium/NineChronicles.Headless/blob/main/NineChronicles.Headless/GraphTypes/CurrencyEnumType.cs + currency: CurrencyEnum! + ): [Address!] + + # pledge information address. + pledgeAddress( + # address of agent state. + agentAddress: Address! + ): Address! +} + +type StandaloneMutation { + keyStore: KeyStoreMutation + @deprecated( + reason: "Use `planet key` command instead. https://www.npmjs.com/package/@planetarium/cli" + ) + activationStatus: ActivationStatusMutation + @deprecated(reason: "Since NCIP-15, it doesn't care account activation.") + action: ActionMutation + + # Add a new transaction to staging + stageTx( + # The base64-encoded bytes for new transaction. + payload: String! + ): Boolean! + + # Add a new transaction to staging and return TxId + stageTxV2( + # The base64-encoded bytes for new transaction. + payload: String! + ): TxId! + @deprecated( + reason: "API update with action query. use stageTransaction mutation" + ) + transfer( + # A hex-encoded value for address of recipient. + recipient: Address! + + # A string value of the value to be transferred. + amount: String! + + # A sender's transaction counter. You can get it through nextTxNonce(). + txNonce: Long! + + # A hex-encoded value for address of currency to be transferred. The default is the NCG's address. + currencyAddress: String! = "000000000000000000000000000000000000000A" + + # A 80-max length string to note. + memo: String + ): TxId + transferGold(recipient: Address!, amount: String!): TxId + @deprecated( + reason: "Incorrect remittance may occur when using transferGold() to the same address consecutively. Use transfer() instead." + ) + + # Add a new transaction to staging and return TxId + stageTransaction( + # The hexadecimal string of the transaction to stage. + payload: String! + ): TxId! +} + +type KeyStoreMutation { + createPrivateKey(passphrase: String!, privateKey: ByteString): PrivateKeyType! + revokePrivateKey(address: Address!): ProtectedPrivateKeyType! +} + +type ActivationStatusMutation { + activateAccount(encodedActivationKey: String!): Boolean! + @deprecated(reason: "Since NCIP-15, it doesn't care account activation.") +} + +type ActionMutation { + # Create new avatar. + createAvatar( + # Avatar name. + avatarName: String! + + # The index of character slot. 0 ~ 2 + avatarIndex: Int! + + # The index of character hair color. 0 ~ 8 + hairIndex: Int! + + # The index of character eye color. 0 ~ 8 + lensIndex: Int! + + # The index of character ear color. 0 ~ 8 + earIndex: Int! + + # The index of character tail color. 0 ~ 8 + tailIndex: Int! + ): TxId! + + # Start stage to get material. + hackAndSlash( + # Avatar address. + avatarAddress: Address! + + # World ID containing the stage ID. + worldId: Int! + + # Stage ID. + stageId: Int! + + # List of costume id for equip. + costumeIds: [Guid] + + # List of equipment id for equip. + equipmentIds: [Guid] + + # List of consumable id for use. + consumableIds: [Guid] + + # List of rune slot info for equip. + runeSlotInfos: [RuneSlotInfoInputType!] = [] + ): TxId! + + # Combine new equipment. + combinationEquipment( + # Avatar address to create equipment. + avatarAddress: Address! + + # EquipmentRecipe ID from EquipmentRecipeSheet. + recipeId: Int! + + # The empty combination slot index to combine equipment. 0 ~ 3 + slotIndex: Int! + + # EquipmentSubRecipe ID from EquipmentSubRecipeSheet. + subRecipeId: Int + ): TxId! + + # Upgrade equipment. + itemEnhancement( + # Avatar address to upgrade equipment. + avatarAddress: Address! + + # Equipment Guid for upgrade. + itemId: Guid! + + # Material Guids for equipment upgrade. + materialIds: [Guid!]! + + # The empty combination slot index to upgrade equipment. 0 ~ 3 + slotIndex: Int! + ): TxId! + + # Get daily reward. + dailyReward( + # Avatar address to receive reward. + avatarAddress: Address! + ): TxId! + + # Charge Action Points using Material. + chargeActionPoint( + # Avatar to use potion. + avatarAddress: Address! + ): TxId! + + # Combine new Consumable. + combinationConsumable( + # Avatar address to combine consumable. + avatarAddress: Address! + + # ConsumableRecipe ID from ConsumableRecipeSheet. + recipeId: Int! + + # The empty combination slot index to combine consumable. 0 ~ 3 + slotIndex: Int! + ): TxId! +} + +type StandaloneSubscription { + tipChanged: TipChanged + tx( + # A regular expression to filter transactions based on action type. + actionType: String! + ): TxType + preloadProgress: PreloadStateType + nodeStatus: NodeStatusType + differentAppProtocolVersionEncounter: DifferentAppProtocolVersionEncounterType! + notification: NotificationType! + nodeException: NodeExceptionType! + balanceByAgent( + # A hex-encoded address of agent. + address: Address! + ): String! +} + +type TipChanged { + index: Long! + hash: ByteString +} + +type TxType { + transaction: TransactionType! + txResult: TxResultType +} + +type PreloadStateType { + currentPhase: Long! + totalPhase: Long! + extra: PreloadStateExtraType! +} + +type PreloadStateExtraType { + type: String! + currentCount: Long! + totalCount: Long! +} + +type DifferentAppProtocolVersionEncounterType { + peer: String! + peerVersion: AppProtocolVersionType! + localVersion: AppProtocolVersionType! +} + +type NotificationType { + # The type of Notification. + type: NotificationEnum! + + # The message of Notification. + message: String +} + +enum NotificationEnum { + REFILL + HAS + COMBINATION_EQUIPMENT + COMBINATION_CONSUMABLE + BUYER + SELLER +} + +type NodeExceptionType { + # The code of NodeException. + code: Int! + + # The message of NodeException. + message: String! +} \ No newline at end of file diff --git a/integrations/javascript/@planetarium/9c-headless-provider/jsr.json b/integrations/javascript/@planetarium/9c-headless-provider/jsr.json new file mode 100644 index 0000000000..744cef2698 --- /dev/null +++ b/integrations/javascript/@planetarium/9c-headless-provider/jsr.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://jsr.io/schema/config-file.v1.json", + "name": "@planetarium/9c-headless-provider", + "version": "0.1.0", + "exports": "./src/index.ts", + "publish": { + "include": [ + "LICENSE", + "src/**/*.ts", + "README.md", + "jsr.json" + ] + } +} diff --git a/integrations/javascript/@planetarium/9c-headless-provider/package.json b/integrations/javascript/@planetarium/9c-headless-provider/package.json new file mode 100644 index 0000000000..20dd2510cd --- /dev/null +++ b/integrations/javascript/@planetarium/9c-headless-provider/package.json @@ -0,0 +1,37 @@ +{ + "name": "@planetarium/9c-headless-provider", + "version": "0.1.0", + "description": "A library to provide TxMetadataProvider implementation for NineChronicles.Headless.", + "module": "dist/index.js", + "type": "module", + "scripts": { + "build": "tsup", + "codegen": "graphql-codegen --config ./graphql/headless/codegen.yml", + "fmt": "biome check --apply src tests", + "fmt:ci": "biome check src tests", + "test": "vitest" + }, + "keywords": [], + "author": "Planetarium", + "license": "AGPL-3.0-only", + "dependencies": { + "@planetarium/account": "^5.4.1", + "@planetarium/tx": "^5.4.1", + "@planetarium/bencodex": "^0.2.2", + "@planetarium/lib9c": "workspace:^", + "graphql-request": "^6.0.0", + "graphql-tag": "^2.12.6" + }, + "devDependencies": { + "@biomejs/biome": "^1.7.1", + "@types/node": "^22.10.1", + "tsup": "^8.0.2", + "tsx": "^4.7.3", + "typescript": "^5.4.5", + "vitest": "^1.5.2", + "@graphql-codegen/cli": "^5.0.2", + "@graphql-codegen/typescript": "^4.0.0", + "@graphql-codegen/typescript-graphql-request": "^6.2.0", + "@graphql-codegen/typescript-operations": "^4.2.0" + } +} diff --git a/integrations/javascript/@planetarium/9c-headless-provider/src/index.ts b/integrations/javascript/@planetarium/9c-headless-provider/src/index.ts new file mode 100644 index 0000000000..7dfd9b5e8d --- /dev/null +++ b/integrations/javascript/@planetarium/9c-headless-provider/src/index.ts @@ -0,0 +1,42 @@ +import type { Address } from "@planetarium/account"; +import { encode } from "@planetarium/bencodex"; +import type { TxMetadataProvider } from "@planetarium/lib9c"; +import { encodeSignedTx, type signTx } from "@planetarium/tx"; +import { GraphQLClient } from "graphql-request"; +import { type Sdk, getSdk } from "./generated/headless/graphql-request.js"; + +export type SignedTx = Awaited>; +function serializeSignedTx(signedTx: SignedTx): Uint8Array { + return encode(encodeSignedTx(signedTx)); +} + +export class HeadlessClient implements TxMetadataProvider { + private constructor(private readonly sdk: Sdk) {} + + public static create(url: string): HeadlessClient { + const client = new GraphQLClient(url); + return new HeadlessClient(getSdk(client)); + } + + async getNextNonce(address: Address): Promise { + const response = await this.sdk.GetNextNonce({ + address: address.toString(), + }); + + return BigInt(response.nextTxNonce); + } + + async getGenesisHash(): Promise { + const response = await this.sdk.GetGenesisHash(); + return Buffer.from(response.nodeStatus.genesis.hash, "hex"); + } + + async stageTransaction(signedTx: SignedTx): Promise { + const serialized = serializeSignedTx(signedTx); + const response = await this.sdk.StageTransaction({ + tx: Buffer.from(serialized).toString("hex"), + }); + + return response.stageTransaction; + } +} diff --git a/integrations/javascript/@planetarium/9c-headless-provider/tsconfig.json b/integrations/javascript/@planetarium/9c-headless-provider/tsconfig.json new file mode 100644 index 0000000000..682b14dd05 --- /dev/null +++ b/integrations/javascript/@planetarium/9c-headless-provider/tsconfig.json @@ -0,0 +1,114 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig.json", + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "ESNext" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, + "lib": [ + "ESNext" + ] /* Specify a set of bundled library declaration files that describe the target runtime environment. */, + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + + /* Modules */ + "module": "NodeNext" /* Specify what module code is generated. */, + // "rootDir": "./", /* Specify the root folder within your source files. */ + "moduleResolution": "NodeNext" /* Specify how TypeScript looks up a file from a given module specifier. */, + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + "types": [ + "node" + ] /* Specify type package names to be included without being referenced in a source file. */, + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ + // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ + // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ + // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + + /* Emit */ + // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + // "outDir": "./", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, + + /* Type Checking */ + "strict": true /* Enable all strict type-checking options. */, + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + } +} diff --git a/integrations/javascript/@planetarium/9c-headless-provider/tsup.config.ts b/integrations/javascript/@planetarium/9c-headless-provider/tsup.config.ts new file mode 100644 index 0000000000..601cef65d9 --- /dev/null +++ b/integrations/javascript/@planetarium/9c-headless-provider/tsup.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'tsup'; + +export default defineConfig({ + entry: ['src/index.ts'], + format: ['cjs', 'esm'], + dts: true, +}); diff --git a/integrations/javascript/@planetarium/9c-headless-provider/vite.config.ts b/integrations/javascript/@planetarium/9c-headless-provider/vite.config.ts new file mode 100644 index 0000000000..7391578195 --- /dev/null +++ b/integrations/javascript/@planetarium/9c-headless-provider/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + cache: false, + }, +}); diff --git a/integrations/javascript/@planetarium/lib9c/README.md b/integrations/javascript/@planetarium/lib9c/README.md index 2f4e135b80..6105541019 100644 --- a/integrations/javascript/@planetarium/lib9c/README.md +++ b/integrations/javascript/@planetarium/lib9c/README.md @@ -7,7 +7,7 @@ This npm package provides functions to build actions equivalent to [Lib9c]. ## Usage Example ```typescript -import { HeadlessNetworkProvider } from "@planetarium/9c-headless-network-provider"; +import { HeadlessNetworkProvider } from "@planetarium/9c-headless-provider"; import { RawPrivateKey, Address } from "@planetarium/account"; import { signTx } from "@planetarium/tx"; import { makeTx, ClaimStakeReward } from "@planetarium/lib9c"; diff --git a/integrations/javascript/@planetarium/pnpm-lock.yaml b/integrations/javascript/@planetarium/pnpm-lock.yaml index a3ff3d83e3..1e6d109cbd 100644 --- a/integrations/javascript/@planetarium/pnpm-lock.yaml +++ b/integrations/javascript/@planetarium/pnpm-lock.yaml @@ -6,6 +6,58 @@ settings: importers: + 9c-headless-provider: + dependencies: + '@planetarium/account': + specifier: ^5.4.1 + version: 5.4.1 + '@planetarium/bencodex': + specifier: ^0.2.2 + version: 0.2.2 + '@planetarium/lib9c': + specifier: workspace:^ + version: link:../lib9c + '@planetarium/tx': + specifier: ^5.4.1 + version: 5.4.1(@planetarium/account@5.4.1) + graphql-request: + specifier: ^6.0.0 + version: 6.1.0(graphql@16.9.0) + graphql-tag: + specifier: ^2.12.6 + version: 2.12.6(graphql@16.9.0) + devDependencies: + '@biomejs/biome': + specifier: ^1.7.1 + version: 1.7.1 + '@graphql-codegen/cli': + specifier: ^5.0.2 + version: 5.0.3(@types/node@22.10.1)(graphql@16.9.0)(typescript@5.4.5) + '@graphql-codegen/typescript': + specifier: ^4.0.0 + version: 4.1.2(graphql@16.9.0) + '@graphql-codegen/typescript-graphql-request': + specifier: ^6.2.0 + version: 6.2.0(graphql-request@6.1.0(graphql@16.9.0))(graphql-tag@2.12.6(graphql@16.9.0))(graphql@16.9.0) + '@graphql-codegen/typescript-operations': + specifier: ^4.2.0 + version: 4.4.0(graphql@16.9.0) + '@types/node': + specifier: ^22.10.1 + version: 22.10.1 + tsup: + specifier: ^8.0.2 + version: 8.0.2(postcss@8.4.38)(typescript@5.4.5) + tsx: + specifier: ^4.7.3 + version: 4.7.3 + typescript: + specifier: ^5.4.5 + version: 5.4.5 + vitest: + specifier: ^1.5.2 + version: 1.5.2(@types/node@22.10.1) + lib9c: dependencies: '@planetarium/account': @@ -50,7 +102,7 @@ importers: version: 5.4.5 vitepress: specifier: ^1.2.2 - version: 1.2.2(@algolia/client-search@4.23.3)(@types/node@22.10.1)(postcss@8.4.38)(search-insights@2.14.0)(typescript@5.4.5) + version: 1.2.2(@algolia/client-search@4.23.3)(@types/node@22.10.1)(change-case@4.1.2)(postcss@8.4.38)(search-insights@2.14.0)(typescript@5.4.5) vitest: specifier: ^1.5.2 version: 1.5.2(@types/node@22.10.1) @@ -122,23 +174,298 @@ packages: '@algolia/transporter@4.23.3': resolution: {integrity: sha512-Wjl5gttqnf/gQKJA+dafnD0Y6Yw97yvfY8R9h0dQltX1GXTgNs1zWgvtWW0tHl1EgMdhAyw189uWiZMnL3QebQ==} + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + + '@ardatan/relay-compiler@12.0.0': + resolution: {integrity: sha512-9anThAaj1dQr6IGmzBMcfzOQKTa5artjuPmw8NYK/fiGEMjADbSguBY2FMDykt+QhilR3wc9VA/3yVju7JHg7Q==} + hasBin: true + peerDependencies: + graphql: '*' + + '@ardatan/sync-fetch@0.0.1': + resolution: {integrity: sha512-xhlTqH0m31mnsG0tIP4ETgfSB6gXDaYYsUWTrlUV93fFQPI9dd8hE0Ot6MHLCtqgB32hwJAC3YZMWlXZw7AleA==} + engines: {node: '>=14'} + + '@babel/code-frame@7.26.2': + resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.26.3': + resolution: {integrity: sha512-nHIxvKPniQXpmQLb0vhY3VaFb3S0YrTAwpOWJZh1wn3oJPjJk9Asva204PsBdmAE8vpzfHudT8DB0scYvy9q0g==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.26.0': + resolution: {integrity: sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.26.3': + resolution: {integrity: sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-annotate-as-pure@7.25.9': + resolution: {integrity: sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.25.9': + resolution: {integrity: sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-create-class-features-plugin@7.25.9': + resolution: {integrity: sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-member-expression-to-functions@7.25.9': + resolution: {integrity: sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.25.9': + resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.26.0': + resolution: {integrity: sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-optimise-call-expression@7.25.9': + resolution: {integrity: sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-plugin-utils@7.25.9': + resolution: {integrity: sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-replace-supers@7.25.9': + resolution: {integrity: sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-skip-transparent-expression-wrappers@7.25.9': + resolution: {integrity: sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==} + engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@7.24.6': resolution: {integrity: sha512-WdJjwMEkmBicq5T9fm/cHND3+UlFa2Yj8ALLgmoSQAJZysYbBjw+azChSGPN4DSPLXOcooGRvDwZWMcF/mLO2Q==} engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@7.25.9': + resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.24.6': resolution: {integrity: sha512-4yA7s865JHaqUdRbnaxarZREuPTHrjpDT+pXoAZ1yhyo6uFnIEpS8VMu16siFOHDpZNKYv5BObhsB//ycbICyw==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.25.9': + resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.25.9': + resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.26.0': + resolution: {integrity: sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==} + engines: {node: '>=6.9.0'} + '@babel/parser@7.24.6': resolution: {integrity: sha512-eNZXdfU35nJC2h24RznROuOpO94h6x8sg9ju0tT9biNtLZ2vuP8SduLqqV+/8+cebSLV9SJEAN5Z3zQbJG/M+Q==} engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@7.26.3': + resolution: {integrity: sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-proposal-class-properties@7.18.6': + resolution: {integrity: sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==} + engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead. + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-object-rest-spread@7.20.7': + resolution: {integrity: sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==} + engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-object-rest-spread instead. + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-class-properties@7.12.13': + resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-flow@7.26.0': + resolution: {integrity: sha512-B+O2DnPc0iG+YXFqOxv2WNuNU97ToWjOomUQ78DouOENWUaM5sVrmet9mcomUGQFwpJd//gvUagXBSdzO1fRKg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-assertions@7.26.0': + resolution: {integrity: sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-jsx@7.25.9': + resolution: {integrity: sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-object-rest-spread@7.8.3': + resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-arrow-functions@7.25.9': + resolution: {integrity: sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-block-scoped-functions@7.25.9': + resolution: {integrity: sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-block-scoping@7.25.9': + resolution: {integrity: sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-classes@7.25.9': + resolution: {integrity: sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-computed-properties@7.25.9': + resolution: {integrity: sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-destructuring@7.25.9': + resolution: {integrity: sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-flow-strip-types@7.25.9': + resolution: {integrity: sha512-/VVukELzPDdci7UUsWQaSkhgnjIWXnIyRpM02ldxaVoFK96c41So8JcKT3m0gYjyv7j5FNPGS5vfELrWalkbDA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-for-of@7.25.9': + resolution: {integrity: sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-function-name@7.25.9': + resolution: {integrity: sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-literals@7.25.9': + resolution: {integrity: sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-member-expression-literals@7.25.9': + resolution: {integrity: sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-commonjs@7.26.3': + resolution: {integrity: sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-object-super@7.25.9': + resolution: {integrity: sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-parameters@7.25.9': + resolution: {integrity: sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-property-literals@7.25.9': + resolution: {integrity: sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-display-name@7.25.9': + resolution: {integrity: sha512-KJfMlYIUxQB1CJfO3e0+h0ZHWOTLCPP115Awhaz8U0Zpq36Gl/cXlpoyMRnUWlhNUBAzldnCiAZNvCDj7CrKxQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx@7.25.9': + resolution: {integrity: sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-shorthand-properties@7.25.9': + resolution: {integrity: sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-spread@7.25.9': + resolution: {integrity: sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-template-literals@7.25.9': + resolution: {integrity: sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/runtime@7.26.0': + resolution: {integrity: sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.25.9': + resolution: {integrity: sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.26.4': + resolution: {integrity: sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==} + engines: {node: '>=6.9.0'} + '@babel/types@7.24.6': resolution: {integrity: sha512-WaMsgi6Q8zMgMth93GvWPXkhAIEobfsIkLTacoVZoK1J0CevIPGYY2Vo5YvJGqyHqXM6P4ppOYGsIRU8MM9pFQ==} engines: {node: '>=6.9.0'} + '@babel/types@7.26.3': + resolution: {integrity: sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==} + engines: {node: '>=6.9.0'} + '@biomejs/biome@1.7.1': resolution: {integrity: sha512-wb2UNoFXcgaMdKXKT5ytsYntaogl2FSTjDt20CZynF3v7OXQUcIpTrr+be3XoOGpoZRj3Ytq9TSpmplUREXmeA==} engines: {node: '>=14.21.3'} @@ -491,85 +818,351 @@ packages: cpu: [x64] os: [win32] - '@isaacs/cliui@8.0.2': - resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} - engines: {node: '>=12'} + '@graphql-codegen/add@5.0.3': + resolution: {integrity: sha512-SxXPmramkth8XtBlAHu4H4jYcYXM/o3p01+psU+0NADQowA8jtYkK6MW5rV6T+CxkEaNZItfSmZRPgIuypcqnA==} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - '@jest/schemas@29.6.3': - resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@graphql-codegen/cli@5.0.3': + resolution: {integrity: sha512-ULpF6Sbu2d7vNEOgBtE9avQp2oMgcPY/QBYcCqk0Xru5fz+ISjcovQX29V7CS7y5wWBRzNLoXwJQGeEyWbl05g==} + engines: {node: '>=16'} + hasBin: true + peerDependencies: + '@parcel/watcher': ^2.1.0 + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + peerDependenciesMeta: + '@parcel/watcher': + optional: true - '@jridgewell/gen-mapping@0.3.5': - resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} - engines: {node: '>=6.0.0'} + '@graphql-codegen/client-preset@4.5.1': + resolution: {integrity: sha512-UE2/Kz2eaxv35HIXFwlm2QwoUH77am6+qp54aeEWYq+T+WPwmIc6+YzqtGiT/VcaXgoOUSgidREGm9R6jKcf9g==} + engines: {node: '>=16'} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - '@jridgewell/resolve-uri@3.1.2': - resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} - engines: {node: '>=6.0.0'} + '@graphql-codegen/core@4.0.2': + resolution: {integrity: sha512-IZbpkhwVqgizcjNiaVzNAzm/xbWT6YnGgeOLwVjm4KbJn3V2jchVtuzHH09G5/WkkLSk2wgbXNdwjM41JxO6Eg==} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - '@jridgewell/set-array@1.2.1': - resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} - engines: {node: '>=6.0.0'} + '@graphql-codegen/gql-tag-operations@4.0.12': + resolution: {integrity: sha512-v279i49FJ5dMmQXIGUgm6FtnnkxtJjVJWDNYh9JK4ppvOixdHp+PmEzW227DkLN6avhVxNnYdp/1gdRBwdWypw==} + engines: {node: '>=16'} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - '@jridgewell/sourcemap-codec@1.4.15': - resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + '@graphql-codegen/plugin-helpers@2.7.2': + resolution: {integrity: sha512-kln2AZ12uii6U59OQXdjLk5nOlh1pHis1R98cDZGFnfaiAbX9V3fxcZ1MMJkB7qFUymTALzyjZoXXdyVmPMfRg==} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - '@jridgewell/trace-mapping@0.3.25': - resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@graphql-codegen/plugin-helpers@3.1.2': + resolution: {integrity: sha512-emOQiHyIliVOIjKVKdsI5MXj312zmRDwmHpyUTZMjfpvxq/UVAHUJIVdVf+lnjjrI+LXBTgMlTWTgHQfmICxjg==} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - '@noble/hashes@1.4.0': - resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==} - engines: {node: '>= 16'} + '@graphql-codegen/plugin-helpers@5.1.0': + resolution: {integrity: sha512-Y7cwEAkprbTKzVIe436TIw4w03jorsMruvCvu0HJkavaKMQbWY+lQ1RIuROgszDbxAyM35twB5/sUvYG5oW+yg==} + engines: {node: '>=16'} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - '@noble/secp256k1@1.7.1': - resolution: {integrity: sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==} + '@graphql-codegen/schema-ast@4.1.0': + resolution: {integrity: sha512-kZVn0z+th9SvqxfKYgztA6PM7mhnSZaj4fiuBWvMTqA+QqQ9BBed6Pz41KuD/jr0gJtnlr2A4++/0VlpVbCTmQ==} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - '@nodelib/fs.scandir@2.1.5': - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} + '@graphql-codegen/typed-document-node@5.0.12': + resolution: {integrity: sha512-Wsbc1AqC+MFp3maWPzrmmyHLuWCPB63qBBFLTKtO6KSsnn0KnLocBp475wkfBZnFISFvzwpJ0e6LV71gKfTofQ==} + engines: {node: '>=16'} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - '@nodelib/fs.stat@2.0.5': - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} + '@graphql-codegen/typescript-graphql-request@6.2.0': + resolution: {integrity: sha512-nkp5tr4PrC/+2QkQqi+IB+bc7AavUnUvXPW8MC93HZRvwfMGy6m2Oo7b9JCPZ3vhNpqT2VDWOn/zIZXKz6zJAw==} + engines: {node: '>= 16.0.0'} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + graphql-request: ^6.0.0 + graphql-tag: ^2.0.0 - '@nodelib/fs.walk@1.2.8': - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} + '@graphql-codegen/typescript-operations@4.4.0': + resolution: {integrity: sha512-oVlos2ySx8xIbbe8r5ZI6mOpI+OTeP14RmS2MchBJ6DL+S9G16O6+9V3Y8V22fTnmBTZkTfAAaBv4HYhhDGWVA==} + engines: {node: '>=16'} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - '@pkgjs/parseargs@0.11.0': - resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} - engines: {node: '>=14'} + '@graphql-codegen/typescript@4.1.2': + resolution: {integrity: sha512-GhPgfxgWEkBrvKR2y77OThus3K8B6U3ESo68l7+sHH1XiL2WapK5DdClViblJWKQerJRjfJu8tcaxQ8Wpk6Ogw==} + engines: {node: '>=16'} + peerDependencies: + graphql: ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - '@planetarium/account@5.4.1': - resolution: {integrity: sha512-J6sOkL0nyawqqgHj5WtK1w90BDhVJIgddsbL89HCTsn+ybhNdqFM8O9pdx38FuF3REyGmIv2DvD/4MCcljDGvQ==} + '@graphql-codegen/visitor-plugin-common@2.13.1': + resolution: {integrity: sha512-mD9ufZhDGhyrSaWQGrU1Q1c5f01TeWtSWy/cDwXYjJcHIj1Y/DG2x0tOflEfCvh5WcnmHNIw4lzDsg1W7iFJEg==} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - '@planetarium/bencodex@0.2.2': - resolution: {integrity: sha512-hXYmV0gzEUxbhpVDiMIe/b0XAUoXBIl8Fo84Fx8nE0U39sU5q7tCFTkceock8TYs5rS4ix793033b7QYAPVoLA==} + '@graphql-codegen/visitor-plugin-common@5.6.0': + resolution: {integrity: sha512-PowcVPJbUqMC9xTJ/ZRX1p/fsdMZREc+69CM1YY+AlFng2lL0zsdBskFJSRoviQk2Ch9IPhKGyHxlJCy9X22tg==} + engines: {node: '>=16'} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - '@planetarium/tx@5.4.1': - resolution: {integrity: sha512-+OAHaScs/BVZUoQZsbQCWEKybEfSOKmhhaNAVj6Ko6RHE7pHJ+4s2dRmUOaffnbZy0FymHookciJcA3BThfevw==} - engines: {node: '>=19.0.0'} + '@graphql-tools/apollo-engine-loader@8.0.7': + resolution: {integrity: sha512-jyQU4ZhbkUM7C3V+m15K3ch7BSCTdWw/bthjhYhMkiMoFGL/ClNL5+fCIFMcQi5xSxPPmwkBkxzQ8u8UoNPMAg==} + engines: {node: '>=16.0.0'} peerDependencies: - '@planetarium/account': ^5.4.1 + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - '@rollup/rollup-android-arm-eabi@4.16.4': - resolution: {integrity: sha512-GkhjAaQ8oUTOKE4g4gsZ0u8K/IHU1+2WQSgS1TwTcYvL+sjbaQjNHFXbOJ6kgqGHIO1DfUhI/Sphi9GkRT9K+Q==} - cpu: [arm] - os: [android] + '@graphql-tools/batch-execute@9.0.10': + resolution: {integrity: sha512-nCRNFq2eqy+ONDknd8DfqidY/Ljgyq67Q0Hb9SMJ3FOWpKrApqmNT9J1BA3JW4r+/zIGtM1VKi+P9FYu3zMHHA==} + engines: {node: '>=18.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - '@rollup/rollup-android-arm64@4.16.4': - resolution: {integrity: sha512-Bvm6D+NPbGMQOcxvS1zUl8H7DWlywSXsphAeOnVeiZLQ+0J6Is8T7SrjGTH29KtYkiY9vld8ZnpV3G2EPbom+w==} - cpu: [arm64] - os: [android] + '@graphql-tools/code-file-loader@8.1.8': + resolution: {integrity: sha512-b8BTP0cVTgWgc60H7LNfY7dZcEJVsgyCm52BsWOggwWapKAdli1T7ZaLJvnTAbVd8EY8+k4OAO1Z/ti1iirVOA==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - '@rollup/rollup-darwin-arm64@4.16.4': - resolution: {integrity: sha512-i5d64MlnYBO9EkCOGe5vPR/EeDwjnKOGGdd7zKFhU5y8haKhQZTN2DgVtpODDMxUr4t2K90wTUJg7ilgND6bXw==} - cpu: [arm64] - os: [darwin] + '@graphql-tools/delegate@10.2.7': + resolution: {integrity: sha512-cHNRguTi/RGxLttmDR5F4698kVtoPnYCFjgEZh/sg8MGrejTiCpQeg+aXUqcj0efWmnKIkeia5JaqqbTGpc0xA==} + engines: {node: '>=18.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - '@rollup/rollup-darwin-x64@4.16.4': - resolution: {integrity: sha512-WZupV1+CdUYehaZqjaFTClJI72fjJEgTXdf4NbW69I9XyvdmztUExBtcI2yIIU6hJtYvtwS6pkTkHJz+k08mAQ==} - cpu: [x64] + '@graphql-tools/documents@1.0.1': + resolution: {integrity: sha512-aweoMH15wNJ8g7b2r4C4WRuJxZ0ca8HtNO54rkye/3duxTkW4fGBEutCx03jCIr5+a1l+4vFJNP859QnAVBVCA==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/executor-graphql-ws@1.3.5': + resolution: {integrity: sha512-8BZf9a9SkaJAkF5Byb4ZdiwzCNoTrfl515m206XvCkCHM7dM1AwvX1rYZTrnJWgXgQUxhPjvll5vgciOe1APaA==} + engines: {node: '>=18.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/executor-http@1.1.14': + resolution: {integrity: sha512-y/j+fOTgkYSEDzLQ7xMErSfg6kgejVhG4yieKy1PXBaiDNN8t9MOUxEJDDtRDr/pFnvjTtm78UFo04I7S+m7BA==} + engines: {node: '>=18.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/executor-legacy-ws@1.1.5': + resolution: {integrity: sha512-iqN3NYpv4mGTOUUkhNOL0v9kskVHXl1BrzueRtDFaWznjO7qpwAUwCAih3AMHDNadLQdppkjIhOJB+YU8KCfsQ==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/executor@1.3.6': + resolution: {integrity: sha512-ZmWsWdUhTez2b4w9NkmL4wpPb8n8WZmLOMIPTXH2A2yEe2nHrK/tk653JZXvZFtx2HrBIcoZD4Fe/STYWIR74Q==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/git-loader@8.0.12': + resolution: {integrity: sha512-B65UbwMeR6TWwTzz5OS6iGuqSa1za/lbLO3buSwDs8+zxTpqrJljeKllG2EFk7g7D2OtTt3Tu9+itWkuIbqOUw==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/github-loader@8.0.7': + resolution: {integrity: sha512-p7aGLbOkwLTCKk/hSEJJgrSIhbwNS7SBhtYFPMa1uoga4I10xDJuGrUl8l9Jq2y953rtJA6/aGyVJs87Yn2hwA==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/graphql-file-loader@8.0.6': + resolution: {integrity: sha512-nLOvotKcvZLXQWryYl34vHI4Fr+VTA/y6WHcZ73gXBQ//8oGKgnuDNoAdi4rXgk4iGyIMvRxZpYU27k6Z4acBw==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/graphql-tag-pluck@8.3.7': + resolution: {integrity: sha512-QoGf/8oVzhMZW+EbgpkM7zUxlNyv60Twb254R0D8TxS19OznoMMZMiDJdoID/k42QRoJ7o1V/yEOHgJFcqYHVw==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/import@7.0.6': + resolution: {integrity: sha512-F28lG9w3gckZ+ubnq3jM2s2OiyH+cVZZXvOZ8RO/EJQ0dS+BE/S9zzvpCTuOWyuZvcLvbYBDjliZTOmeSQUhMg==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/json-file-loader@8.0.6': + resolution: {integrity: sha512-mjZFVMtBL9fcvovwCoXKjZxXqr92/dcPZmHlQsW9jUC9WW6KfmolwtyvRxy9CcOjjh1HDTPcNoDgW05iI1CFYQ==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/load@8.0.7': + resolution: {integrity: sha512-1JmZaMxs9LOyyq7XF/knBxY+Uejnc68+nILCFYwsts9KTUOZHpJqjleIIDf7Il1yHDaujjThX4Xqg2Dwhdb/bw==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/merge@9.0.12': + resolution: {integrity: sha512-ECkUdgWkizhzQ6JJg16MCYnIN2r2+q/vP5smzi3YeeJkZ/3f9ynFDkaqoMg0Ddg9MugR03hMiQQrssk5f0389Q==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/optimize@1.4.0': + resolution: {integrity: sha512-dJs/2XvZp+wgHH8T5J2TqptT9/6uVzIYvA6uFACha+ufvdMBedkfR4b4GbT8jAKLRARiqRTxy3dctnwkTM2tdw==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/optimize@2.0.0': + resolution: {integrity: sha512-nhdT+CRGDZ+bk68ic+Jw1OZ99YCDIKYA5AlVAnBHJvMawSx9YQqQAIj4refNc1/LRieGiuWvhbG3jvPVYho0Dg==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/prisma-loader@8.0.17': + resolution: {integrity: sha512-fnuTLeQhqRbA156pAyzJYN0KxCjKYRU5bz1q/SKOwElSnAU4k7/G1kyVsWLh7fneY78LoMNH5n+KlFV8iQlnyg==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/relay-operation-optimizer@6.5.18': + resolution: {integrity: sha512-mc5VPyTeV+LwiM+DNvoDQfPqwQYhPV/cl5jOBjTgSniyaq8/86aODfMkrE2OduhQ5E00hqrkuL2Fdrgk0w1QJg==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/relay-operation-optimizer@7.0.6': + resolution: {integrity: sha512-hzzH1flmvL0o7tczQbnGVmsaLruhl8rxoqszo6uBjjjPxppoT0vwqIvU5X+lGJi2U+/fv3Q2FV3XALQB5Pmeaw==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/schema@10.0.11': + resolution: {integrity: sha512-cYr/7SJSKtdwPByTKHlBr0tYGf7/sYNyzKlPhPMHWoYyGxtn8ytbfF6wEUcxuaOoqksIFxOGr+WOJh1WvShb6A==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/url-loader@8.0.18': + resolution: {integrity: sha512-gz6oRoZzUJyBDIVMBKFa35InRqzq3FOb/kEb+8T3/DrDZCIxFlmLBZzy9ANjKmF3ctLn0WQXopRSaG/Wq7NEwA==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/utils@10.6.2': + resolution: {integrity: sha512-ABZHTpwiVR8oE2//NI/nnU3nNhbBpqMlMYyCF5cnqjLfhlyOdFfoRuhYEATEsmMfDg0ijGreULywK/SmepVGfw==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/utils@8.13.1': + resolution: {integrity: sha512-qIh9yYpdUFmctVqovwMdheVNJqFh+DQNWIhX87FJStfXYnmweBUDATok9fWPleKeFwxnW8IapKmY8m8toJEkAw==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/utils@9.2.1': + resolution: {integrity: sha512-WUw506Ql6xzmOORlriNrD6Ugx+HjVgYxt9KCXD9mHAak+eaXSwuGGPyE60hy9xaDEoXKBsG7SkG69ybitaVl6A==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/wrap@10.0.25': + resolution: {integrity: sha512-51Koxi6IZHF4Ns7c6jvLU2x7GJyGGDL7V6e0u4J6ci/0vSCqLBwT3YYutDlZ7uJTpbLjEbjl0R0+1fOerdIkOQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-typed-document-node/core@3.2.0': + resolution: {integrity: sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@jest/schemas@29.6.3': + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jridgewell/gen-mapping@0.3.5': + resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} + engines: {node: '>=6.0.0'} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/set-array@1.2.1': + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.4.15': + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + + '@kamilkisiela/fast-url-parser@1.1.4': + resolution: {integrity: sha512-gbkePEBupNydxCelHCESvFSFM8XPh1Zs/OAVRW/rKpEqPAl5PbOM90Si8mv9bvnR53uPD2s/FiRxdvSejpRJew==} + + '@noble/hashes@1.4.0': + resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==} + engines: {node: '>= 16'} + + '@noble/secp256k1@1.7.1': + resolution: {integrity: sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + '@planetarium/account@5.4.1': + resolution: {integrity: sha512-J6sOkL0nyawqqgHj5WtK1w90BDhVJIgddsbL89HCTsn+ybhNdqFM8O9pdx38FuF3REyGmIv2DvD/4MCcljDGvQ==} + + '@planetarium/bencodex@0.2.2': + resolution: {integrity: sha512-hXYmV0gzEUxbhpVDiMIe/b0XAUoXBIl8Fo84Fx8nE0U39sU5q7tCFTkceock8TYs5rS4ix793033b7QYAPVoLA==} + + '@planetarium/tx@5.4.1': + resolution: {integrity: sha512-+OAHaScs/BVZUoQZsbQCWEKybEfSOKmhhaNAVj6Ko6RHE7pHJ+4s2dRmUOaffnbZy0FymHookciJcA3BThfevw==} + engines: {node: '>=19.0.0'} + peerDependencies: + '@planetarium/account': ^5.4.1 + + '@repeaterjs/repeater@3.0.6': + resolution: {integrity: sha512-Javneu5lsuhwNCryN+pXH93VPQ8g0dBX7wItHFgYiwQmzE1sVdg5tWHiOgHywzL2W21XQopa7IwIEnNbmeUJYA==} + + '@rollup/rollup-android-arm-eabi@4.16.4': + resolution: {integrity: sha512-GkhjAaQ8oUTOKE4g4gsZ0u8K/IHU1+2WQSgS1TwTcYvL+sjbaQjNHFXbOJ6kgqGHIO1DfUhI/Sphi9GkRT9K+Q==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.16.4': + resolution: {integrity: sha512-Bvm6D+NPbGMQOcxvS1zUl8H7DWlywSXsphAeOnVeiZLQ+0J6Is8T7SrjGTH29KtYkiY9vld8ZnpV3G2EPbom+w==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.16.4': + resolution: {integrity: sha512-i5d64MlnYBO9EkCOGe5vPR/EeDwjnKOGGdd7zKFhU5y8haKhQZTN2DgVtpODDMxUr4t2K90wTUJg7ilgND6bXw==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.16.4': + resolution: {integrity: sha512-WZupV1+CdUYehaZqjaFTClJI72fjJEgTXdf4NbW69I9XyvdmztUExBtcI2yIIU6hJtYvtwS6pkTkHJz+k08mAQ==} + cpu: [x64] os: [darwin] '@rollup/rollup-linux-arm-gnueabihf@4.16.4': @@ -644,6 +1237,9 @@ packages: '@types/estree@1.0.5': resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + '@types/js-yaml@4.0.9': + resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==} + '@types/linkify-it@5.0.0': resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==} @@ -662,6 +1258,9 @@ packages: '@types/web-bluetooth@0.0.20': resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==} + '@types/ws@8.5.13': + resolution: {integrity: sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA==} + '@vitejs/plugin-vue@5.0.4': resolution: {integrity: sha512-WS3hevEszI6CEVEx28F8RjTX97k3KsrcY6kvTg7+Whm5y3oYvcqzVeGCU3hxSAn4uY2CLCkeokkGKpoctccilQ==} engines: {node: ^18.0.0 || >=20.0.0} @@ -774,6 +1373,26 @@ packages: '@vueuse/shared@10.10.0': resolution: {integrity: sha512-2aW33Ac0Uk0U+9yo3Ypg9s5KcR42cuehRWl7vnUHadQyFvCktseyxxEPBi1Eiq4D2yBGACOnqLZpx1eMc7g5Og==} + '@whatwg-node/disposablestack@0.0.5': + resolution: {integrity: sha512-9lXugdknoIequO4OYvIjhygvfSEgnO8oASLqLelnDhkRjgBZhc39shC3QSlZuyDO9bgYSIVa2cHAiN+St3ty4w==} + engines: {node: '>=18.0.0'} + + '@whatwg-node/fetch@0.10.1': + resolution: {integrity: sha512-gmPOLrsjSZWEZlr9Oe5+wWFBq3CG6fN13rGlM91Jsj/vZ95G9CCvrORGBAxMXy0AJGiC83aYiHXn3JzTzXQmbA==} + engines: {node: '>=18.0.0'} + + '@whatwg-node/fetch@0.9.23': + resolution: {integrity: sha512-7xlqWel9JsmxahJnYVUj/LLxWcnA93DR4c9xlw3U814jWTiYalryiH1qToik1hOxweKKRLi4haXHM5ycRksPBA==} + engines: {node: '>=18.0.0'} + + '@whatwg-node/node-fetch@0.6.0': + resolution: {integrity: sha512-tcZAhrpx6oVlkEsRngeTEEE7I5/QdLjeEz4IlekabGaESP7+Dkm/6a9KcF1KdCBB7mO9PXtBkwCuTCt8+UPg8Q==} + engines: {node: '>=18.0.0'} + + '@whatwg-node/node-fetch@0.7.4': + resolution: {integrity: sha512-rvUtU/xKKl/av5EIwyqfw7w0R+hx+tQrlhpIyFr27MwJRlUb+xcYv97kOmp7FE/WmQ8s+Tb6bcD6W8o/s2pGWw==} + engines: {node: '>=18.0.0'} + acorn-walk@8.3.2: resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==} engines: {node: '>=0.4.0'} @@ -783,9 +1402,21 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + agent-base@7.1.3: + resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==} + engines: {node: '>= 14'} + + aggregate-error@3.1.0: + resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} + engines: {node: '>=8'} + algoliasearch@4.23.3: resolution: {integrity: sha512-Le/3YgNvjW9zxIQMRhUHuhiUjAlKY/zsdZpfq4dlLqg6mEm0nL6yk+7f2hDOtLpxsgE4jSzDmvHL7nXdBp5feg==} + ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -813,13 +1444,35 @@ packages: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + array-union@2.1.0: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} + asap@2.0.6: + resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} + assertion-error@1.1.0: resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + astral-regex@2.0.0: + resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} + engines: {node: '>=8'} + + auto-bind@4.0.0: + resolution: {integrity: sha512-Hdw8qdNiqdJ8LqT0iK0sVzkFbzg6fhnQqqfWhBDxcHZvU75+B+ayzTy8x+k5Ix0Y92XOhOUlx74ps+bA6BeYMQ==} + engines: {node: '>=8'} + + babel-plugin-syntax-trailing-function-commas@7.0.0-beta.0: + resolution: {integrity: sha512-Xj9XuRuz3nTSbaTXWv3itLOcxyF4oPD8douBBmj7U9BBC6nEBYfyOJYQMf/8PJAFotC62UY5dFfIGEPr7WswzQ==} + + babel-preset-fbjs@3.4.0: + resolution: {integrity: sha512-9ywCsCvo1ojrw0b+XYk7aFvTH6D9064t0RIL1rtMf3nsa02Xw41MS7sZw216Im35xj/UY0PDBQsa1brUDDF1Ow==} + peerDependencies: + '@babel/core': ^7.0.0 + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -830,6 +1483,12 @@ packages: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} + bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + + brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + brace-expansion@2.0.1: resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} @@ -837,6 +1496,21 @@ packages: resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} engines: {node: '>=8'} + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.24.2: + resolution: {integrity: sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + bser@2.1.1: + resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} + + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + buffer@6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} @@ -846,14 +1520,51 @@ packages: peerDependencies: esbuild: '>=0.17' + busboy@1.6.0: + resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} + engines: {node: '>=10.16.0'} + cac@6.7.14: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + camel-case@4.1.2: + resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==} + + camelcase@5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + + caniuse-lite@1.0.30001687: + resolution: {integrity: sha512-0S/FDhf4ZiqrTUiQ39dKeUjYRjkv7lOZU1Dgif2rIqrTzX/1wV2hfKu9TOm1IHkdSijfLswxTFzl/cvir+SLSQ==} + + capital-case@1.0.4: + resolution: {integrity: sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==} + chai@4.4.1: resolution: {integrity: sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==} engines: {node: '>=4'} + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + change-case-all@1.0.14: + resolution: {integrity: sha512-CWVm2uT7dmSHdO/z1CXT/n47mWonyypzBbuCy5tN7uMg22BsfkhwT6oHmFCAk+gL1LOOxhdbB9SZz3J1KTY3gA==} + + change-case-all@1.0.15: + resolution: {integrity: sha512-3+GIFhk3sNuvFAJKU46o26OdzudQlPNBCu1ZQi3cMeMHhty1bhDxu2WrEilVNYaGvqUtR1VSigFcJOiS13dRhQ==} + + change-case@4.1.2: + resolution: {integrity: sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==} + + chardet@0.7.0: + resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + check-error@1.0.3: resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} @@ -861,6 +1572,37 @@ packages: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} + clean-stack@2.2.0: + resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} + engines: {node: '>=6'} + + cli-cursor@3.1.0: + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} + + cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + engines: {node: '>=6'} + + cli-truncate@2.1.0: + resolution: {integrity: sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==} + engines: {node: '>=8'} + + cli-width@3.0.0: + resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==} + engines: {node: '>= 10'} + + cliui@6.0.0: + resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + clone@1.0.4: + resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} + engines: {node: '>=0.8'} + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -868,13 +1610,45 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + commander@4.1.1: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} + common-tags@1.8.2: + resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==} + engines: {node: '>=4.0.0'} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + confbox@0.1.7: resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} + constant-case@3.0.4: + resolution: {integrity: sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cosmiconfig@8.3.6: + resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + + cross-fetch@3.1.8: + resolution: {integrity: sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==} + + cross-inspect@1.0.1: + resolution: {integrity: sha512-Pcw1JTvZLSJH83iiGWt6fRcT+BjZlCDRVwYLbUcHzv/CRpB7r0MlSrGbIyQvVSNyGnbt7G4AXuyCiDR3POvZ1A==} + engines: {node: '>=16.0.0'} + cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} @@ -882,6 +1656,12 @@ packages: csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + dataloader@2.2.3: + resolution: {integrity: sha512-y2krtASINtPFS1rSDjacrFgn1dcUuoREVabwlOGOe4SdxenREqwjwjElAdwvbGM7kgZz9a3KVicWR7vcz8rnzA==} + + debounce@1.2.1: + resolution: {integrity: sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==} + debug@4.3.4: resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} @@ -891,6 +1671,10 @@ packages: supports-color: optional: true + decamelize@1.2.0: + resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} + engines: {node: '>=0.10.0'} + decimal.js@10.4.3: resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} @@ -898,6 +1682,17 @@ packages: resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==} engines: {node: '>=6'} + defaults@1.0.4: + resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + + dependency-graph@0.11.0: + resolution: {integrity: sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==} + engines: {node: '>= 0.6.0'} + + detect-indent@6.1.0: + resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} + engines: {node: '>=8'} + diff-sequences@29.6.3: resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -906,9 +1701,23 @@ packages: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} + dot-case@3.0.4: + resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} + + dotenv@16.4.7: + resolution: {integrity: sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==} + engines: {node: '>=12'} + + dset@3.1.4: + resolution: {integrity: sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==} + engines: {node: '>=4'} + eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + electron-to-chromium@1.5.71: + resolution: {integrity: sha512-dB68l59BI75W1BUGVTAEJy45CEVuEGy9qPVVQ8pnHyHMn36PLPPoE1mjLH+lo9rKulO3HC2OhbACI/8tCqJBcA==} + emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -919,6 +1728,9 @@ packages: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} + error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + esbuild@0.19.12: resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==} engines: {node: '>=12'} @@ -929,6 +1741,14 @@ packages: engines: {node: '>=12'} hasBin: true + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} @@ -943,17 +1763,52 @@ packages: resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} engines: {node: '>=16.17'} + external-editor@3.1.0: + resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} + engines: {node: '>=4'} + + extract-files@11.0.0: + resolution: {integrity: sha512-FuoE1qtbJ4bBVvv94CC7s0oTnKUGvQs+Rjf1L2SJFfS+HTVVjhPFtehPdQ0JiGPqVNfSSZvL5yzHHQq2Z4WNhQ==} + engines: {node: ^12.20 || >= 14.13} + + fast-decode-uri-component@1.0.1: + resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} + fast-glob@3.3.2: resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} engines: {node: '>=8.6.0'} + fast-querystring@1.1.2: + resolution: {integrity: sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==} + fastq@1.17.1: resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + fb-watchman@2.0.2: + resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} + + fbjs-css-vars@1.0.2: + resolution: {integrity: sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==} + + fbjs@3.0.5: + resolution: {integrity: sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg==} + + figures@3.2.0: + resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} + engines: {node: '>=8'} + fill-range@7.0.1: resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} engines: {node: '>=8'} + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + focus-trap@7.5.4: resolution: {integrity: sha512-N7kHdlgsO/v+iD/dMoJKtsSqs5Dz/dXZVebRgJw23LDk+jMi/974zyiOYDziY2JPp8xivq9BmUGwIJMiuSBi7w==} @@ -961,11 +1816,22 @@ packages: resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} engines: {node: '>=14'} + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + get-func-name@2.0.2: resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} @@ -989,13 +1855,67 @@ packages: engines: {node: '>=16 || 14 >=14.17'} hasBin: true + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported + + globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + globby@11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} + graphql-config@5.1.3: + resolution: {integrity: sha512-RBhejsPjrNSuwtckRlilWzLVt2j8itl74W9Gke1KejDTz7oaA5kVd6wRn9zK9TS5mcmIYGxf7zN7a1ORMdxp1Q==} + engines: {node: '>= 16.0.0'} + peerDependencies: + cosmiconfig-toml-loader: ^1.0.0 + graphql: ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + peerDependenciesMeta: + cosmiconfig-toml-loader: + optional: true + + graphql-request@6.1.0: + resolution: {integrity: sha512-p+XPfS4q7aIpKVcgmnZKhMNqhltk20hfXtkaIkTfjjmiKMJ5xrt5c743cL03y/K7y1rg3WrIC49xGiEQ4mxdNw==} + peerDependencies: + graphql: 14 - 16 + + graphql-tag@2.12.6: + resolution: {integrity: sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==} + engines: {node: '>=10'} + peerDependencies: + graphql: ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + + graphql-ws@5.16.0: + resolution: {integrity: sha512-Ju2RCU2dQMgSKtArPbEtsK5gNLnsQyTNIo/T7cZNp96niC1x0KdJNZV0TIoilceBPQwfb5itrGl8pkFeOUMl4A==} + engines: {node: '>=10'} + peerDependencies: + graphql: '>=0.11 <=16' + + graphql@16.9.0: + resolution: {integrity: sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw==} + engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + header-case@2.0.4: + resolution: {integrity: sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==} + hookable@5.5.3: resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + human-signals@2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} @@ -1004,6 +1924,10 @@ packages: resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} engines: {node: '>=16.17.0'} + iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} @@ -1011,6 +1935,43 @@ packages: resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} engines: {node: '>= 4'} + immutable@3.7.6: + resolution: {integrity: sha512-AizQPcaofEtO11RZhPPHBOJRdo/20MKQF9mBLnVkBoyHi1/zXK8fzVdnEpSV9gxqtnh6Qomfp3F0xT5qP/vThw==} + engines: {node: '>=0.8.0'} + + import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + + import-from@4.0.0: + resolution: {integrity: sha512-P9J71vT5nLlDeV8FHs5nNxaLbrpfAV5cF5srvbZfpwpcJoM/xZR3hiv+q+SAnuSmuGbXMWud063iIMx/V/EWZQ==} + engines: {node: '>=12.2'} + + indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + inquirer@8.2.6: + resolution: {integrity: sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==} + engines: {node: '>=12.0.0'} + + invariant@2.2.4: + resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} + + is-absolute@1.0.0: + resolution: {integrity: sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==} + engines: {node: '>=0.10.0'} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + is-binary-path@2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} @@ -1027,10 +1988,21 @@ packages: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} + is-interactive@1.0.0: + resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} + engines: {node: '>=8'} + + is-lower-case@2.0.2: + resolution: {integrity: sha512-bVcMJy4X5Og6VZfdOZstSexlEy20Sr0k/p/b2IlQJlfdKAQuMpiv5w2Ccxb8sKdRUNAG1PnHVHjFSdRDVS6NlQ==} + is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} + is-relative@1.0.0: + resolution: {integrity: sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==} + engines: {node: '>=0.10.0'} + is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} @@ -1039,20 +2011,75 @@ packages: resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + is-unc-path@1.0.0: + resolution: {integrity: sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==} + engines: {node: '>=0.10.0'} + + is-unicode-supported@0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + + is-upper-case@2.0.2: + resolution: {integrity: sha512-44pxmxAvnnAOwBg4tHPnkfvgjPwbc5QIsSstNU+YcJ1ovxVzCWpSGosPJOZh/a1tdl81fbgnLc9LLv+x2ywbPQ==} + + is-windows@1.0.2: + resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} + engines: {node: '>=0.10.0'} + isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + isomorphic-ws@5.0.0: + resolution: {integrity: sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==} + peerDependencies: + ws: '*' + jackspeak@2.3.6: resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==} engines: {node: '>=14'} + jiti@1.21.6: + resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==} + hasBin: true + + jiti@2.4.1: + resolution: {integrity: sha512-yPBThwecp1wS9DmoA4x4KR2h3QoslacnDR8ypuFM962kI4/456Iy1oHx2RAgh4jfZNdn0bctsdadceiBUgpU1g==} + hasBin: true + + jose@5.9.6: + resolution: {integrity: sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ==} + joycon@3.1.1: resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} engines: {node: '>=10'} + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + js-tokens@9.0.0: resolution: {integrity: sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==} + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + jsesc@3.0.2: + resolution: {integrity: sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==} + engines: {node: '>=6'} + hasBin: true + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-to-pretty-yaml@1.2.2: + resolution: {integrity: sha512-rvm6hunfCcqegwYaG5T4yKJWxc9FXFgBVrcTZ4XfSVRwa5HA/Xs+vB/Eo9treYYHCeNM0nrSUr82V/M31Urc7A==} + engines: {node: '>= 0.2.0'} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + lilconfig@3.1.1: resolution: {integrity: sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==} engines: {node: '>=14'} @@ -1060,6 +2087,15 @@ packages: lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + listr2@4.0.5: + resolution: {integrity: sha512-juGHV1doQdpNT3GSTs9IUN43QJb7KHdF9uqg7Vufs/tG9VTzpFphqF4pm/ICdAABGQxsyNn9CiYA3StkI6jpwA==} + engines: {node: '>=12'} + peerDependencies: + enquirer: '>= 2.3.0 < 3' + peerDependenciesMeta: + enquirer: + optional: true + load-tsconfig@0.2.5: resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -1068,19 +2104,51 @@ packages: resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==} engines: {node: '>=14'} + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + lodash.sortby@4.7.0: resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + log-symbols@4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} + + log-update@4.0.0: + resolution: {integrity: sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==} + engines: {node: '>=10'} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + loupe@2.3.7: resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} + lower-case-first@2.0.2: + resolution: {integrity: sha512-EVm/rR94FJTZi3zefZ82fLWab+GX14LJN4HrWBcuo6Evmsl9hEfnqxgcHCKb9q+mNf6EVdsjx/qucYFIIB84pg==} + + lower-case@2.0.2: + resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + lru-cache@10.2.1: resolution: {integrity: sha512-tS24spDe/zXhWbNPErCHs/AGOzbKGHT+ybSBqmdLm8WZ1xXLWvH8Qn71QPAlqVhd0qUTWjy+Kl9JmISgDdEjsA==} engines: {node: 14 || >=16.14} + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + magic-string@0.30.10: resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} + map-cache@0.2.2: + resolution: {integrity: sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==} + engines: {node: '>=0.10.0'} + mark.js@8.11.1: resolution: {integrity: sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==} @@ -1091,10 +2159,23 @@ packages: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} + meros@1.3.0: + resolution: {integrity: sha512-2BNGOimxEz5hmjUG2FwoxCt5HN7BXdaWyFqEwxPTrJzVdABtrL4TiHTcsWSFAxPQ/tOnEaQEJh3qWq71QRMY+w==} + engines: {node: '>=13'} + peerDependencies: + '@types/node': '>=13' + peerDependenciesMeta: + '@types/node': + optional: true + micromatch@4.0.5: resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} engines: {node: '>=8.6'} + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} @@ -1103,10 +2184,17 @@ packages: resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} engines: {node: '>=12'} + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + minimatch@9.0.4: resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==} engines: {node: '>=16 || 14 >=14.17'} + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + minipass@7.0.4: resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==} engines: {node: '>=16 || 14 >=14.17'} @@ -1123,6 +2211,9 @@ packages: ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + mute-stream@0.0.8: + resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} + mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} @@ -1131,33 +2222,118 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + no-case@3.0.4: + resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + node-int64@0.4.0: + resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} + + node-releases@2.0.18: + resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==} + + normalize-path@2.1.1: + resolution: {integrity: sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==} + engines: {node: '>=0.10.0'} + normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} - npm-run-path@4.0.1: - resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + + npm-run-path@5.3.0: + resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + nullthrows@1.1.1: + resolution: {integrity: sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + onetime@6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + + ora@5.4.1: + resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} + engines: {node: '>=10'} + + os-tmpdir@1.0.2: + resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} + engines: {node: '>=0.10.0'} + + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-limit@5.0.0: + resolution: {integrity: sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==} + engines: {node: '>=18'} + + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + + p-map@4.0.0: + resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} + engines: {node: '>=10'} + + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + + param-case@3.0.4: + resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-filepath@1.0.2: + resolution: {integrity: sha512-FwdRXKCohSVeXqwtYonZTXtbGJKrn+HNyWDYVcp5yuJlesTwNH4rsmRZ+GrKAPJ5bLpRxESMeS+Rl0VCHRvB2Q==} + engines: {node: '>=0.8'} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} - npm-run-path@5.3.0: - resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} + pascal-case@3.1.2: + resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} - onetime@5.1.2: - resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} - engines: {node: '>=6'} + path-case@3.0.4: + resolution: {integrity: sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==} - onetime@6.0.0: - resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} - engines: {node: '>=12'} + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} - p-limit@5.0.0: - resolution: {integrity: sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==} - engines: {node: '>=18'} + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} @@ -1167,6 +2343,14 @@ packages: resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} engines: {node: '>=12'} + path-root-regex@0.1.2: + resolution: {integrity: sha512-4GlJ6rZDhQZFE0DPVKh0e9jmZ5egZfxTkp7bcRDuPlJXbAwhxcl2dINPUAsjLdejqaLsCeg8axcLjIbvBjN4pQ==} + engines: {node: '>=0.10.0'} + + path-root@0.1.1: + resolution: {integrity: sha512-QLcPegTHF11axjfojBIoDygmS2E3Lf+8+jI6wOVmNVenrKSo3mFdSGiIgdSHenczw3wPtlVMQaFVwGmM7BJdtg==} + engines: {node: '>=0.10.0'} + path-scurry@1.10.2: resolution: {integrity: sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==} engines: {node: '>=16 || 14 >=14.17'} @@ -1187,6 +2371,9 @@ packages: picocolors@1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} @@ -1221,6 +2408,9 @@ packages: resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + promise@7.3.1: + resolution: {integrity: sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -1231,10 +2421,40 @@ packages: react-is@18.3.0: resolution: {integrity: sha512-wRiUsea88TjKDc4FBEn+sLvIDesp6brMbGWnJGjew2waAc9evdhja/2LvePc898HJbHw0L+MTWy7NhpnELAvLQ==} + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + readdirp@3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} + regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + + relay-runtime@12.0.0: + resolution: {integrity: sha512-QU6JKr1tMsry22DXNy9Whsq5rmvwr3LSZiiWV/9+DFpuTWvp+WFhobWMc8TC4OjKFfNhEZy7mOiqUAn5atQtug==} + + remedial@1.0.8: + resolution: {integrity: sha512-/62tYiOe6DzS5BqVsNpH/nkGlX45C/Sp6V+NtiN6JQNS1Viay7cWkazmRkrQrdFj2eshDe96SIQNIoMxqhzBOg==} + + remove-trailing-separator@1.1.0: + resolution: {integrity: sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==} + + remove-trailing-spaces@1.0.8: + resolution: {integrity: sha512-O3vsMYfWighyFbTd8hk8VaSj9UAGENxAtX+//ugIst2RMk5e03h6RoIS+0ylsFxY1gvmPuAY/PO4It+gPEeySA==} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-main-filename@2.0.0: + resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + resolve-from@5.0.0: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} @@ -1242,6 +2462,10 @@ packages: resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + restore-cursor@3.1.0: + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} + reusify@1.0.4: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} @@ -1254,12 +2478,41 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + run-async@2.4.1: + resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} + engines: {node: '>=0.12.0'} + run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + rxjs@7.8.1: + resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + scuid@1.1.0: + resolution: {integrity: sha512-MuCAyrGZcTLfQoH2XoBlQ8C6bzwN88XT/0slOGz0pn8+gIP85BOAfYa44ZXQUTOwRwPU0QvgU+V+OSajl/59Xg==} + search-insights@2.14.0: resolution: {integrity: sha512-OLN6MsPMCghDOqlCtsIsYgtsC0pnwVTyT9Mu6A3ewOj1DxvzZF6COrn2g86E/c05xbktB0XN04m/t1Z+n+fTGw==} + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + sentence-case@3.0.4: + resolution: {integrity: sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==} + + set-blocking@2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + + setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -1268,6 +2521,10 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} + shell-quote@1.8.2: + resolution: {integrity: sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==} + engines: {node: '>= 0.4'} + shiki@1.6.1: resolution: {integrity: sha512-1Pu/A1rtsG6HZvQm4W0NExQ45e02og+rPog7PDaFDiMumZgOYnZIu4JtGQeAIfMwdbKSjJQoCUr79vDLKUUxWA==} @@ -1281,10 +2538,24 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + signedsource@1.0.0: + resolution: {integrity: sha512-6+eerH9fEnNmi/hyM1DXcRK3pWdoMQtlkQ+ns0ntzunjKqp5i3sKCc80ym8Fib3iaYhdJUOPdhlJWj1tvge2Ww==} + slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} + slice-ansi@3.0.0: + resolution: {integrity: sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==} + engines: {node: '>=8'} + + slice-ansi@4.0.0: + resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} + engines: {node: '>=10'} + + snake-case@3.0.4: + resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} + source-map-js@1.2.0: resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} engines: {node: '>=0.10.0'} @@ -1297,12 +2568,22 @@ packages: resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==} engines: {node: '>=0.10.0'} + sponge-case@1.0.1: + resolution: {integrity: sha512-dblb9Et4DAtiZ5YSUZHLl4XhH4uK80GhAZrVXdN4O2P4gQ40Wa5UIOPUHlA/nFd2PLblBZWUioLMMAVrgpoYcA==} + stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} std-env@3.7.0: resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} + streamsearch@1.1.0: + resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} + engines: {node: '>=10.0.0'} + + string-env-interpolation@1.0.1: + resolution: {integrity: sha512-78lwMoCcn0nNu8LszbP1UA7g55OeE4v7rCeWnM5B453rnNr4aq+5it3FEYtZrSEiMvHZOZ9Jlqb0OD0M2VInqg==} + string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -1311,6 +2592,9 @@ packages: resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} engines: {node: '>=12'} + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -1335,6 +2619,13 @@ packages: engines: {node: '>=16 || 14 >=14.17'} hasBin: true + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + swap-case@2.0.2: + resolution: {integrity: sha512-kc6S2YS/2yXbtkSMunBtKdah4VFETZ8Oh6ONSmSd9bRxhqTrtARUCBUiWXH3xVPpvR7tz2CSnkuXVE42EcGnMw==} + tabbable@6.2.0: resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==} @@ -1345,6 +2636,9 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + tinybench@2.8.0: resolution: {integrity: sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==} @@ -1356,6 +2650,13 @@ packages: resolution: {integrity: sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==} engines: {node: '>=14.0.0'} + title-case@3.0.3: + resolution: {integrity: sha512-e1zGYRvbffpcHIrnuqT0Dh+gEJtDaxDSoG4JAIpq4oDFyooziLBIiYQv0GBT4FUAnUop5uZ1hiIAj7oAF6sOCA==} + + tmp@0.0.33: + resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} + engines: {node: '>=0.6.0'} + to-fast-properties@2.0.0: resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} engines: {node: '>=4'} @@ -1364,6 +2665,9 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + tr46@1.0.1: resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} @@ -1374,6 +2678,18 @@ packages: ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + ts-log@2.2.7: + resolution: {integrity: sha512-320x5Ggei84AxzlXp91QkIGSw5wgaLT6GeAH0KsqDmRZdVWW2OiSeVvElVoatk3f7nicwXlElXsoFkARiGE2yg==} + + tslib@2.4.1: + resolution: {integrity: sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==} + + tslib@2.6.3: + resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + tsup@8.0.2: resolution: {integrity: sha512-NY8xtQXdH7hDUAZwcQdY/Vzlw9johQsaqf7iwZ6g1DOUlFYQ5/AtVAjTvihhEyeRlGo4dLRVHtrRaL35M1daqQ==} engines: {node: '>=18'} @@ -1402,21 +2718,59 @@ packages: resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} engines: {node: '>=4'} + type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + typescript@5.4.5: resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==} engines: {node: '>=14.17'} hasBin: true + ua-parser-js@1.0.39: + resolution: {integrity: sha512-k24RCVWlEcjkdOxYmVJgeD/0a1TiSpqLg+ZalVGV9lsnr4yqu0w7tX/x2xX6G4zpkgQnRf89lxuZ1wsbjXM8lw==} + hasBin: true + ufo@1.5.3: resolution: {integrity: sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==} + unc-path-regex@0.1.2: + resolution: {integrity: sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==} + engines: {node: '>=0.10.0'} + undici-types@6.20.0: resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} + unixify@1.0.0: + resolution: {integrity: sha512-6bc58dPYhCMHHuwxldQxO3RRNZ4eCogZ/st++0+fcC1nr0jiGUtAdBJ2qzmLQWSxbtz42pWt4QQMiZ9HvZf5cg==} + engines: {node: '>=0.10.0'} + + update-browserslist-db@1.1.1: + resolution: {integrity: sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + upper-case-first@2.0.2: + resolution: {integrity: sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==} + + upper-case@2.0.2: + resolution: {integrity: sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==} + + urlpattern-polyfill@10.0.0: + resolution: {integrity: sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + uuid@9.0.1: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} hasBin: true + value-or-promise@1.0.12: + resolution: {integrity: sha512-Z6Uz+TYwEqE7ZN50gwn+1LCVo9ZVrpxRPOhOLnncYkY1ZzOYtrX8Fwf/rFktZ8R5mJms6EZf5TqNOMeZmnPq9Q==} + engines: {node: '>=12'} + vite-node@1.5.2: resolution: {integrity: sha512-Y8p91kz9zU+bWtF7HGt6DVw2JbhyuB2RlZix3FPYAYmUyZ3n7iTp8eSyLyY6sxtPegvxQtmlTMhfPhUfCUF93A==} engines: {node: ^18.0.0 || >=20.0.0} @@ -1534,12 +2888,24 @@ packages: typescript: optional: true + wcwidth@1.0.1: + resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + webidl-conversions@4.0.2: resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + whatwg-url@7.1.0: resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} + which-module@2.0.1: + resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -1550,6 +2916,10 @@ packages: engines: {node: '>=8'} hasBin: true + wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -1558,11 +2928,59 @@ packages: resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} engines: {node: '>=12'} + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + ws@8.18.0: + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + y18n@4.0.3: + resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yaml-ast-parser@0.0.43: + resolution: {integrity: sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==} + yaml@2.4.1: resolution: {integrity: sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==} engines: {node: '>= 14'} hasBin: true + yargs-parser@18.1.3: + resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} + engines: {node: '>=6'} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@15.4.1: + resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} + engines: {node: '>=8'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + yocto-queue@1.0.0: resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} engines: {node: '>=12.20'} @@ -1673,13 +3091,364 @@ snapshots: '@algolia/logger-common': 4.23.3 '@algolia/requester-common': 4.23.3 - '@babel/helper-string-parser@7.24.6': {} + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + + '@ardatan/relay-compiler@12.0.0(graphql@16.9.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/generator': 7.26.3 + '@babel/parser': 7.24.6 + '@babel/runtime': 7.26.0 + '@babel/traverse': 7.26.4 + '@babel/types': 7.24.6 + babel-preset-fbjs: 3.4.0(@babel/core@7.26.0) + chalk: 4.1.2 + fb-watchman: 2.0.2 + fbjs: 3.0.5 + glob: 7.2.3 + graphql: 16.9.0 + immutable: 3.7.6 + invariant: 2.2.4 + nullthrows: 1.1.1 + relay-runtime: 12.0.0 + signedsource: 1.0.0 + yargs: 15.4.1 + transitivePeerDependencies: + - encoding + - supports-color + + '@ardatan/sync-fetch@0.0.1': + dependencies: + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + + '@babel/code-frame@7.26.2': + dependencies: + '@babel/helper-validator-identifier': 7.25.9 + js-tokens: 4.0.0 + picocolors: 1.0.0 + + '@babel/compat-data@7.26.3': {} + + '@babel/core@7.26.0': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.26.2 + '@babel/generator': 7.26.3 + '@babel/helper-compilation-targets': 7.25.9 + '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) + '@babel/helpers': 7.26.0 + '@babel/parser': 7.26.3 + '@babel/template': 7.25.9 + '@babel/traverse': 7.26.4 + '@babel/types': 7.26.3 + convert-source-map: 2.0.0 + debug: 4.3.4 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.26.3': + dependencies: + '@babel/parser': 7.26.3 + '@babel/types': 7.26.3 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + jsesc: 3.0.2 + + '@babel/helper-annotate-as-pure@7.25.9': + dependencies: + '@babel/types': 7.26.3 + + '@babel/helper-compilation-targets@7.25.9': + dependencies: + '@babel/compat-data': 7.26.3 + '@babel/helper-validator-option': 7.25.9 + browserslist: 4.24.2 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-create-class-features-plugin@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-annotate-as-pure': 7.25.9 + '@babel/helper-member-expression-to-functions': 7.25.9 + '@babel/helper-optimise-call-expression': 7.25.9 + '@babel/helper-replace-supers': 7.25.9(@babel/core@7.26.0) + '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 + '@babel/traverse': 7.26.4 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-member-expression-to-functions@7.25.9': + dependencies: + '@babel/traverse': 7.26.4 + '@babel/types': 7.26.3 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-imports@7.25.9': + dependencies: + '@babel/traverse': 7.26.4 + '@babel/types': 7.26.3 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.26.0(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-module-imports': 7.25.9 + '@babel/helper-validator-identifier': 7.25.9 + '@babel/traverse': 7.26.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-optimise-call-expression@7.25.9': + dependencies: + '@babel/types': 7.26.3 + + '@babel/helper-plugin-utils@7.25.9': {} + + '@babel/helper-replace-supers@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-member-expression-to-functions': 7.25.9 + '@babel/helper-optimise-call-expression': 7.25.9 + '@babel/traverse': 7.26.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-skip-transparent-expression-wrappers@7.25.9': + dependencies: + '@babel/traverse': 7.26.4 + '@babel/types': 7.26.3 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.24.6': {} + + '@babel/helper-string-parser@7.25.9': {} + + '@babel/helper-validator-identifier@7.24.6': {} + + '@babel/helper-validator-identifier@7.25.9': {} + + '@babel/helper-validator-option@7.25.9': {} + + '@babel/helpers@7.26.0': + dependencies: + '@babel/template': 7.25.9 + '@babel/types': 7.26.3 + + '@babel/parser@7.24.6': + dependencies: + '@babel/types': 7.24.6 + + '@babel/parser@7.26.3': + dependencies: + '@babel/types': 7.26.3 + + '@babel/plugin-proposal-class-properties@7.18.6(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-object-rest-spread@7.20.7(@babel/core@7.26.0)': + dependencies: + '@babel/compat-data': 7.26.3 + '@babel/core': 7.26.0 + '@babel/helper-compilation-targets': 7.25.9 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.26.0) + '@babel/plugin-transform-parameters': 7.25.9(@babel/core@7.26.0) + + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-flow@7.26.0(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-import-assertions@7.26.0(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-jsx@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-arrow-functions@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-block-scoped-functions@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-block-scoping@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-classes@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-annotate-as-pure': 7.25.9 + '@babel/helper-compilation-targets': 7.25.9 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-replace-supers': 7.25.9(@babel/core@7.26.0) + '@babel/traverse': 7.26.4 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-computed-properties@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/template': 7.25.9 + + '@babel/plugin-transform-destructuring@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-flow-strip-types@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/plugin-syntax-flow': 7.26.0(@babel/core@7.26.0) + + '@babel/plugin-transform-for-of@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-function-name@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-compilation-targets': 7.25.9 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/traverse': 7.26.4 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-literals@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-member-expression-literals@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-modules-commonjs@7.26.3(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-object-super@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-replace-supers': 7.25.9(@babel/core@7.26.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-parameters@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-property-literals@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-react-display-name@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-annotate-as-pure': 7.25.9 + '@babel/helper-module-imports': 7.25.9 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.0) + '@babel/types': 7.26.3 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-shorthand-properties@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-spread@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-template-literals@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/runtime@7.26.0': + dependencies: + regenerator-runtime: 0.14.1 - '@babel/helper-validator-identifier@7.24.6': {} + '@babel/template@7.25.9': + dependencies: + '@babel/code-frame': 7.26.2 + '@babel/parser': 7.26.3 + '@babel/types': 7.26.3 - '@babel/parser@7.24.6': + '@babel/traverse@7.26.4': dependencies: - '@babel/types': 7.24.6 + '@babel/code-frame': 7.26.2 + '@babel/generator': 7.26.3 + '@babel/parser': 7.26.3 + '@babel/template': 7.25.9 + '@babel/types': 7.26.3 + debug: 4.3.4 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color '@babel/types@7.24.6': dependencies: @@ -1687,6 +3456,11 @@ snapshots: '@babel/helper-validator-identifier': 7.24.6 to-fast-properties: 2.0.0 + '@babel/types@7.26.3': + dependencies: + '@babel/helper-string-parser': 7.25.9 + '@babel/helper-validator-identifier': 7.25.9 + '@biomejs/biome@1.7.1': optionalDependencies: '@biomejs/cli-darwin-arm64': 1.7.1 @@ -1884,6 +3658,508 @@ snapshots: '@esbuild/win32-x64@0.20.2': optional: true + '@graphql-codegen/add@5.0.3(graphql@16.9.0)': + dependencies: + '@graphql-codegen/plugin-helpers': 5.1.0(graphql@16.9.0) + graphql: 16.9.0 + tslib: 2.6.3 + + '@graphql-codegen/cli@5.0.3(@types/node@22.10.1)(graphql@16.9.0)(typescript@5.4.5)': + dependencies: + '@babel/generator': 7.26.3 + '@babel/template': 7.25.9 + '@babel/types': 7.24.6 + '@graphql-codegen/client-preset': 4.5.1(graphql@16.9.0) + '@graphql-codegen/core': 4.0.2(graphql@16.9.0) + '@graphql-codegen/plugin-helpers': 5.1.0(graphql@16.9.0) + '@graphql-tools/apollo-engine-loader': 8.0.7(graphql@16.9.0) + '@graphql-tools/code-file-loader': 8.1.8(graphql@16.9.0) + '@graphql-tools/git-loader': 8.0.12(graphql@16.9.0) + '@graphql-tools/github-loader': 8.0.7(@types/node@22.10.1)(graphql@16.9.0) + '@graphql-tools/graphql-file-loader': 8.0.6(graphql@16.9.0) + '@graphql-tools/json-file-loader': 8.0.6(graphql@16.9.0) + '@graphql-tools/load': 8.0.7(graphql@16.9.0) + '@graphql-tools/prisma-loader': 8.0.17(@types/node@22.10.1)(graphql@16.9.0) + '@graphql-tools/url-loader': 8.0.18(@types/node@22.10.1)(graphql@16.9.0) + '@graphql-tools/utils': 10.6.2(graphql@16.9.0) + '@whatwg-node/fetch': 0.9.23 + chalk: 4.1.2 + cosmiconfig: 8.3.6(typescript@5.4.5) + debounce: 1.2.1 + detect-indent: 6.1.0 + graphql: 16.9.0 + graphql-config: 5.1.3(@types/node@22.10.1)(graphql@16.9.0)(typescript@5.4.5) + inquirer: 8.2.6 + is-glob: 4.0.3 + jiti: 1.21.6 + json-to-pretty-yaml: 1.2.2 + listr2: 4.0.5 + log-symbols: 4.1.0 + micromatch: 4.0.5 + shell-quote: 1.8.2 + string-env-interpolation: 1.0.1 + ts-log: 2.2.7 + tslib: 2.8.1 + yaml: 2.4.1 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - bufferutil + - cosmiconfig-toml-loader + - encoding + - enquirer + - supports-color + - typescript + - utf-8-validate + + '@graphql-codegen/client-preset@4.5.1(graphql@16.9.0)': + dependencies: + '@babel/helper-plugin-utils': 7.25.9 + '@babel/template': 7.25.9 + '@graphql-codegen/add': 5.0.3(graphql@16.9.0) + '@graphql-codegen/gql-tag-operations': 4.0.12(graphql@16.9.0) + '@graphql-codegen/plugin-helpers': 5.1.0(graphql@16.9.0) + '@graphql-codegen/typed-document-node': 5.0.12(graphql@16.9.0) + '@graphql-codegen/typescript': 4.1.2(graphql@16.9.0) + '@graphql-codegen/typescript-operations': 4.4.0(graphql@16.9.0) + '@graphql-codegen/visitor-plugin-common': 5.6.0(graphql@16.9.0) + '@graphql-tools/documents': 1.0.1(graphql@16.9.0) + '@graphql-tools/utils': 10.6.2(graphql@16.9.0) + '@graphql-typed-document-node/core': 3.2.0(graphql@16.9.0) + graphql: 16.9.0 + tslib: 2.6.3 + transitivePeerDependencies: + - encoding + - supports-color + + '@graphql-codegen/core@4.0.2(graphql@16.9.0)': + dependencies: + '@graphql-codegen/plugin-helpers': 5.1.0(graphql@16.9.0) + '@graphql-tools/schema': 10.0.11(graphql@16.9.0) + '@graphql-tools/utils': 10.6.2(graphql@16.9.0) + graphql: 16.9.0 + tslib: 2.6.3 + + '@graphql-codegen/gql-tag-operations@4.0.12(graphql@16.9.0)': + dependencies: + '@graphql-codegen/plugin-helpers': 5.1.0(graphql@16.9.0) + '@graphql-codegen/visitor-plugin-common': 5.6.0(graphql@16.9.0) + '@graphql-tools/utils': 10.6.2(graphql@16.9.0) + auto-bind: 4.0.0 + graphql: 16.9.0 + tslib: 2.6.3 + transitivePeerDependencies: + - encoding + - supports-color + + '@graphql-codegen/plugin-helpers@2.7.2(graphql@16.9.0)': + dependencies: + '@graphql-tools/utils': 8.13.1(graphql@16.9.0) + change-case-all: 1.0.14 + common-tags: 1.8.2 + graphql: 16.9.0 + import-from: 4.0.0 + lodash: 4.17.21 + tslib: 2.4.1 + + '@graphql-codegen/plugin-helpers@3.1.2(graphql@16.9.0)': + dependencies: + '@graphql-tools/utils': 9.2.1(graphql@16.9.0) + change-case-all: 1.0.15 + common-tags: 1.8.2 + graphql: 16.9.0 + import-from: 4.0.0 + lodash: 4.17.21 + tslib: 2.4.1 + + '@graphql-codegen/plugin-helpers@5.1.0(graphql@16.9.0)': + dependencies: + '@graphql-tools/utils': 10.6.2(graphql@16.9.0) + change-case-all: 1.0.15 + common-tags: 1.8.2 + graphql: 16.9.0 + import-from: 4.0.0 + lodash: 4.17.21 + tslib: 2.6.3 + + '@graphql-codegen/schema-ast@4.1.0(graphql@16.9.0)': + dependencies: + '@graphql-codegen/plugin-helpers': 5.1.0(graphql@16.9.0) + '@graphql-tools/utils': 10.6.2(graphql@16.9.0) + graphql: 16.9.0 + tslib: 2.6.3 + + '@graphql-codegen/typed-document-node@5.0.12(graphql@16.9.0)': + dependencies: + '@graphql-codegen/plugin-helpers': 5.1.0(graphql@16.9.0) + '@graphql-codegen/visitor-plugin-common': 5.6.0(graphql@16.9.0) + auto-bind: 4.0.0 + change-case-all: 1.0.15 + graphql: 16.9.0 + tslib: 2.6.3 + transitivePeerDependencies: + - encoding + - supports-color + + '@graphql-codegen/typescript-graphql-request@6.2.0(graphql-request@6.1.0(graphql@16.9.0))(graphql-tag@2.12.6(graphql@16.9.0))(graphql@16.9.0)': + dependencies: + '@graphql-codegen/plugin-helpers': 3.1.2(graphql@16.9.0) + '@graphql-codegen/visitor-plugin-common': 2.13.1(graphql@16.9.0) + auto-bind: 4.0.0 + graphql: 16.9.0 + graphql-request: 6.1.0(graphql@16.9.0) + graphql-tag: 2.12.6(graphql@16.9.0) + tslib: 2.6.3 + transitivePeerDependencies: + - encoding + - supports-color + + '@graphql-codegen/typescript-operations@4.4.0(graphql@16.9.0)': + dependencies: + '@graphql-codegen/plugin-helpers': 5.1.0(graphql@16.9.0) + '@graphql-codegen/typescript': 4.1.2(graphql@16.9.0) + '@graphql-codegen/visitor-plugin-common': 5.6.0(graphql@16.9.0) + auto-bind: 4.0.0 + graphql: 16.9.0 + tslib: 2.6.3 + transitivePeerDependencies: + - encoding + - supports-color + + '@graphql-codegen/typescript@4.1.2(graphql@16.9.0)': + dependencies: + '@graphql-codegen/plugin-helpers': 5.1.0(graphql@16.9.0) + '@graphql-codegen/schema-ast': 4.1.0(graphql@16.9.0) + '@graphql-codegen/visitor-plugin-common': 5.6.0(graphql@16.9.0) + auto-bind: 4.0.0 + graphql: 16.9.0 + tslib: 2.6.3 + transitivePeerDependencies: + - encoding + - supports-color + + '@graphql-codegen/visitor-plugin-common@2.13.1(graphql@16.9.0)': + dependencies: + '@graphql-codegen/plugin-helpers': 2.7.2(graphql@16.9.0) + '@graphql-tools/optimize': 1.4.0(graphql@16.9.0) + '@graphql-tools/relay-operation-optimizer': 6.5.18(graphql@16.9.0) + '@graphql-tools/utils': 8.13.1(graphql@16.9.0) + auto-bind: 4.0.0 + change-case-all: 1.0.14 + dependency-graph: 0.11.0 + graphql: 16.9.0 + graphql-tag: 2.12.6(graphql@16.9.0) + parse-filepath: 1.0.2 + tslib: 2.4.1 + transitivePeerDependencies: + - encoding + - supports-color + + '@graphql-codegen/visitor-plugin-common@5.6.0(graphql@16.9.0)': + dependencies: + '@graphql-codegen/plugin-helpers': 5.1.0(graphql@16.9.0) + '@graphql-tools/optimize': 2.0.0(graphql@16.9.0) + '@graphql-tools/relay-operation-optimizer': 7.0.6(graphql@16.9.0) + '@graphql-tools/utils': 10.6.2(graphql@16.9.0) + auto-bind: 4.0.0 + change-case-all: 1.0.15 + dependency-graph: 0.11.0 + graphql: 16.9.0 + graphql-tag: 2.12.6(graphql@16.9.0) + parse-filepath: 1.0.2 + tslib: 2.6.3 + transitivePeerDependencies: + - encoding + - supports-color + + '@graphql-tools/apollo-engine-loader@8.0.7(graphql@16.9.0)': + dependencies: + '@ardatan/sync-fetch': 0.0.1 + '@graphql-tools/utils': 10.6.2(graphql@16.9.0) + '@whatwg-node/fetch': 0.10.1 + graphql: 16.9.0 + tslib: 2.8.1 + transitivePeerDependencies: + - encoding + + '@graphql-tools/batch-execute@9.0.10(graphql@16.9.0)': + dependencies: + '@graphql-tools/utils': 10.6.2(graphql@16.9.0) + dataloader: 2.2.3 + graphql: 16.9.0 + tslib: 2.8.1 + + '@graphql-tools/code-file-loader@8.1.8(graphql@16.9.0)': + dependencies: + '@graphql-tools/graphql-tag-pluck': 8.3.7(graphql@16.9.0) + '@graphql-tools/utils': 10.6.2(graphql@16.9.0) + globby: 11.1.0 + graphql: 16.9.0 + tslib: 2.8.1 + unixify: 1.0.0 + transitivePeerDependencies: + - supports-color + + '@graphql-tools/delegate@10.2.7(graphql@16.9.0)': + dependencies: + '@graphql-tools/batch-execute': 9.0.10(graphql@16.9.0) + '@graphql-tools/executor': 1.3.6(graphql@16.9.0) + '@graphql-tools/schema': 10.0.11(graphql@16.9.0) + '@graphql-tools/utils': 10.6.2(graphql@16.9.0) + '@repeaterjs/repeater': 3.0.6 + dataloader: 2.2.3 + dset: 3.1.4 + graphql: 16.9.0 + tslib: 2.8.1 + + '@graphql-tools/documents@1.0.1(graphql@16.9.0)': + dependencies: + graphql: 16.9.0 + lodash.sortby: 4.7.0 + tslib: 2.8.1 + + '@graphql-tools/executor-graphql-ws@1.3.5(graphql@16.9.0)': + dependencies: + '@graphql-tools/utils': 10.6.2(graphql@16.9.0) + '@whatwg-node/disposablestack': 0.0.5 + graphql: 16.9.0 + graphql-ws: 5.16.0(graphql@16.9.0) + isomorphic-ws: 5.0.0(ws@8.18.0) + tslib: 2.8.1 + ws: 8.18.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@graphql-tools/executor-http@1.1.14(@types/node@22.10.1)(graphql@16.9.0)': + dependencies: + '@graphql-tools/utils': 10.6.2(graphql@16.9.0) + '@repeaterjs/repeater': 3.0.6 + '@whatwg-node/disposablestack': 0.0.5 + '@whatwg-node/fetch': 0.10.1 + extract-files: 11.0.0 + graphql: 16.9.0 + meros: 1.3.0(@types/node@22.10.1) + tslib: 2.8.1 + value-or-promise: 1.0.12 + transitivePeerDependencies: + - '@types/node' + + '@graphql-tools/executor-legacy-ws@1.1.5(graphql@16.9.0)': + dependencies: + '@graphql-tools/utils': 10.6.2(graphql@16.9.0) + '@types/ws': 8.5.13 + graphql: 16.9.0 + isomorphic-ws: 5.0.0(ws@8.18.0) + tslib: 2.8.1 + ws: 8.18.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@graphql-tools/executor@1.3.6(graphql@16.9.0)': + dependencies: + '@graphql-tools/utils': 10.6.2(graphql@16.9.0) + '@graphql-typed-document-node/core': 3.2.0(graphql@16.9.0) + '@repeaterjs/repeater': 3.0.6 + graphql: 16.9.0 + tslib: 2.8.1 + value-or-promise: 1.0.12 + + '@graphql-tools/git-loader@8.0.12(graphql@16.9.0)': + dependencies: + '@graphql-tools/graphql-tag-pluck': 8.3.7(graphql@16.9.0) + '@graphql-tools/utils': 10.6.2(graphql@16.9.0) + graphql: 16.9.0 + is-glob: 4.0.3 + micromatch: 4.0.8 + tslib: 2.8.1 + unixify: 1.0.0 + transitivePeerDependencies: + - supports-color + + '@graphql-tools/github-loader@8.0.7(@types/node@22.10.1)(graphql@16.9.0)': + dependencies: + '@ardatan/sync-fetch': 0.0.1 + '@graphql-tools/executor-http': 1.1.14(@types/node@22.10.1)(graphql@16.9.0) + '@graphql-tools/graphql-tag-pluck': 8.3.7(graphql@16.9.0) + '@graphql-tools/utils': 10.6.2(graphql@16.9.0) + '@whatwg-node/fetch': 0.10.1 + graphql: 16.9.0 + tslib: 2.8.1 + value-or-promise: 1.0.12 + transitivePeerDependencies: + - '@types/node' + - encoding + - supports-color + + '@graphql-tools/graphql-file-loader@8.0.6(graphql@16.9.0)': + dependencies: + '@graphql-tools/import': 7.0.6(graphql@16.9.0) + '@graphql-tools/utils': 10.6.2(graphql@16.9.0) + globby: 11.1.0 + graphql: 16.9.0 + tslib: 2.8.1 + unixify: 1.0.0 + + '@graphql-tools/graphql-tag-pluck@8.3.7(graphql@16.9.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/parser': 7.24.6 + '@babel/plugin-syntax-import-assertions': 7.26.0(@babel/core@7.26.0) + '@babel/traverse': 7.26.4 + '@babel/types': 7.24.6 + '@graphql-tools/utils': 10.6.2(graphql@16.9.0) + graphql: 16.9.0 + tslib: 2.8.1 + transitivePeerDependencies: + - supports-color + + '@graphql-tools/import@7.0.6(graphql@16.9.0)': + dependencies: + '@graphql-tools/utils': 10.6.2(graphql@16.9.0) + graphql: 16.9.0 + resolve-from: 5.0.0 + tslib: 2.8.1 + + '@graphql-tools/json-file-loader@8.0.6(graphql@16.9.0)': + dependencies: + '@graphql-tools/utils': 10.6.2(graphql@16.9.0) + globby: 11.1.0 + graphql: 16.9.0 + tslib: 2.8.1 + unixify: 1.0.0 + + '@graphql-tools/load@8.0.7(graphql@16.9.0)': + dependencies: + '@graphql-tools/schema': 10.0.11(graphql@16.9.0) + '@graphql-tools/utils': 10.6.2(graphql@16.9.0) + graphql: 16.9.0 + p-limit: 3.1.0 + tslib: 2.8.1 + + '@graphql-tools/merge@9.0.12(graphql@16.9.0)': + dependencies: + '@graphql-tools/utils': 10.6.2(graphql@16.9.0) + graphql: 16.9.0 + tslib: 2.8.1 + + '@graphql-tools/optimize@1.4.0(graphql@16.9.0)': + dependencies: + graphql: 16.9.0 + tslib: 2.8.1 + + '@graphql-tools/optimize@2.0.0(graphql@16.9.0)': + dependencies: + graphql: 16.9.0 + tslib: 2.6.3 + + '@graphql-tools/prisma-loader@8.0.17(@types/node@22.10.1)(graphql@16.9.0)': + dependencies: + '@graphql-tools/url-loader': 8.0.18(@types/node@22.10.1)(graphql@16.9.0) + '@graphql-tools/utils': 10.6.2(graphql@16.9.0) + '@types/js-yaml': 4.0.9 + '@whatwg-node/fetch': 0.10.1 + chalk: 4.1.2 + debug: 4.3.4 + dotenv: 16.4.7 + graphql: 16.9.0 + graphql-request: 6.1.0(graphql@16.9.0) + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + jose: 5.9.6 + js-yaml: 4.1.0 + lodash: 4.17.21 + scuid: 1.1.0 + tslib: 2.8.1 + yaml-ast-parser: 0.0.43 + transitivePeerDependencies: + - '@types/node' + - bufferutil + - encoding + - supports-color + - utf-8-validate + + '@graphql-tools/relay-operation-optimizer@6.5.18(graphql@16.9.0)': + dependencies: + '@ardatan/relay-compiler': 12.0.0(graphql@16.9.0) + '@graphql-tools/utils': 9.2.1(graphql@16.9.0) + graphql: 16.9.0 + tslib: 2.8.1 + transitivePeerDependencies: + - encoding + - supports-color + + '@graphql-tools/relay-operation-optimizer@7.0.6(graphql@16.9.0)': + dependencies: + '@ardatan/relay-compiler': 12.0.0(graphql@16.9.0) + '@graphql-tools/utils': 10.6.2(graphql@16.9.0) + graphql: 16.9.0 + tslib: 2.6.3 + transitivePeerDependencies: + - encoding + - supports-color + + '@graphql-tools/schema@10.0.11(graphql@16.9.0)': + dependencies: + '@graphql-tools/merge': 9.0.12(graphql@16.9.0) + '@graphql-tools/utils': 10.6.2(graphql@16.9.0) + graphql: 16.9.0 + tslib: 2.8.1 + value-or-promise: 1.0.12 + + '@graphql-tools/url-loader@8.0.18(@types/node@22.10.1)(graphql@16.9.0)': + dependencies: + '@ardatan/sync-fetch': 0.0.1 + '@graphql-tools/executor-graphql-ws': 1.3.5(graphql@16.9.0) + '@graphql-tools/executor-http': 1.1.14(@types/node@22.10.1)(graphql@16.9.0) + '@graphql-tools/executor-legacy-ws': 1.1.5(graphql@16.9.0) + '@graphql-tools/utils': 10.6.2(graphql@16.9.0) + '@graphql-tools/wrap': 10.0.25(graphql@16.9.0) + '@types/ws': 8.5.13 + '@whatwg-node/fetch': 0.10.1 + graphql: 16.9.0 + isomorphic-ws: 5.0.0(ws@8.18.0) + tslib: 2.8.1 + value-or-promise: 1.0.12 + ws: 8.18.0 + transitivePeerDependencies: + - '@types/node' + - bufferutil + - encoding + - utf-8-validate + + '@graphql-tools/utils@10.6.2(graphql@16.9.0)': + dependencies: + '@graphql-typed-document-node/core': 3.2.0(graphql@16.9.0) + cross-inspect: 1.0.1 + dset: 3.1.4 + graphql: 16.9.0 + tslib: 2.8.1 + + '@graphql-tools/utils@8.13.1(graphql@16.9.0)': + dependencies: + graphql: 16.9.0 + tslib: 2.8.1 + + '@graphql-tools/utils@9.2.1(graphql@16.9.0)': + dependencies: + '@graphql-typed-document-node/core': 3.2.0(graphql@16.9.0) + graphql: 16.9.0 + tslib: 2.8.1 + + '@graphql-tools/wrap@10.0.25(graphql@16.9.0)': + dependencies: + '@graphql-tools/delegate': 10.2.7(graphql@16.9.0) + '@graphql-tools/schema': 10.0.11(graphql@16.9.0) + '@graphql-tools/utils': 10.6.2(graphql@16.9.0) + graphql: 16.9.0 + tslib: 2.8.1 + + '@graphql-typed-document-node/core@3.2.0(graphql@16.9.0)': + dependencies: + graphql: 16.9.0 + '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 @@ -1914,6 +4190,8 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.4.15 + '@kamilkisiela/fast-url-parser@1.1.4': {} + '@noble/hashes@1.4.0': {} '@noble/secp256k1@1.7.1': {} @@ -1946,6 +4224,8 @@ snapshots: '@planetarium/account': 5.4.1 '@planetarium/bencodex': 0.2.2 + '@repeaterjs/repeater@3.0.6': {} + '@rollup/rollup-android-arm-eabi@4.16.4': optional: true @@ -2004,6 +4284,8 @@ snapshots: '@types/estree@1.0.5': {} + '@types/js-yaml@4.0.9': {} + '@types/linkify-it@5.0.0': {} '@types/markdown-it@14.1.1': @@ -2021,6 +4303,10 @@ snapshots: '@types/web-bluetooth@0.0.20': {} + '@types/ws@8.5.13': + dependencies: + '@types/node': 22.10.1 + '@vitejs/plugin-vue@5.0.4(vite@5.2.12(@types/node@22.10.1))(vue@3.4.27(typescript@5.4.5))': dependencies: vite: 5.2.12(@types/node@22.10.1) @@ -2137,12 +4423,13 @@ snapshots: - '@vue/composition-api' - vue - '@vueuse/integrations@10.10.0(focus-trap@7.5.4)(vue@3.4.27(typescript@5.4.5))': + '@vueuse/integrations@10.10.0(change-case@4.1.2)(focus-trap@7.5.4)(vue@3.4.27(typescript@5.4.5))': dependencies: '@vueuse/core': 10.10.0(vue@3.4.27(typescript@5.4.5)) '@vueuse/shared': 10.10.0(vue@3.4.27(typescript@5.4.5)) vue-demi: 0.14.8(vue@3.4.27(typescript@5.4.5)) optionalDependencies: + change-case: 4.1.2 focus-trap: 7.5.4 transitivePeerDependencies: - '@vue/composition-api' @@ -2157,10 +4444,46 @@ snapshots: - '@vue/composition-api' - vue + '@whatwg-node/disposablestack@0.0.5': + dependencies: + tslib: 2.8.1 + + '@whatwg-node/fetch@0.10.1': + dependencies: + '@whatwg-node/node-fetch': 0.7.4 + urlpattern-polyfill: 10.0.0 + + '@whatwg-node/fetch@0.9.23': + dependencies: + '@whatwg-node/node-fetch': 0.6.0 + urlpattern-polyfill: 10.0.0 + + '@whatwg-node/node-fetch@0.6.0': + dependencies: + '@kamilkisiela/fast-url-parser': 1.1.4 + busboy: 1.6.0 + fast-querystring: 1.1.2 + tslib: 2.8.1 + + '@whatwg-node/node-fetch@0.7.4': + dependencies: + '@kamilkisiela/fast-url-parser': 1.1.4 + '@whatwg-node/disposablestack': 0.0.5 + busboy: 1.6.0 + fast-querystring: 1.1.2 + tslib: 2.8.1 + acorn-walk@8.3.2: {} acorn@8.11.3: {} + agent-base@7.1.3: {} + + aggregate-error@3.1.0: + dependencies: + clean-stack: 2.2.0 + indent-string: 4.0.0 + algoliasearch@4.23.3: dependencies: '@algolia/cache-browser-local-storage': 4.23.3 @@ -2179,6 +4502,10 @@ snapshots: '@algolia/requester-node-http': 4.23.3 '@algolia/transporter': 4.23.3 + ansi-escapes@4.3.2: + dependencies: + type-fest: 0.21.3 + ansi-regex@5.0.1: {} ansi-regex@6.0.1: {} @@ -2198,16 +4525,70 @@ snapshots: normalize-path: 3.0.0 picomatch: 2.3.1 + argparse@2.0.1: {} + array-union@2.1.0: {} + asap@2.0.6: {} + assertion-error@1.1.0: {} + astral-regex@2.0.0: {} + + auto-bind@4.0.0: {} + + babel-plugin-syntax-trailing-function-commas@7.0.0-beta.0: {} + + babel-preset-fbjs@3.4.0(@babel/core@7.26.0): + dependencies: + '@babel/core': 7.26.0 + '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.26.0) + '@babel/plugin-proposal-object-rest-spread': 7.20.7(@babel/core@7.26.0) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.26.0) + '@babel/plugin-syntax-flow': 7.26.0(@babel/core@7.26.0) + '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.26.0) + '@babel/plugin-transform-arrow-functions': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-block-scoped-functions': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-block-scoping': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-classes': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-computed-properties': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-destructuring': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-flow-strip-types': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-for-of': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-function-name': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-literals': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-member-expression-literals': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-modules-commonjs': 7.26.3(@babel/core@7.26.0) + '@babel/plugin-transform-object-super': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-parameters': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-property-literals': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-react-display-name': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-react-jsx': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-shorthand-properties': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-spread': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-template-literals': 7.25.9(@babel/core@7.26.0) + babel-plugin-syntax-trailing-function-commas: 7.0.0-beta.0 + transitivePeerDependencies: + - supports-color + balanced-match@1.0.2: {} base64-js@1.5.1: {} binary-extensions@2.3.0: {} + bl@4.1.0: + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + + brace-expansion@1.1.11: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + brace-expansion@2.0.1: dependencies: balanced-match: 1.0.2 @@ -2216,6 +4597,26 @@ snapshots: dependencies: fill-range: 7.0.1 + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.24.2: + dependencies: + caniuse-lite: 1.0.30001687 + electron-to-chromium: 1.5.71 + node-releases: 2.0.18 + update-browserslist-db: 1.1.1(browserslist@4.24.2) + + bser@2.1.1: + dependencies: + node-int64: 0.4.0 + + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + buffer@6.0.3: dependencies: base64-js: 1.5.1 @@ -2226,8 +4627,29 @@ snapshots: esbuild: 0.19.12 load-tsconfig: 0.2.5 + busboy@1.6.0: + dependencies: + streamsearch: 1.1.0 + cac@6.7.14: {} + callsites@3.1.0: {} + + camel-case@4.1.2: + dependencies: + pascal-case: 3.1.2 + tslib: 2.8.1 + + camelcase@5.3.1: {} + + caniuse-lite@1.0.30001687: {} + + capital-case@1.0.4: + dependencies: + no-case: 3.0.4 + tslib: 2.8.1 + upper-case-first: 2.0.2 + chai@4.4.1: dependencies: assertion-error: 1.1.0 @@ -2238,6 +4660,54 @@ snapshots: pathval: 1.1.1 type-detect: 4.0.8 + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + change-case-all@1.0.14: + dependencies: + change-case: 4.1.2 + is-lower-case: 2.0.2 + is-upper-case: 2.0.2 + lower-case: 2.0.2 + lower-case-first: 2.0.2 + sponge-case: 1.0.1 + swap-case: 2.0.2 + title-case: 3.0.3 + upper-case: 2.0.2 + upper-case-first: 2.0.2 + + change-case-all@1.0.15: + dependencies: + change-case: 4.1.2 + is-lower-case: 2.0.2 + is-upper-case: 2.0.2 + lower-case: 2.0.2 + lower-case-first: 2.0.2 + sponge-case: 1.0.1 + swap-case: 2.0.2 + title-case: 3.0.3 + upper-case: 2.0.2 + upper-case-first: 2.0.2 + + change-case@4.1.2: + dependencies: + camel-case: 4.1.2 + capital-case: 1.0.4 + constant-case: 3.0.4 + dot-case: 3.0.4 + header-case: 2.0.4 + no-case: 3.0.4 + param-case: 3.0.4 + pascal-case: 3.1.2 + path-case: 3.0.4 + sentence-case: 3.0.4 + snake-case: 3.0.4 + tslib: 2.8.1 + + chardet@0.7.0: {} + check-error@1.0.3: dependencies: get-func-name: 2.0.2 @@ -2254,16 +4724,78 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + clean-stack@2.2.0: {} + + cli-cursor@3.1.0: + dependencies: + restore-cursor: 3.1.0 + + cli-spinners@2.9.2: {} + + cli-truncate@2.1.0: + dependencies: + slice-ansi: 3.0.0 + string-width: 4.2.3 + + cli-width@3.0.0: {} + + cliui@6.0.0: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + clone@1.0.4: {} + color-convert@2.0.1: dependencies: color-name: 1.1.4 color-name@1.1.4: {} + colorette@2.0.20: {} + commander@4.1.1: {} + common-tags@1.8.2: {} + + concat-map@0.0.1: {} + confbox@0.1.7: {} + constant-case@3.0.4: + dependencies: + no-case: 3.0.4 + tslib: 2.8.1 + upper-case: 2.0.2 + + convert-source-map@2.0.0: {} + + cosmiconfig@8.3.6(typescript@5.4.5): + dependencies: + import-fresh: 3.3.0 + js-yaml: 4.1.0 + parse-json: 5.2.0 + path-type: 4.0.0 + optionalDependencies: + typescript: 5.4.5 + + cross-fetch@3.1.8: + dependencies: + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + + cross-inspect@1.0.1: + dependencies: + tslib: 2.8.1 + cross-spawn@7.0.3: dependencies: path-key: 3.1.1 @@ -2272,30 +4804,59 @@ snapshots: csstype@3.1.3: {} + dataloader@2.2.3: {} + + debounce@1.2.1: {} + debug@4.3.4: dependencies: ms: 2.1.2 + decamelize@1.2.0: {} + decimal.js@10.4.3: {} deep-eql@4.1.3: dependencies: type-detect: 4.0.8 + defaults@1.0.4: + dependencies: + clone: 1.0.4 + + dependency-graph@0.11.0: {} + + detect-indent@6.1.0: {} + diff-sequences@29.6.3: {} dir-glob@3.0.1: dependencies: path-type: 4.0.0 + dot-case@3.0.4: + dependencies: + no-case: 3.0.4 + tslib: 2.8.1 + + dotenv@16.4.7: {} + + dset@3.1.4: {} + eastasianwidth@0.2.0: {} + electron-to-chromium@1.5.71: {} + emoji-regex@8.0.0: {} emoji-regex@9.2.2: {} entities@4.5.0: {} + error-ex@1.3.2: + dependencies: + is-arrayish: 0.2.1 + esbuild@0.19.12: optionalDependencies: '@esbuild/aix-ppc64': 0.19.12 @@ -2348,6 +4909,10 @@ snapshots: '@esbuild/win32-ia32': 0.20.2 '@esbuild/win32-x64': 0.20.2 + escalade@3.2.0: {} + + escape-string-regexp@1.0.5: {} + estree-walker@2.0.2: {} estree-walker@3.0.3: @@ -2378,6 +4943,16 @@ snapshots: signal-exit: 4.1.0 strip-final-newline: 3.0.0 + external-editor@3.1.0: + dependencies: + chardet: 0.7.0 + iconv-lite: 0.4.24 + tmp: 0.0.33 + + extract-files@11.0.0: {} + + fast-decode-uri-component@1.0.1: {} + fast-glob@3.3.2: dependencies: '@nodelib/fs.stat': 2.0.5 @@ -2386,14 +4961,49 @@ snapshots: merge2: 1.4.1 micromatch: 4.0.5 + fast-querystring@1.1.2: + dependencies: + fast-decode-uri-component: 1.0.1 + fastq@1.17.1: dependencies: - reusify: 1.0.4 + reusify: 1.0.4 + + fb-watchman@2.0.2: + dependencies: + bser: 2.1.1 + + fbjs-css-vars@1.0.2: {} + + fbjs@3.0.5: + dependencies: + cross-fetch: 3.1.8 + fbjs-css-vars: 1.0.2 + loose-envify: 1.4.0 + object-assign: 4.1.1 + promise: 7.3.1 + setimmediate: 1.0.5 + ua-parser-js: 1.0.39 + transitivePeerDependencies: + - encoding + + figures@3.2.0: + dependencies: + escape-string-regexp: 1.0.5 fill-range@7.0.1: dependencies: to-regex-range: 5.0.1 + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + focus-trap@7.5.4: dependencies: tabbable: 6.2.0 @@ -2403,9 +5013,15 @@ snapshots: cross-spawn: 7.0.3 signal-exit: 4.1.0 + fs.realpath@1.0.0: {} + fsevents@2.3.3: optional: true + gensync@1.0.0-beta.2: {} + + get-caller-file@2.0.5: {} + get-func-name@2.0.2: {} get-stream@6.0.1: {} @@ -2428,6 +5044,17 @@ snapshots: minipass: 7.0.4 path-scurry: 1.10.2 + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + + globals@11.12.0: {} + globby@11.1.0: dependencies: array-union: 2.1.0 @@ -2437,16 +5064,128 @@ snapshots: merge2: 1.4.1 slash: 3.0.0 + graphql-config@5.1.3(@types/node@22.10.1)(graphql@16.9.0)(typescript@5.4.5): + dependencies: + '@graphql-tools/graphql-file-loader': 8.0.6(graphql@16.9.0) + '@graphql-tools/json-file-loader': 8.0.6(graphql@16.9.0) + '@graphql-tools/load': 8.0.7(graphql@16.9.0) + '@graphql-tools/merge': 9.0.12(graphql@16.9.0) + '@graphql-tools/url-loader': 8.0.18(@types/node@22.10.1)(graphql@16.9.0) + '@graphql-tools/utils': 10.6.2(graphql@16.9.0) + cosmiconfig: 8.3.6(typescript@5.4.5) + graphql: 16.9.0 + jiti: 2.4.1 + minimatch: 9.0.5 + string-env-interpolation: 1.0.1 + tslib: 2.8.1 + transitivePeerDependencies: + - '@types/node' + - bufferutil + - encoding + - typescript + - utf-8-validate + + graphql-request@6.1.0(graphql@16.9.0): + dependencies: + '@graphql-typed-document-node/core': 3.2.0(graphql@16.9.0) + cross-fetch: 3.1.8 + graphql: 16.9.0 + transitivePeerDependencies: + - encoding + + graphql-tag@2.12.6(graphql@16.9.0): + dependencies: + graphql: 16.9.0 + tslib: 2.8.1 + + graphql-ws@5.16.0(graphql@16.9.0): + dependencies: + graphql: 16.9.0 + + graphql@16.9.0: {} + + has-flag@4.0.0: {} + + header-case@2.0.4: + dependencies: + capital-case: 1.0.4 + tslib: 2.8.1 + hookable@5.5.3: {} + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.3 + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.3 + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + human-signals@2.1.0: {} human-signals@5.0.0: {} + iconv-lite@0.4.24: + dependencies: + safer-buffer: 2.1.2 + ieee754@1.2.1: {} ignore@5.3.1: {} + immutable@3.7.6: {} + + import-fresh@3.3.0: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + import-from@4.0.0: {} + + indent-string@4.0.0: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + inquirer@8.2.6: + dependencies: + ansi-escapes: 4.3.2 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-width: 3.0.0 + external-editor: 3.1.0 + figures: 3.2.0 + lodash: 4.17.21 + mute-stream: 0.0.8 + ora: 5.4.1 + run-async: 2.4.1 + rxjs: 7.8.1 + string-width: 4.2.3 + strip-ansi: 6.0.1 + through: 2.3.8 + wrap-ansi: 6.2.0 + + invariant@2.2.4: + dependencies: + loose-envify: 1.4.0 + + is-absolute@1.0.0: + dependencies: + is-relative: 1.0.0 + is-windows: 1.0.2 + + is-arrayish@0.2.1: {} + is-binary-path@2.1.0: dependencies: binary-extensions: 2.3.0 @@ -2459,28 +5198,88 @@ snapshots: dependencies: is-extglob: 2.1.1 + is-interactive@1.0.0: {} + + is-lower-case@2.0.2: + dependencies: + tslib: 2.8.1 + is-number@7.0.0: {} + is-relative@1.0.0: + dependencies: + is-unc-path: 1.0.0 + is-stream@2.0.1: {} is-stream@3.0.0: {} + is-unc-path@1.0.0: + dependencies: + unc-path-regex: 0.1.2 + + is-unicode-supported@0.1.0: {} + + is-upper-case@2.0.2: + dependencies: + tslib: 2.8.1 + + is-windows@1.0.2: {} + isexe@2.0.0: {} + isomorphic-ws@5.0.0(ws@8.18.0): + dependencies: + ws: 8.18.0 + jackspeak@2.3.6: dependencies: '@isaacs/cliui': 8.0.2 optionalDependencies: '@pkgjs/parseargs': 0.11.0 + jiti@1.21.6: {} + + jiti@2.4.1: {} + + jose@5.9.6: {} + joycon@3.1.1: {} + js-tokens@4.0.0: {} + js-tokens@9.0.0: {} + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + jsesc@3.0.2: {} + + json-parse-even-better-errors@2.3.1: {} + + json-to-pretty-yaml@1.2.2: + dependencies: + remedial: 1.0.8 + remove-trailing-spaces: 1.0.8 + + json5@2.2.3: {} + lilconfig@3.1.1: {} lines-and-columns@1.2.4: {} + listr2@4.0.5: + dependencies: + cli-truncate: 2.1.0 + colorette: 2.0.20 + log-update: 4.0.0 + p-map: 4.0.0 + rfdc: 1.3.1 + rxjs: 7.8.1 + through: 2.3.8 + wrap-ansi: 7.0.0 + load-tsconfig@0.2.5: {} local-pkg@0.5.0: @@ -2488,37 +5287,90 @@ snapshots: mlly: 1.6.1 pkg-types: 1.1.0 + locate-path@5.0.0: + dependencies: + p-locate: 4.1.0 + lodash.sortby@4.7.0: {} + lodash@4.17.21: {} + + log-symbols@4.1.0: + dependencies: + chalk: 4.1.2 + is-unicode-supported: 0.1.0 + + log-update@4.0.0: + dependencies: + ansi-escapes: 4.3.2 + cli-cursor: 3.1.0 + slice-ansi: 4.0.0 + wrap-ansi: 6.2.0 + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + loupe@2.3.7: dependencies: get-func-name: 2.0.2 + lower-case-first@2.0.2: + dependencies: + tslib: 2.8.1 + + lower-case@2.0.2: + dependencies: + tslib: 2.8.1 + lru-cache@10.2.1: {} + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + magic-string@0.30.10: dependencies: '@jridgewell/sourcemap-codec': 1.4.15 + map-cache@0.2.2: {} + mark.js@8.11.1: {} merge-stream@2.0.0: {} merge2@1.4.1: {} + meros@1.3.0(@types/node@22.10.1): + optionalDependencies: + '@types/node': 22.10.1 + micromatch@4.0.5: dependencies: braces: 3.0.2 picomatch: 2.3.1 + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + mimic-fn@2.1.0: {} mimic-fn@4.0.0: {} + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.11 + minimatch@9.0.4: dependencies: brace-expansion: 2.0.1 + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.1 + minipass@7.0.4: {} minisearch@6.3.0: {} @@ -2534,6 +5386,8 @@ snapshots: ms@2.1.2: {} + mute-stream@0.0.8: {} + mz@2.7.0: dependencies: any-promise: 1.3.0 @@ -2542,6 +5396,23 @@ snapshots: nanoid@3.3.7: {} + no-case@3.0.4: + dependencies: + lower-case: 2.0.2 + tslib: 2.8.1 + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + + node-int64@0.4.0: {} + + node-releases@2.0.18: {} + + normalize-path@2.1.1: + dependencies: + remove-trailing-separator: 1.1.0 + normalize-path@3.0.0: {} npm-run-path@4.0.1: @@ -2552,8 +5423,14 @@ snapshots: dependencies: path-key: 4.0.0 + nullthrows@1.1.1: {} + object-assign@4.1.1: {} + once@1.4.0: + dependencies: + wrappy: 1.0.2 + onetime@5.1.2: dependencies: mimic-fn: 2.1.0 @@ -2562,14 +5439,88 @@ snapshots: dependencies: mimic-fn: 4.0.0 + ora@5.4.1: + dependencies: + bl: 4.1.0 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-spinners: 2.9.2 + is-interactive: 1.0.0 + is-unicode-supported: 0.1.0 + log-symbols: 4.1.0 + strip-ansi: 6.0.1 + wcwidth: 1.0.1 + + os-tmpdir@1.0.2: {} + + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + p-limit@5.0.0: dependencies: yocto-queue: 1.0.0 + p-locate@4.1.0: + dependencies: + p-limit: 2.3.0 + + p-map@4.0.0: + dependencies: + aggregate-error: 3.1.0 + + p-try@2.2.0: {} + + param-case@3.0.4: + dependencies: + dot-case: 3.0.4 + tslib: 2.8.1 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-filepath@1.0.2: + dependencies: + is-absolute: 1.0.0 + map-cache: 0.2.2 + path-root: 0.1.1 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.26.2 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + pascal-case@3.1.2: + dependencies: + no-case: 3.0.4 + tslib: 2.8.1 + + path-case@3.0.4: + dependencies: + dot-case: 3.0.4 + tslib: 2.8.1 + + path-exists@4.0.0: {} + + path-is-absolute@1.0.1: {} + path-key@3.1.1: {} path-key@4.0.0: {} + path-root-regex@0.1.2: {} + + path-root@0.1.1: + dependencies: + path-root-regex: 0.1.2 + path-scurry@1.10.2: dependencies: lru-cache: 10.2.1 @@ -2585,6 +5536,8 @@ snapshots: picocolors@1.0.0: {} + picocolors@1.1.1: {} + picomatch@2.3.1: {} pirates@4.0.6: {} @@ -2616,20 +5569,57 @@ snapshots: ansi-styles: 5.2.0 react-is: 18.3.0 + promise@7.3.1: + dependencies: + asap: 2.0.6 + punycode@2.3.1: {} queue-microtask@1.2.3: {} react-is@18.3.0: {} + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + readdirp@3.6.0: dependencies: picomatch: 2.3.1 + regenerator-runtime@0.14.1: {} + + relay-runtime@12.0.0: + dependencies: + '@babel/runtime': 7.26.0 + fbjs: 3.0.5 + invariant: 2.2.4 + transitivePeerDependencies: + - encoding + + remedial@1.0.8: {} + + remove-trailing-separator@1.1.0: {} + + remove-trailing-spaces@1.0.8: {} + + require-directory@2.1.1: {} + + require-main-filename@2.0.0: {} + + resolve-from@4.0.0: {} + resolve-from@5.0.0: {} resolve-pkg-maps@1.0.0: {} + restore-cursor@3.1.0: + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + reusify@1.0.4: {} rfdc@1.3.1: {} @@ -2656,18 +5646,44 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.16.4 fsevents: 2.3.3 + run-async@2.4.1: {} + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 + rxjs@7.8.1: + dependencies: + tslib: 2.8.1 + + safe-buffer@5.2.1: {} + + safer-buffer@2.1.2: {} + + scuid@1.1.0: {} + search-insights@2.14.0: {} + semver@6.3.1: {} + + sentence-case@3.0.4: + dependencies: + no-case: 3.0.4 + tslib: 2.8.1 + upper-case-first: 2.0.2 + + set-blocking@2.0.0: {} + + setimmediate@1.0.5: {} + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 shebang-regex@3.0.0: {} + shell-quote@1.8.2: {} + shiki@1.6.1: dependencies: '@shikijs/core': 1.6.1 @@ -2678,8 +5694,27 @@ snapshots: signal-exit@4.1.0: {} + signedsource@1.0.0: {} + slash@3.0.0: {} + slice-ansi@3.0.0: + dependencies: + ansi-styles: 4.3.0 + astral-regex: 2.0.0 + is-fullwidth-code-point: 3.0.0 + + slice-ansi@4.0.0: + dependencies: + ansi-styles: 4.3.0 + astral-regex: 2.0.0 + is-fullwidth-code-point: 3.0.0 + + snake-case@3.0.4: + dependencies: + dot-case: 3.0.4 + tslib: 2.8.1 + source-map-js@1.2.0: {} source-map@0.8.0-beta.0: @@ -2688,10 +5723,18 @@ snapshots: speakingurl@14.0.1: {} + sponge-case@1.0.1: + dependencies: + tslib: 2.8.1 + stackback@0.0.2: {} std-env@3.7.0: {} + streamsearch@1.1.0: {} + + string-env-interpolation@1.0.1: {} + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -2704,6 +5747,10 @@ snapshots: emoji-regex: 9.2.2 strip-ansi: 7.1.0 + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 @@ -2730,6 +5777,14 @@ snapshots: pirates: 4.0.6 ts-interface-checker: 0.1.13 + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + swap-case@2.0.2: + dependencies: + tslib: 2.8.1 + tabbable@6.2.0: {} thenify-all@1.6.0: @@ -2740,18 +5795,30 @@ snapshots: dependencies: any-promise: 1.3.0 + through@2.3.8: {} + tinybench@2.8.0: {} tinypool@0.8.4: {} tinyspy@2.2.1: {} + title-case@3.0.3: + dependencies: + tslib: 2.8.1 + + tmp@0.0.33: + dependencies: + os-tmpdir: 1.0.2 + to-fast-properties@2.0.0: {} to-regex-range@5.0.1: dependencies: is-number: 7.0.0 + tr46@0.0.3: {} + tr46@1.0.1: dependencies: punycode: 2.3.1 @@ -2760,6 +5827,14 @@ snapshots: ts-interface-checker@0.1.13: {} + ts-log@2.2.7: {} + + tslib@2.4.1: {} + + tslib@2.6.3: {} + + tslib@2.8.1: {} + tsup@8.0.2(postcss@8.4.38)(typescript@5.4.5): dependencies: bundle-require: 4.0.3(esbuild@0.19.12) @@ -2792,14 +5867,44 @@ snapshots: type-detect@4.0.8: {} + type-fest@0.21.3: {} + typescript@5.4.5: {} + ua-parser-js@1.0.39: {} + ufo@1.5.3: {} + unc-path-regex@0.1.2: {} + undici-types@6.20.0: {} + unixify@1.0.0: + dependencies: + normalize-path: 2.1.1 + + update-browserslist-db@1.1.1(browserslist@4.24.2): + dependencies: + browserslist: 4.24.2 + escalade: 3.2.0 + picocolors: 1.1.1 + + upper-case-first@2.0.2: + dependencies: + tslib: 2.8.1 + + upper-case@2.0.2: + dependencies: + tslib: 2.8.1 + + urlpattern-polyfill@10.0.0: {} + + util-deprecate@1.0.2: {} + uuid@9.0.1: {} + value-or-promise@1.0.12: {} + vite-node@1.5.2(@types/node@22.10.1): dependencies: cac: 6.7.14 @@ -2835,7 +5940,7 @@ snapshots: '@types/node': 22.10.1 fsevents: 2.3.3 - vitepress@1.2.2(@algolia/client-search@4.23.3)(@types/node@22.10.1)(postcss@8.4.38)(search-insights@2.14.0)(typescript@5.4.5): + vitepress@1.2.2(@algolia/client-search@4.23.3)(@types/node@22.10.1)(change-case@4.1.2)(postcss@8.4.38)(search-insights@2.14.0)(typescript@5.4.5): dependencies: '@docsearch/css': 3.6.0 '@docsearch/js': 3.6.0(@algolia/client-search@4.23.3)(search-insights@2.14.0) @@ -2846,7 +5951,7 @@ snapshots: '@vue/devtools-api': 7.2.1(vue@3.4.27(typescript@5.4.5)) '@vue/shared': 3.4.27 '@vueuse/core': 10.10.0(vue@3.4.27(typescript@5.4.5)) - '@vueuse/integrations': 10.10.0(focus-trap@7.5.4)(vue@3.4.27(typescript@5.4.5)) + '@vueuse/integrations': 10.10.0(change-case@4.1.2)(focus-trap@7.5.4)(vue@3.4.27(typescript@5.4.5)) focus-trap: 7.5.4 mark.js: 8.11.1 minisearch: 6.3.0 @@ -2929,14 +6034,27 @@ snapshots: optionalDependencies: typescript: 5.4.5 + wcwidth@1.0.1: + dependencies: + defaults: 1.0.4 + + webidl-conversions@3.0.1: {} + webidl-conversions@4.0.2: {} + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + whatwg-url@7.1.0: dependencies: lodash.sortby: 4.7.0 tr46: 1.0.1 webidl-conversions: 4.0.2 + which-module@2.0.1: {} + which@2.0.2: dependencies: isexe: 2.0.0 @@ -2946,6 +6064,12 @@ snapshots: siginfo: 2.0.0 stackback: 0.0.2 + wrap-ansi@6.2.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 @@ -2958,6 +6082,51 @@ snapshots: string-width: 5.1.2 strip-ansi: 7.1.0 + wrappy@1.0.2: {} + + ws@8.18.0: {} + + y18n@4.0.3: {} + + y18n@5.0.8: {} + + yallist@3.1.1: {} + + yaml-ast-parser@0.0.43: {} + yaml@2.4.1: {} + yargs-parser@18.1.3: + dependencies: + camelcase: 5.3.1 + decamelize: 1.2.0 + + yargs-parser@21.1.1: {} + + yargs@15.4.1: + dependencies: + cliui: 6.0.0 + decamelize: 1.2.0 + find-up: 4.1.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + require-main-filename: 2.0.0 + set-blocking: 2.0.0 + string-width: 4.2.3 + which-module: 2.0.1 + y18n: 4.0.3 + yargs-parser: 18.1.3 + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yocto-queue@0.1.0: {} + yocto-queue@1.0.0: {} diff --git a/integrations/javascript/@planetarium/pnpm-workspace.yaml b/integrations/javascript/@planetarium/pnpm-workspace.yaml index 40ecc3c845..dc9f3f53fd 100644 --- a/integrations/javascript/@planetarium/pnpm-workspace.yaml +++ b/integrations/javascript/@planetarium/pnpm-workspace.yaml @@ -1,2 +1,3 @@ packages: - 'lib9c' + - '9c-headless-provider' From 01af74e1025f4449c017571e6b7e8a429048e26c Mon Sep 17 00:00:00 2001 From: moreal Date: Mon, 9 Dec 2024 20:13:07 +0900 Subject: [PATCH 085/136] ci: make jsr publish 9c-headless-provider too --- .github/workflows/publish.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 16fdace581..970e512f24 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -43,8 +43,12 @@ jobs: env: NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }} jsr: + strategy: + matrix: + package: [ "lib9c", "9c-headless-provider" ] + name: "jsr (@planetarium/${{ matrix.package }})" uses: planetarium/.github/.github/workflows/publish_jsr.yaml@bfb34283b538003768d19bff9ea05bcbd709d643 with: workspace_directory: "integrations/javascript/@planetarium" - working_directory: "integrations/javascript/@planetarium/lib9c" + working_directory: "integrations/javascript/@planetarium/${{ matrix.package }}" pnpm_version: "9" From 27c00313f906f6e6f4c21d567b77cbb08ed4c954 Mon Sep 17 00:00:00 2001 From: moreal Date: Mon, 9 Dec 2024 20:19:27 +0900 Subject: [PATCH 086/136] docs(js): update README --- .../9c-headless-provider/README.md | 26 +++++++++++++++++++ .../javascript/@planetarium/lib9c/README.md | 5 ++-- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/integrations/javascript/@planetarium/9c-headless-provider/README.md b/integrations/javascript/@planetarium/9c-headless-provider/README.md index 3e80d512db..7bcfae49cb 100644 --- a/integrations/javascript/@planetarium/9c-headless-provider/README.md +++ b/integrations/javascript/@planetarium/9c-headless-provider/README.md @@ -3,3 +3,29 @@ A library to provide `TxMetadataProvider` implementation for [NineChronicles.Headless][9c-headless]. [9c-headless]: https://github.com/planetarium/NineChronicles + +## Usage Example + +```typescript +import { HeadlessClient } from "@planetarium/9c-headless-provider"; +import { RawPrivateKey, Address } from "@planetarium/account"; +import { signTx } from "@planetarium/tx"; +import { makeTx, ClaimStakeReward } from "@planetarium/lib9c"; + +const headlessClient = new HeadlessClient("https://9c-main-full-state.nine-chronicles.com/graphql"); +const account = RawPrivateKey.generate(); // Temporary private key key. + +const unsignedTx = await makeTx(account, headlessClient, new ClaimStakeReward({ + avatarAddress: Address.fromHex('
'), +})); + +console.log(unsignedTx); + +const signedTx = await signTx(unsignedTx, account); + +console.log(signedTx); + +const txId = await headlessClient.stageTransaction(signedTx); + +console.log(txId); +``` diff --git a/integrations/javascript/@planetarium/lib9c/README.md b/integrations/javascript/@planetarium/lib9c/README.md index 6105541019..660b9cc83e 100644 --- a/integrations/javascript/@planetarium/lib9c/README.md +++ b/integrations/javascript/@planetarium/lib9c/README.md @@ -7,12 +7,11 @@ This npm package provides functions to build actions equivalent to [Lib9c]. ## Usage Example ```typescript -import { HeadlessNetworkProvider } from "@planetarium/9c-headless-provider"; import { RawPrivateKey, Address } from "@planetarium/account"; import { signTx } from "@planetarium/tx"; -import { makeTx, ClaimStakeReward } from "@planetarium/lib9c"; +import { makeTx, ClaimStakeReward, type TxMetadataProvider } from "@planetarium/lib9c"; -const networkProvider = new HeadlessNetworkProvider("https://9c-main-full-state.nine-chronicles.com/graphql"); +const provider: TxMetadataProvider; // You can use jsr:@planetarium/9c-headless-provider const account = RawPrivateKey.generate(); // Temporary private key key. const unsignedTx = await makeTx(account, networkProvider, new ClaimStakeReward({ From a1376ebe2af8ee3ebbceb526bc2769daea46bccd Mon Sep 17 00:00:00 2001 From: moreal Date: Mon, 9 Dec 2024 20:29:35 +0900 Subject: [PATCH 087/136] ci(gh-actions): fix working-directory --- .github/workflows/main.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 97261b24ea..3c416825bd 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -65,11 +65,11 @@ jobs: - name: Build Lib9c.Tools run: dotnet build .Lib9c.Tools/Lib9c.Tools.csproj - run: pnpm -r build - working-directory: "integrations/javascript/@planetarium/lib9c" + working-directory: "integrations/javascript/@planetarium" - run: pnpm -r fmt:ci - working-directory: "integrations/javascript/@planetarium/lib9c" + working-directory: "integrations/javascript/@planetarium" - run: pnpm -r test - working-directory: "integrations/javascript/@planetarium/lib9c" + working-directory: "integrations/javascript/@planetarium" release: if: github.ref_type == 'tag' && startsWith(github.ref_name, 'v') From e8a21cf5ef3d6c7bf0a83e1f85d6b63462a2ad99 Mon Sep 17 00:00:00 2001 From: moreal Date: Mon, 9 Dec 2024 20:30:54 +0900 Subject: [PATCH 088/136] chore: create test directory --- .../javascript/@planetarium/9c-headless-provider/tests/.gitkeep | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 integrations/javascript/@planetarium/9c-headless-provider/tests/.gitkeep diff --git a/integrations/javascript/@planetarium/9c-headless-provider/tests/.gitkeep b/integrations/javascript/@planetarium/9c-headless-provider/tests/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 From cc939514d9fdca438a985417c9f790daa415734b Mon Sep 17 00:00:00 2001 From: moreal Date: Mon, 9 Dec 2024 20:38:37 +0900 Subject: [PATCH 089/136] chore: update scripts in `package.json` --- .../javascript/@planetarium/9c-headless-provider/package.json | 2 +- integrations/javascript/@planetarium/lib9c/package.json | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/integrations/javascript/@planetarium/9c-headless-provider/package.json b/integrations/javascript/@planetarium/9c-headless-provider/package.json index 20dd2510cd..98c09b82f7 100644 --- a/integrations/javascript/@planetarium/9c-headless-provider/package.json +++ b/integrations/javascript/@planetarium/9c-headless-provider/package.json @@ -9,7 +9,7 @@ "codegen": "graphql-codegen --config ./graphql/headless/codegen.yml", "fmt": "biome check --apply src tests", "fmt:ci": "biome check src tests", - "test": "vitest" + "test": "echo 'There is no tests yet.'" }, "keywords": [], "author": "Planetarium", diff --git a/integrations/javascript/@planetarium/lib9c/package.json b/integrations/javascript/@planetarium/lib9c/package.json index 8b9f4f54a8..cecaf7d0f2 100644 --- a/integrations/javascript/@planetarium/lib9c/package.json +++ b/integrations/javascript/@planetarium/lib9c/package.json @@ -20,6 +20,7 @@ "fmt": "biome check --apply src tests", "fmt:ci": "biome check src tests", "test": "vitest", + "test:ci": "vitest run", "docs:dev": "vitepress dev docs", "docs:build": "vitepress build docs", "docs:preview": "vitepress preview docs" From b405a8d2d04090913d990c3b2cd55cb9e3da134b Mon Sep 17 00:00:00 2001 From: moreal Date: Mon, 9 Dec 2024 20:41:06 +0900 Subject: [PATCH 090/136] ci(gh-actions): run codegen if it needs --- .github/workflows/main.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3c416825bd..69da4d0e67 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -64,6 +64,8 @@ jobs: cwd: "integrations/javascript/@planetarium" - name: Build Lib9c.Tools run: dotnet build .Lib9c.Tools/Lib9c.Tools.csproj + - run: pnpm -r codegen + working-directory: "integrations/javascript/@planetarium" - run: pnpm -r build working-directory: "integrations/javascript/@planetarium" - run: pnpm -r fmt:ci From 945f1bf69ecdce0fabb3c1b2e92329ee8a9514c9 Mon Sep 17 00:00:00 2001 From: moreal Date: Mon, 9 Dec 2024 21:00:22 +0900 Subject: [PATCH 091/136] ci(gh-actions): correct jsr publish workflow --- .github/workflows/publish.yml | 32 ++++++++++++++++--- .../9c-headless-provider/jsr.json | 3 ++ .../javascript/@planetarium/package.json | 7 ++++ 3 files changed, 37 insertions(+), 5 deletions(-) create mode 100644 integrations/javascript/@planetarium/package.json diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 970e512f24..40dcbc0b06 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -47,8 +47,30 @@ jobs: matrix: package: [ "lib9c", "9c-headless-provider" ] name: "jsr (@planetarium/${{ matrix.package }})" - uses: planetarium/.github/.github/workflows/publish_jsr.yaml@bfb34283b538003768d19bff9ea05bcbd709d643 - with: - workspace_directory: "integrations/javascript/@planetarium" - working_directory: "integrations/javascript/@planetarium/${{ matrix.package }}" - pnpm_version: "9" + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + steps: + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v3 + with: + version: "9" + run_install: | + cwd: "integrations/javascript/@planetarium/" + recursive: true + - run: pnpm -r codegen + working-directory: "integrations/javascript/@planetarium" + - run: pnpm -r build + working-directory: "integrations/javascript/@planetarium" + - name: choose version + working-directory: "integrations/javascript/@planetarium/${{ matrix.package }}" + run: | + if [[ ! "$GITHUB_REF" =~ ^refs/tags/[0-9]+.[0-9]+.[0-9]+$ ]]; then + timestamp=$(date -u "+%Y%m%d%H%M%S%4N") + suffix="-dev.$timestamp+${{ github.sha }}" + jq ".version = .version + \"$suffix\"" jsr.json > jsr.json.tmp + mv jsr.json.tmp jsr.json + fi + - run: npx jsr publish --allow-dirty + working-directory: "integrations/javascript/@planetarium/${{ matrix.package }}" diff --git a/integrations/javascript/@planetarium/9c-headless-provider/jsr.json b/integrations/javascript/@planetarium/9c-headless-provider/jsr.json index 744cef2698..0390fa1d2a 100644 --- a/integrations/javascript/@planetarium/9c-headless-provider/jsr.json +++ b/integrations/javascript/@planetarium/9c-headless-provider/jsr.json @@ -9,6 +9,9 @@ "src/**/*.ts", "README.md", "jsr.json" + ], + "exclude": [ + "!src/generated" ] } } diff --git a/integrations/javascript/@planetarium/package.json b/integrations/javascript/@planetarium/package.json new file mode 100644 index 0000000000..eb796aa262 --- /dev/null +++ b/integrations/javascript/@planetarium/package.json @@ -0,0 +1,7 @@ +{ + "private": true, + "workspaces": [ + "lib9c", + "9c-headless-provider" + ] +} From fe3234fc90ecb6df4c0f314f43c6a744ff36d219 Mon Sep 17 00:00:00 2001 From: moreal Date: Mon, 9 Dec 2024 22:19:28 +0900 Subject: [PATCH 092/136] chore: prepare lib9c.js 0.5.0 --- integrations/javascript/@planetarium/lib9c/jsr.json | 2 +- integrations/javascript/@planetarium/lib9c/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/integrations/javascript/@planetarium/lib9c/jsr.json b/integrations/javascript/@planetarium/lib9c/jsr.json index f8597242df..c226c3d164 100644 --- a/integrations/javascript/@planetarium/lib9c/jsr.json +++ b/integrations/javascript/@planetarium/lib9c/jsr.json @@ -1,7 +1,7 @@ { "$schema": "https://jsr.io/schema/config-file.v1.json", "name": "@planetarium/lib9c", - "version": "0.4.0", + "version": "0.5.0", "exports": "./src/index.ts", "publish": { "include": [ diff --git a/integrations/javascript/@planetarium/lib9c/package.json b/integrations/javascript/@planetarium/lib9c/package.json index cecaf7d0f2..1746929268 100644 --- a/integrations/javascript/@planetarium/lib9c/package.json +++ b/integrations/javascript/@planetarium/lib9c/package.json @@ -1,6 +1,6 @@ { "name": "@planetarium/lib9c", - "version": "0.4.0", + "version": "0.5.0", "description": "", "module": "dist/index.js", "exports": { From 6b378bde1bdd93d8d7d8c4b51cd7a99f11e90da9 Mon Sep 17 00:00:00 2001 From: sonohoshi Date: Tue, 10 Dec 2024 15:42:57 +0900 Subject: [PATCH 093/136] feat: validate about RuneSlotInfo argument at BattleArena --- Lib9c/Action/BattleArena.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Lib9c/Action/BattleArena.cs b/Lib9c/Action/BattleArena.cs index c3bbf7bee3..4a30d6ef0d 100644 --- a/Lib9c/Action/BattleArena.cs +++ b/Lib9c/Action/BattleArena.cs @@ -354,6 +354,11 @@ public override IWorld Execute(IActionContext context) states = states.SetRuneState(myAvatarAddress, myRuneStates); } + foreach (var runeSlotInfo in runeInfos) + { + myRuneStates.GetRuneState(runeSlotInfo.RuneId); + } + // get enemy equipped items var enemyItemSlotStateAddress = ItemSlotState.DeriveAddress(enemyAvatarAddress, BattleType.Arena); var enemyItemSlotState = states.TryGetLegacyState(enemyItemSlotStateAddress, out List rawEnemyItemSlotState) From 9d9ce82f5b499b3017ae5d356b30eb6f70a42d33 Mon Sep 17 00:00:00 2001 From: sonohoshi Date: Tue, 10 Dec 2024 17:14:05 +0900 Subject: [PATCH 094/136] chore: add unit test about RuneNotFoundException --- .Lib9c.Tests/Action/BattleArenaTest.cs | 64 ++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/.Lib9c.Tests/Action/BattleArenaTest.cs b/.Lib9c.Tests/Action/BattleArenaTest.cs index d427263f8e..41dcf9aaf8 100644 --- a/.Lib9c.Tests/Action/BattleArenaTest.cs +++ b/.Lib9c.Tests/Action/BattleArenaTest.cs @@ -1119,6 +1119,70 @@ public void Execute_ValidateDuplicateTicketPurchaseException() })); } + [Fact] + public void ExecuteRuneNotFoundException() + { + var previousStates = _initialStates; + var context = new ActionContext(); + Assert.True( + previousStates.GetSheet().TryGetValue( + 1, + out var row)); + + if (!row.TryGetRound(1, out var roundData)) + { + throw new RoundNotFoundException( + $"[{nameof(BattleArena)}] ChampionshipId({row.ChampionshipId}) - round({1})"); + } + + if (roundData.ArenaType != ArenaType.OffSeason) + { + throw new InvalidSeasonException( + $"[{nameof(BattleArena)}] This test is only for OffSeason. ArenaType : {roundData.ArenaType}"); + } + + var random = new TestRandom(); + previousStates = JoinArena( + context, + previousStates, + _agent1Address, + _avatar1Address, + roundData.StartBlockIndex, + 1, + 1, + random); + previousStates = JoinArena( + context, + previousStates, + _agent2Address, + _avatar2Address, + roundData.StartBlockIndex, + 1, + 1, + random); + + var action = new BattleArena + { + myAvatarAddress = _avatar1Address, + enemyAvatarAddress = _avatar2Address, + championshipId = 1, + round = 1, + ticket = 1, + costumes = new List(), + equipments = new List(), + runeInfos = new List { new (0, 10035), }, + }; + Assert.Throws( + () => action.Execute( + new ActionContext + { + BlockIndex = roundData.StartBlockIndex + 1, + PreviousState = previousStates, + Signer = _agent1Address, + RandomSeed = 0, + })); + } + [Theory] [InlineData(8, null)] [InlineData(100, null)] From c10a410a307bf3594d773d8e623dc52a6fde82f1 Mon Sep 17 00:00:00 2001 From: sonohoshi Date: Tue, 10 Dec 2024 17:18:39 +0900 Subject: [PATCH 095/136] chore: add comment about runeSlotInfo validating --- Lib9c/Action/BattleArena.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib9c/Action/BattleArena.cs b/Lib9c/Action/BattleArena.cs index 4a30d6ef0d..c413843398 100644 --- a/Lib9c/Action/BattleArena.cs +++ b/Lib9c/Action/BattleArena.cs @@ -354,6 +354,7 @@ public override IWorld Execute(IActionContext context) states = states.SetRuneState(myAvatarAddress, myRuneStates); } + // just validate foreach (var runeSlotInfo in runeInfos) { myRuneStates.GetRuneState(runeSlotInfo.RuneId); From 64e69edab717fa961ece6427880615754e8656c6 Mon Sep 17 00:00:00 2001 From: moreal Date: Wed, 11 Dec 2024 16:24:00 +0900 Subject: [PATCH 096/136] fix(js): use published `@planetarium/lib9c` --- integrations/javascript/@planetarium/.npmrc | 1 + .../9c-headless-provider/package.json | 14 +++++++------- .../javascript/@planetarium/pnpm-lock.yaml | 18 ++++++++++++++++-- 3 files changed, 24 insertions(+), 9 deletions(-) create mode 100644 integrations/javascript/@planetarium/.npmrc diff --git a/integrations/javascript/@planetarium/.npmrc b/integrations/javascript/@planetarium/.npmrc new file mode 100644 index 0000000000..41583e36ca --- /dev/null +++ b/integrations/javascript/@planetarium/.npmrc @@ -0,0 +1 @@ +@jsr:registry=https://npm.jsr.io diff --git a/integrations/javascript/@planetarium/9c-headless-provider/package.json b/integrations/javascript/@planetarium/9c-headless-provider/package.json index 98c09b82f7..37cd6d284d 100644 --- a/integrations/javascript/@planetarium/9c-headless-provider/package.json +++ b/integrations/javascript/@planetarium/9c-headless-provider/package.json @@ -16,22 +16,22 @@ "license": "AGPL-3.0-only", "dependencies": { "@planetarium/account": "^5.4.1", - "@planetarium/tx": "^5.4.1", "@planetarium/bencodex": "^0.2.2", - "@planetarium/lib9c": "workspace:^", + "@planetarium/lib9c": "npm:@jsr/planetarium__lib9c@^0.4.0", + "@planetarium/tx": "^5.4.1", "graphql-request": "^6.0.0", "graphql-tag": "^2.12.6" }, "devDependencies": { "@biomejs/biome": "^1.7.1", + "@graphql-codegen/cli": "^5.0.2", + "@graphql-codegen/typescript": "^4.0.0", + "@graphql-codegen/typescript-graphql-request": "^6.2.0", + "@graphql-codegen/typescript-operations": "^4.2.0", "@types/node": "^22.10.1", "tsup": "^8.0.2", "tsx": "^4.7.3", "typescript": "^5.4.5", - "vitest": "^1.5.2", - "@graphql-codegen/cli": "^5.0.2", - "@graphql-codegen/typescript": "^4.0.0", - "@graphql-codegen/typescript-graphql-request": "^6.2.0", - "@graphql-codegen/typescript-operations": "^4.2.0" + "vitest": "^1.5.2" } } diff --git a/integrations/javascript/@planetarium/pnpm-lock.yaml b/integrations/javascript/@planetarium/pnpm-lock.yaml index 1e6d109cbd..7a74cbcd68 100644 --- a/integrations/javascript/@planetarium/pnpm-lock.yaml +++ b/integrations/javascript/@planetarium/pnpm-lock.yaml @@ -6,6 +6,8 @@ settings: importers: + .: {} + 9c-headless-provider: dependencies: '@planetarium/account': @@ -15,8 +17,8 @@ importers: specifier: ^0.2.2 version: 0.2.2 '@planetarium/lib9c': - specifier: workspace:^ - version: link:../lib9c + specifier: npm:@jsr/planetarium__lib9c@^0.4.0 + version: '@jsr/planetarium__lib9c@0.4.0' '@planetarium/tx': specifier: ^5.4.1 version: 5.4.1(@planetarium/account@5.4.1) @@ -1104,6 +1106,9 @@ packages: '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@jsr/planetarium__lib9c@0.4.0': + resolution: {integrity: sha512-bzMzIxdzuRkxwO03TgjTHNvgnhCrqxVF6Bbn3MY8yejOQ6JJeK/qlF5yf0Fk17sSjYtMOmAGSHP5V88HtMaXOg==, tarball: https://npm.jsr.io/~/11/@jsr/planetarium__lib9c/0.4.0.tgz} + '@kamilkisiela/fast-url-parser@1.1.4': resolution: {integrity: sha512-gbkePEBupNydxCelHCESvFSFM8XPh1Zs/OAVRW/rKpEqPAl5PbOM90Si8mv9bvnR53uPD2s/FiRxdvSejpRJew==} @@ -4190,6 +4195,15 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.4.15 + '@jsr/planetarium__lib9c@0.4.0': + dependencies: + '@planetarium/account': 5.4.1 + '@planetarium/bencodex': 0.2.2 + '@planetarium/tx': 5.4.1(@planetarium/account@5.4.1) + buffer: 6.0.3 + decimal.js: 10.4.3 + uuid: 9.0.1 + '@kamilkisiela/fast-url-parser@1.1.4': {} '@noble/hashes@1.4.0': {} From 49a162999511a5dbb6fad28b6808c7536c8cc4c6 Mon Sep 17 00:00:00 2001 From: moreal Date: Wed, 11 Dec 2024 17:39:21 +0900 Subject: [PATCH 097/136] fix(js): avoid confusion between jsr pkg and monorepo pkg --- .../@planetarium/9c-headless-provider/package.json | 2 +- .../@planetarium/9c-headless-provider/src/index.ts | 2 +- integrations/javascript/@planetarium/pnpm-lock.yaml | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/integrations/javascript/@planetarium/9c-headless-provider/package.json b/integrations/javascript/@planetarium/9c-headless-provider/package.json index 37cd6d284d..29e30c54bf 100644 --- a/integrations/javascript/@planetarium/9c-headless-provider/package.json +++ b/integrations/javascript/@planetarium/9c-headless-provider/package.json @@ -17,7 +17,7 @@ "dependencies": { "@planetarium/account": "^5.4.1", "@planetarium/bencodex": "^0.2.2", - "@planetarium/lib9c": "npm:@jsr/planetarium__lib9c@^0.4.0", + "@jsr/planetarium__lib9c": "^0.4.0", "@planetarium/tx": "^5.4.1", "graphql-request": "^6.0.0", "graphql-tag": "^2.12.6" diff --git a/integrations/javascript/@planetarium/9c-headless-provider/src/index.ts b/integrations/javascript/@planetarium/9c-headless-provider/src/index.ts index 7dfd9b5e8d..891c53565b 100644 --- a/integrations/javascript/@planetarium/9c-headless-provider/src/index.ts +++ b/integrations/javascript/@planetarium/9c-headless-provider/src/index.ts @@ -1,6 +1,6 @@ +import type { TxMetadataProvider } from "@jsr/planetarium__lib9c"; import type { Address } from "@planetarium/account"; import { encode } from "@planetarium/bencodex"; -import type { TxMetadataProvider } from "@planetarium/lib9c"; import { encodeSignedTx, type signTx } from "@planetarium/tx"; import { GraphQLClient } from "graphql-request"; import { type Sdk, getSdk } from "./generated/headless/graphql-request.js"; diff --git a/integrations/javascript/@planetarium/pnpm-lock.yaml b/integrations/javascript/@planetarium/pnpm-lock.yaml index 7a74cbcd68..d696fcd004 100644 --- a/integrations/javascript/@planetarium/pnpm-lock.yaml +++ b/integrations/javascript/@planetarium/pnpm-lock.yaml @@ -10,15 +10,15 @@ importers: 9c-headless-provider: dependencies: + '@jsr/planetarium__lib9c': + specifier: ^0.4.0 + version: 0.4.0 '@planetarium/account': specifier: ^5.4.1 version: 5.4.1 '@planetarium/bencodex': specifier: ^0.2.2 version: 0.2.2 - '@planetarium/lib9c': - specifier: npm:@jsr/planetarium__lib9c@^0.4.0 - version: '@jsr/planetarium__lib9c@0.4.0' '@planetarium/tx': specifier: ^5.4.1 version: 5.4.1(@planetarium/account@5.4.1) From 7628d5a739632224613a6a713c2f8b54220310ca Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Thu, 12 Dec 2024 13:50:27 +0900 Subject: [PATCH 098/136] remove grade key in synthesize --- .Lib9c.Tests/Action/SynthesizeTest.cs | 8 +++++ Lib9c/Helper/SynthesizeSimulator.cs | 14 ++++---- Lib9c/TableCSV/Item/SynthesizeWeightSheet.csv | 13 ++++---- Lib9c/TableData/Item/SynthesizeWeightSheet.cs | 32 +++---------------- 4 files changed, 27 insertions(+), 40 deletions(-) diff --git a/.Lib9c.Tests/Action/SynthesizeTest.cs b/.Lib9c.Tests/Action/SynthesizeTest.cs index cda7f63720..db8ae19d23 100644 --- a/.Lib9c.Tests/Action/SynthesizeTest.cs +++ b/.Lib9c.Tests/Action/SynthesizeTest.cs @@ -256,6 +256,14 @@ public void ExecuteMultiple(Grade grade, ItemSubType itemSubType) if (result.IsSuccess) { Assert.Equal((int)grade + 1, result.ItemBase.Grade); + + var weightSheet = TableSheets.SynthesizeWeightSheet; + var weightRow = weightSheet.Values.FirstOrDefault(r => r.ItemId == result.ItemBase.Id); + + if (weightRow != null) + { + Assert.True(weightRow.Weight != 0); + } } else { diff --git a/Lib9c/Helper/SynthesizeSimulator.cs b/Lib9c/Helper/SynthesizeSimulator.cs index 8bab8c5df9..e5d1493413 100644 --- a/Lib9c/Helper/SynthesizeSimulator.cs +++ b/Lib9c/Helper/SynthesizeSimulator.cs @@ -384,7 +384,7 @@ private static ItemBase GetRandomCostume(Grade grade, bool isSuccess, ItemSubTyp throw new InvalidOperationException($"No available items to synthesize for grade {grade} and subtype {itemSubType}"); } - var randomValue = GetRandomValueForItem(grade, synthesizeResultPool, weightSheet, random, out var itemWeights); + var randomValue = GetRandomValueForItem(synthesizeResultPool, weightSheet, random, out var itemWeights); var cumulativeWeight = 0; foreach (var (itemId, weight) in itemWeights) { @@ -440,7 +440,7 @@ private static ItemBase GetRandomEquipment( throw new InvalidOperationException($"No available items to synthesize for grade {grade} and subtype {itemSubType}"); } - var randomValue = GetRandomValueForItem(grade, synthesizeResultPool, weightSheet, random, out var itemWeights); + var randomValue = GetRandomValueForItem(synthesizeResultPool, weightSheet, random, out var itemWeights); var cumulativeWeight = 0; foreach (var (itemId, weight) in itemWeights) { @@ -506,14 +506,14 @@ private static ItemBase GetRandomEquipment( throw new InvalidOperationException("Failed to select a synthesized item."); } - private static int GetRandomValueForItem(Grade grade, HashSet synthesizeResultPool, SynthesizeWeightSheet synthesizeWeightSheet, + private static int GetRandomValueForItem(HashSet synthesizeResultPool, SynthesizeWeightSheet synthesizeWeightSheet, IRandom random, out List<(int ItemId, int Weight)> itemWeights) { var totalWeight = 0; itemWeights = new List<(int ItemId, int Weight)>(); foreach (var itemId in synthesizeResultPool) { - var weight = GetWeight(grade, itemId, synthesizeWeightSheet); + var weight = GetWeight(itemId, synthesizeWeightSheet); itemWeights.Add((itemId, weight)); totalWeight += weight; } @@ -631,11 +631,11 @@ public static Grade GetUpgradeGrade(Grade grade ,ItemSubType subType, EquipmentI /// item id of material item /// SynthesizeWeightSheet to use /// weight of the item that can be obtained by synthesizing the item - public static int GetWeight(Grade grade, int itemId, SynthesizeWeightSheet sheet) + public static int GetWeight(int itemId, SynthesizeWeightSheet sheet) { var defaultWeight = SynthesizeWeightSheet.DefaultWeight; - var gradeRow = sheet.Values.FirstOrDefault(r => r.GradeId == (int)grade); - return gradeRow == null ? defaultWeight : gradeRow.WeightDict.GetValueOrDefault(itemId, defaultWeight); + var gradeRow = sheet.Values.FirstOrDefault(r => r.Key == itemId); + return gradeRow?.Weight ?? defaultWeight; } // TODO: move to ItemExtensions diff --git a/Lib9c/TableCSV/Item/SynthesizeWeightSheet.csv b/Lib9c/TableCSV/Item/SynthesizeWeightSheet.csv index c808bb9e0b..afed6fd97f 100644 --- a/Lib9c/TableCSV/Item/SynthesizeWeightSheet.csv +++ b/Lib9c/TableCSV/Item/SynthesizeWeightSheet.csv @@ -1,7 +1,8 @@ -grade_id,item_id,weight +item_id,weight _default value for weight is 10000. -3,40100001,3000 -3,40100015,1000 -3,40100016,1000 -3,40100019,1000 -5,49900027,40000 \ No newline at end of file +40100001,3000 +40100015,1000 +40100016,1000 +40100017,0 +40100019,1000 +49900027,40000 \ No newline at end of file diff --git a/Lib9c/TableData/Item/SynthesizeWeightSheet.cs b/Lib9c/TableData/Item/SynthesizeWeightSheet.cs index 35b270b95a..f5e02e90aa 100644 --- a/Lib9c/TableData/Item/SynthesizeWeightSheet.cs +++ b/Lib9c/TableData/Item/SynthesizeWeightSheet.cs @@ -17,42 +17,20 @@ public class SynthesizeWeightSheet : Sheet [Serializable] public class Row : SheetRow { - public override int Key => GradeId; + public override int Key => ItemId; - public int GradeId { get; private set; } - - public Dictionary WeightDict { get; private set; } + public int ItemId { get; private set; } + public int Weight { get; private set; } public override void Set(IReadOnlyList fields) { - GradeId = ParseInt(fields[0]); - - WeightDict = new Dictionary(); - var itemId = ParseInt(fields[1]); - var weight = ParseInt(fields[2], DefaultWeight); - WeightDict.Add(itemId, weight); + ItemId = ParseInt(fields[0]); + Weight = TryParseInt(fields[1], out var weight) ? weight : DefaultWeight; } } public SynthesizeWeightSheet() : base(nameof(SynthesizeWeightSheet)) { } - - protected override void AddRow(int key, Row value) - { - if (!TryGetValue(key, out var row)) - { - Add(key, value); - - return; - } - - if (!value.WeightDict.Any()) - { - return; - } - - row.WeightDict.TryAdd(value.WeightDict.First().Key, value.WeightDict.First().Value); - } } } From d9e9a5e96339b184aaa50b282e60f56aeaebd125 Mon Sep 17 00:00:00 2001 From: s2quake Date: Thu, 5 Dec 2024 15:27:02 +0900 Subject: [PATCH 099/136] fix: Only a validator can change the commission --- .../ValidatorDelegation/SetValidatorCommission.cs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/Lib9c/Action/ValidatorDelegation/SetValidatorCommission.cs b/Lib9c/Action/ValidatorDelegation/SetValidatorCommission.cs index ebf99fbb4b..a0d4b0e278 100644 --- a/Lib9c/Action/ValidatorDelegation/SetValidatorCommission.cs +++ b/Lib9c/Action/ValidatorDelegation/SetValidatorCommission.cs @@ -3,7 +3,6 @@ using Bencodex.Types; using Libplanet.Action.State; using Libplanet.Action; -using Libplanet.Crypto; using Nekoyume.ValidatorDelegation; namespace Nekoyume.Action.ValidatorDelegation @@ -15,20 +14,16 @@ public sealed class SetValidatorCommission : ActionBase public SetValidatorCommission() { } - public SetValidatorCommission(Address validatorDelegatee, BigInteger commissionPercentage) + public SetValidatorCommission(BigInteger commissionPercentage) { - ValidatorDelegatee = validatorDelegatee; CommissionPercentage = commissionPercentage; } - public Address ValidatorDelegatee { get; private set; } - public BigInteger CommissionPercentage { get; private set; } public override IValue PlainValue => Dictionary.Empty .Add("type_id", TypeIdentifier) .Add("values", List.Empty - .Add(ValidatorDelegatee.Bencoded) .Add(CommissionPercentage)); public override void LoadPlainValue(IValue plainValue) @@ -41,17 +36,17 @@ public override void LoadPlainValue(IValue plainValue) throw new InvalidCastException(); } - ValidatorDelegatee = new Address(values[0]); - CommissionPercentage = (Integer)values[1]; + CommissionPercentage = (Integer)values[0]; } public override IWorld Execute(IActionContext context) { GasTracer.UseGas(1); + var validatorAddress = context.Signer; var world = context.PreviousState; var repository = new ValidatorRepository(world, context); - repository.SetCommissionPercentage(ValidatorDelegatee, CommissionPercentage, context.BlockIndex); + repository.SetCommissionPercentage(validatorAddress, CommissionPercentage, context.BlockIndex); return repository.World; } From 3ac6d2b45057a5f5b021f1b238bb63ea556eacc8 Mon Sep 17 00:00:00 2001 From: s2quake Date: Thu, 5 Dec 2024 15:27:25 +0900 Subject: [PATCH 100/136] test: Fix test failure for commission change action --- .../SetValidatorCommissionTest.cs | 42 ++++++++++++++----- .../ValidatorDelegationTestBase.cs | 3 +- 2 files changed, 32 insertions(+), 13 deletions(-) diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/SetValidatorCommissionTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/SetValidatorCommissionTest.cs index 6886fa952c..97eacf55f1 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/SetValidatorCommissionTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/SetValidatorCommissionTest.cs @@ -4,6 +4,7 @@ namespace Lib9c.Tests.Action.ValidatorDelegation using System.Collections.Generic; using System.Numerics; using Libplanet.Crypto; + using Nekoyume.Action; using Nekoyume.Action.ValidatorDelegation; using Nekoyume.ValidatorDelegation; using Xunit; @@ -49,14 +50,12 @@ private static readonly long CommissionPercentageChangeCooldown [Fact] public void Serialization() { - var address = new PrivateKey().Address; BigInteger commissionPercentage = 10; - var action = new SetValidatorCommission(address, commissionPercentage); + var action = new SetValidatorCommission(commissionPercentage); var plainValue = action.PlainValue; var deserialized = new SetValidatorCommission(); deserialized.LoadPlainValue(plainValue); - Assert.Equal(address, deserialized.ValidatorDelegatee); Assert.Equal(commissionPercentage, deserialized.CommissionPercentage); } @@ -73,7 +72,7 @@ public void Execute() // When var setValidatorCommission = new SetValidatorCommission( - validatorKey.Address, commissionPercentage: 11); + commissionPercentage: 11); var actionContext = new ActionContext { PreviousState = world, @@ -109,7 +108,6 @@ public void Execute_Theory(int oldCommissionPercentage, int newCommissionPercent // When var setValidatorCommission = new SetValidatorCommission( - validatorKey.Address, newCommissionPercentage); var actionContext = new ActionContext { @@ -148,7 +146,6 @@ public void Execute_Theory_WithValueGreaterThanMaximum_Throw(int commissionPerce BlockIndex = height + CommissionPercentageChangeCooldown, }; var setValidatorCommission = new SetValidatorCommission( - validatorKey.Address, commissionPercentage); // Then @@ -178,7 +175,6 @@ public void Execute_Theory_WithNegative_Throw(int commissionPercentage) BlockIndex = height + CommissionPercentageChangeCooldown, }; var setValidatorCommission = new SetValidatorCommission( - validatorKey.Address, commissionPercentage); // Then @@ -206,8 +202,7 @@ public void Execute_Theory_WithInvalidValue_Throw(int cooldown) Signer = validatorKey.Address, BlockIndex = height + cooldown, }; - var setValidatorCommission = new SetValidatorCommission( - validatorKey.Address, commissionPercentage: 14); + var setValidatorCommission = new SetValidatorCommission(commissionPercentage: 14); // Then Assert.Throws( @@ -235,8 +230,7 @@ public void Execute_Theory_WitValue(int period) Signer = validatorKey.Address, BlockIndex = height + period, }; - var setValidatorCommission = new SetValidatorCommission( - validatorKey.Address, commissionPercentage: expectedCommission); + var setValidatorCommission = new SetValidatorCommission(expectedCommission); world = setValidatorCommission.Execute(actionContext); // Then @@ -246,5 +240,31 @@ public void Execute_Theory_WitValue(int period) Assert.Equal(expectedCommission, actualPercentage); } + + [Fact] + public void Execute_NotValidator_Throw() + { + // Given + var world = World; + var validatorKey = new PrivateKey(); + var agentAddress = new PrivateKey().Address; + var validatorGold = DelegationCurrency * 10; + var height = 1L; + world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); + world = EnsurePromotedValidator(world, validatorKey, validatorGold, height); + + // When + var setValidatorCommission = new SetValidatorCommission( + commissionPercentage: 11); + var actionContext = new ActionContext + { + PreviousState = world, + Signer = agentAddress, + BlockIndex = height + CommissionPercentageChangeCooldown, + }; + + Assert.Throws( + () => setValidatorCommission.Execute(actionContext)); + } } } diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs index 7cd66f9f5d..8105c0839f 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs @@ -452,8 +452,7 @@ protected static IWorld EnsureCommissionChangedValidator( BlockIndex = blockHeight, Signer = validatorKey.Address, }; - var setValidatorCommission = new SetValidatorCommission( - validatorKey.Address, currentCommission + increment); + var setValidatorCommission = new SetValidatorCommission(currentCommission + increment); world = setValidatorCommission.Execute(actionContext); currentCommission += increment; preferredHeight = blockHeight + cooldown; From d36fb9b73ee1cb85ae1ab6f8e42bd45a2a882c53 Mon Sep 17 00:00:00 2001 From: s2quake Date: Thu, 5 Dec 2024 14:11:40 +0900 Subject: [PATCH 101/136] fix: A validator can stake whether it is claimable or not. --- Lib9c/Action/Stake.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Lib9c/Action/Stake.cs b/Lib9c/Action/Stake.cs index b87ce5a9c2..b59ead3e07 100644 --- a/Lib9c/Action/Stake.cs +++ b/Lib9c/Action/Stake.cs @@ -16,6 +16,7 @@ using Nekoyume.Model.State; using Nekoyume.Module; using Nekoyume.Module.Guild; +using Nekoyume.Module.ValidatorDelegation; using Nekoyume.TableData; using Nekoyume.TableData.Stake; using Nekoyume.TypedAddress; @@ -142,7 +143,13 @@ public override IWorld Execute(IActionContext context) // NOTE: Cannot anything if staking state is claimable. if (stakeStateV2.ClaimableBlockIndex <= context.BlockIndex) { - throw new StakeExistingClaimableException(); + var validatorRepository = new ValidatorRepository(states, context); + var isValidator = validatorRepository.TryGetValidatorDelegatee( + context.Signer, out var validatorDelegatee); + if (!isValidator) + { + throw new StakeExistingClaimableException(); + } } // NOTE: When the staking state is locked up. From 96fa9085f58e5033b0bc8b03adce3816831e176f Mon Sep 17 00:00:00 2001 From: s2quake Date: Thu, 5 Dec 2024 14:12:12 +0900 Subject: [PATCH 102/136] test: Test code for validator staking --- .Lib9c.Tests/Action/StakeTest.cs | 79 +++++++++++++++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/.Lib9c.Tests/Action/StakeTest.cs b/.Lib9c.Tests/Action/StakeTest.cs index fd4aa3c6e9..90a7133aa6 100644 --- a/.Lib9c.Tests/Action/StakeTest.cs +++ b/.Lib9c.Tests/Action/StakeTest.cs @@ -28,6 +28,7 @@ public class StakeTest { private readonly IWorld _initialState; private readonly Currency _ncg; + private readonly PublicKey _agentPublicKey = new PrivateKey().PublicKey; private readonly Address _agentAddr; private readonly StakePolicySheet _stakePolicySheet; @@ -66,7 +67,9 @@ public StakeTest(ITestOutputHelper outputHelper) _agentAddr, _, _initialState - ) = InitializeUtil.InitializeStates(sheetsOverride: sheetsOverride); + ) = InitializeUtil.InitializeStates( + sheetsOverride: sheetsOverride, + agentAddr: _agentPublicKey.Address); _ncg = _initialState.GetGoldCurrency(); _stakePolicySheet = _initialState.GetSheet(); } @@ -471,6 +474,80 @@ public void Execute_Success_When_Exist_StakeStateV3_Without_Guild( Assert.Equal(Currencies.GuildGold * amount, stakeBalance); } + [Theory] + // NOTE: non + [InlineData(50, 50)] + [InlineData(long.MaxValue, long.MaxValue)] + // NOTE: delegate + [InlineData(0, 500)] + [InlineData(50, 100)] + [InlineData(0, long.MaxValue)] + // NOTE: undelegate + [InlineData(50, 0)] + [InlineData(75, 50)] + [InlineData(long.MaxValue, 0)] + [InlineData(long.MaxValue, 500)] + public void Execute_Success_When_Exist_StakeStateV3_Validator_Without_Interval( + long previousAmount, + long amount) + { + var interval = previousAmount < amount + ? LegacyStakeState.RewardInterval : LegacyStakeState.LockupInterval; + var stakeStateAddr = StakeState.DeriveAddress(_agentAddr); + var stakeState = new StakeState( + contract: new Contract(_stakePolicySheet), + startedBlockIndex: 0L, + receivedBlockIndex: interval, + stateVersion: 3); + var world = _initialState; + var height = 0L; + + world = DelegationUtil.EnsureValidatorPromotionReady(world, _agentPublicKey, height++); + + if (previousAmount > 0) + { + var ncgToStake = _ncg * previousAmount; + var gg = FungibleAssetValue.Parse(Currencies.GuildGold, ncgToStake.GetQuantityString(true)); + world = DelegationUtil.MintGuildGold(world, _agentAddr, gg, height); + world = world.MintAsset(new ActionContext(), _agentAddr, ncgToStake); + world = world.TransferAsset( + new ActionContext(), _agentAddr, stakeStateAddr, ncgToStake); + } + + world = world.SetLegacyState(stakeStateAddr, stakeState.Serialize()); + + if (amount - previousAmount > 0) + { + var ncgToStake = _ncg * (amount - previousAmount); + world = world.MintAsset(new ActionContext(), _agentAddr, ncgToStake); + } + + var nextState = Execute( + height + 1, + world, + new TestRandom(), + _agentAddr, + amount); + + if (amount > 0) + { + Assert.True(nextState.TryGetStakeState(_agentAddr, out StakeState nextStakeState)); + Assert.Equal(3, nextStakeState.StateVersion); + } + + world = DelegationUtil.EnsureStakeReleased( + nextState, height + LegacyStakeState.LockupInterval); + + var expectedBalance = _ncg * Math.Max(0, previousAmount - amount); + var actualBalance = world.GetBalance(_agentAddr, _ncg); + var nonValidatorDelegateeBalance = world.GetBalance( + Addresses.NonValidatorDelegatee, Currencies.GuildGold); + var stakeBalance = world.GetBalance(stakeStateAddr, Currencies.GuildGold); + Assert.Equal(expectedBalance, actualBalance); + Assert.Equal(Currencies.GuildGold * 0, nonValidatorDelegateeBalance); + Assert.Equal(Currencies.GuildGold * amount, stakeBalance); + } + private IWorld Execute( long blockIndex, IWorld previousState, From 93f810e3c2bee7c47226c1b44d5d1086c7377cd9 Mon Sep 17 00:00:00 2001 From: s2quake Date: Fri, 6 Dec 2024 10:55:49 +0900 Subject: [PATCH 103/136] fix: The validator cannot claim the stake reward --- .Lib9c.Tests/Action/StakeTest.cs | 34 ++++++++++++++++---------------- Lib9c/Action/ClaimStakeReward.cs | 12 +++++++++++ 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/.Lib9c.Tests/Action/StakeTest.cs b/.Lib9c.Tests/Action/StakeTest.cs index 90a7133aa6..c2f2d499f3 100644 --- a/.Lib9c.Tests/Action/StakeTest.cs +++ b/.Lib9c.Tests/Action/StakeTest.cs @@ -475,24 +475,24 @@ public void Execute_Success_When_Exist_StakeStateV3_Without_Guild( } [Theory] - // NOTE: non - [InlineData(50, 50)] - [InlineData(long.MaxValue, long.MaxValue)] - // NOTE: delegate - [InlineData(0, 500)] - [InlineData(50, 100)] - [InlineData(0, long.MaxValue)] - // NOTE: undelegate - [InlineData(50, 0)] - [InlineData(75, 50)] - [InlineData(long.MaxValue, 0)] - [InlineData(long.MaxValue, 500)] - public void Execute_Success_When_Exist_StakeStateV3_Validator_Without_Interval( + [InlineData(0, 500, false)] + [InlineData(50, 100, false)] + [InlineData(0, long.MaxValue, false)] + [InlineData(0, 500, true)] + [InlineData(50, 100, true)] + [InlineData(0, long.MaxValue, true)] + public void Execute_Success_When_Validator_Tries_To_Increase_Amount_Without_Claim( long previousAmount, - long amount) + long amount, + bool withoutInterval) { - var interval = previousAmount < amount - ? LegacyStakeState.RewardInterval : LegacyStakeState.LockupInterval; + if (previousAmount >= amount) + { + throw new ArgumentException( + "previousAmount should be less than amount.", nameof(previousAmount)); + } + + var interval = LegacyStakeState.RewardInterval; var stakeStateAddr = StakeState.DeriveAddress(_agentAddr); var stakeState = new StakeState( contract: new Contract(_stakePolicySheet), @@ -523,7 +523,7 @@ public void Execute_Success_When_Exist_StakeStateV3_Validator_Without_Interval( } var nextState = Execute( - height + 1, + height + (withoutInterval ? 1 : interval), world, new TestRandom(), _agentAddr, diff --git a/Lib9c/Action/ClaimStakeReward.cs b/Lib9c/Action/ClaimStakeReward.cs index 98073aca4a..a027f35e2f 100644 --- a/Lib9c/Action/ClaimStakeReward.cs +++ b/Lib9c/Action/ClaimStakeReward.cs @@ -16,7 +16,9 @@ using Nekoyume.Model.Stake; using Nekoyume.Model.State; using Nekoyume.Module; +using Nekoyume.Module.ValidatorDelegation; using Nekoyume.TableData; +using Nekoyume.ValidatorDelegation; using static Lib9c.SerializeKeys; namespace Nekoyume.Action @@ -58,6 +60,16 @@ public override IWorld Execute(IActionContext context) var states = context.PreviousState; var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress); var stakeStateAddr = LegacyStakeState.DeriveAddress(context.Signer); + + var validatorRepository = new ValidatorRepository(states, context); + var isValidator = validatorRepository.TryGetValidatorDelegatee( + context.Signer, out var _); + if (isValidator) + { + throw new InvalidOperationException( + "The validator cannot claim the stake reward."); + } + if (!states.TryGetStakeState(context.Signer, out var stakeStateV2)) { throw new FailedLoadStateException( From af418fef70a6078a111cd7bf9e3d784ce7bd325b Mon Sep 17 00:00:00 2001 From: s2quake Date: Fri, 6 Dec 2024 11:05:33 +0900 Subject: [PATCH 104/136] test: Test code for a validator claims the stake reward --- .Lib9c.Tests/Action/ClaimStakeRewardTest.cs | 29 +++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/.Lib9c.Tests/Action/ClaimStakeRewardTest.cs b/.Lib9c.Tests/Action/ClaimStakeRewardTest.cs index 729bef6536..d4509a2998 100644 --- a/.Lib9c.Tests/Action/ClaimStakeRewardTest.cs +++ b/.Lib9c.Tests/Action/ClaimStakeRewardTest.cs @@ -706,6 +706,35 @@ public void Execute_V6() } } + [Fact] + public void Execute_Throw_When_Validator_Tries_To_Claim() + { + // When + var world = _initialState; + var validatorKey = new PrivateKey().PublicKey; + var validatorAddress = validatorKey.Address; + var height = 0L; + world = DelegationUtil.EnsureValidatorPromotionReady(world, validatorKey, height); + var stakeAddr = StakeState.DeriveAddress(AgentAddr); + var stakeStateV2 = PrepareStakeStateV2( + _stakePolicySheet, + 0, + LegacyStakeState.RewardInterval); + var action = new ClaimStakeReward(validatorAddress); + var actionContext = new ActionContext + { + PreviousState = world, + Signer = validatorAddress, + BlockIndex = height, + }; + + // When + var e = Assert.Throws(() => action.Execute(actionContext)); + + // Then + Assert.Equal("The validator cannot claim the stake reward.", e.Message); + } + private static StakeState PrepareStakeStateV2( StakePolicySheet stakePolicySheet, long startedBlockIndex, From 0d5299dd908376f5ebe551858175f44956aad0dc Mon Sep 17 00:00:00 2001 From: s2quake Date: Mon, 9 Dec 2024 15:54:48 +0900 Subject: [PATCH 105/136] fix: Cannot set commission with the same value --- Lib9c/ValidatorDelegation/ValidatorDelegatee.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs b/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs index 7ebe20bea4..69513711e9 100644 --- a/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs +++ b/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs @@ -162,6 +162,12 @@ FungibleAssetValue commission public void SetCommissionPercentage(BigInteger percentage, long height) { + if (CommissionPercentage == percentage) + { + throw new InvalidOperationException( + "The commission percentage is already set to the requested value."); + } + if (height - CommissionPercentageLastUpdateHeight < CommissionPercentageUpdateCooldown) { throw new InvalidOperationException( From 66b1d5792b91e0ea2427ed249ade4a35bd2a24f6 Mon Sep 17 00:00:00 2001 From: s2quake Date: Mon, 9 Dec 2024 15:55:10 +0900 Subject: [PATCH 106/136] test: Test code for SetValidatorCommission action --- .../SetValidatorCommissionTest.cs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/SetValidatorCommissionTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/SetValidatorCommissionTest.cs index 97eacf55f1..1b6ef02a35 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/SetValidatorCommissionTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/SetValidatorCommissionTest.cs @@ -266,5 +266,33 @@ public void Execute_NotValidator_Throw() Assert.Throws( () => setValidatorCommission.Execute(actionContext)); } + + [Fact] + public void Execute_With_SameValue_Throw() + { + // Given + var world = World; + var validatorKey = new PrivateKey(); + var validatorGold = DelegationCurrency * 10; + var height = 1L; + world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); + world = EnsurePromotedValidator(world, validatorKey, validatorGold, height); + + // When + var repository = new ValidatorRepository(world, new ActionContext()); + var delegatee = repository.GetValidatorDelegatee(validatorKey.Address); + var commissionPercentage = delegatee.CommissionPercentage; + var setValidatorCommission = new SetValidatorCommission( + commissionPercentage: commissionPercentage); + var actionContext = new ActionContext + { + PreviousState = world, + Signer = validatorKey.Address, + BlockIndex = height + CommissionPercentageChangeCooldown, + }; + + Assert.Throws( + () => setValidatorCommission.Execute(actionContext)); + } } } From 7ea81cec3dd5168cf526afdce87ae02d28409d5f Mon Sep 17 00:00:00 2001 From: ilgyu Date: Thu, 5 Dec 2024 01:26:12 +0900 Subject: [PATCH 107/136] feat: Introduce RewardBase --- Lib9c/Delegation/RewardBase.cs | 188 +++++++++++++++++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 Lib9c/Delegation/RewardBase.cs diff --git a/Lib9c/Delegation/RewardBase.cs b/Lib9c/Delegation/RewardBase.cs new file mode 100644 index 0000000000..85b3acd14d --- /dev/null +++ b/Lib9c/Delegation/RewardBase.cs @@ -0,0 +1,188 @@ +#nullable enable +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Numerics; +using Bencodex; +using Bencodex.Types; +using Libplanet.Crypto; +using Libplanet.Types.Assets; +using Nekoyume.Action; + +namespace Nekoyume.Delegation +{ + /// + /// RewardBase is a class that represents the base of the reward. + /// If it's multiplied by the number of shares, it will be the reward for the period. + /// Also, it holds the significant figure to calculate the reward. + /// + public class RewardBase : IBencodable, IEquatable + { + private const string StateTypeName = "reward_base"; + private const long StateVersion = 1; + private readonly IComparer _currencyComparer = new CurrencyComparer(); + + public RewardBase( + Address address, + BigInteger totalShares, + IEnumerable currencies) + : this( + address, + totalShares, + currencies.Select(c => c * 0), + RecommendedSigFig(totalShares)) + { + } + + public RewardBase(Address address, IValue bencoded) + : this(address, (List)bencoded) + { + } + + public RewardBase( + Address address, + BigInteger totalShares, + IEnumerable rewardPortion, + int sigfig) + { + Address = address; + + if (totalShares.Sign <= 0) + { + throw new ArgumentOutOfRangeException(nameof(totalShares)); + } + + TotalShares = totalShares; + + if (!rewardPortion.Select(f => f.Currency).All(new HashSet().Add)) + { + throw new ArgumentException("Duplicated currency in reward base."); + } + + RewardPortion = rewardPortion.ToImmutableDictionary(f => f.Currency, f => f); + SigFig = sigfig; + } + + + public RewardBase(Address address, List bencoded) + { + if (bencoded[0] is not Text text || text != StateTypeName || bencoded[1] is not Integer integer) + { + throw new InvalidCastException(); + } + + if (integer > StateVersion) + { + throw new FailedLoadStateException("Un-deserializable state."); + } + + Address = address; + TotalShares = (Integer)bencoded[2]; + var rewardPortion = ((List)bencoded[3]).Select(v => new FungibleAssetValue(v)); + + if (!rewardPortion.Select(f => f.Currency).All(new HashSet().Add)) + { + throw new ArgumentException("Duplicated currency in reward base."); + } + + RewardPortion = rewardPortion.ToImmutableDictionary(f => f.Currency, f => f); + SigFig = (Integer)bencoded[4]; + } + + private RewardBase( + Address address, + BigInteger totalShares, + ImmutableDictionary rewardPortion, + int sigfig) + { + Address = address; + TotalShares = totalShares; + RewardPortion = rewardPortion; + SigFig = sigfig; + } + + public Address Address { get; } + + public BigInteger TotalShares { get; } + + public int SigFig { get; private set; } + + public static int Margin => 2; + + public ImmutableDictionary RewardPortion { get; } + + public List Bencoded + => List.Empty + .Add(StateTypeName) + .Add(StateVersion) + .Add(TotalShares) + .Add(new List(RewardPortion + .OrderBy(r => r.Key, _currencyComparer) + .Select(r => r.Value.Serialize()))) + .Add(SigFig); + + IValue IBencodable.Bencoded => Bencoded; + + public RewardBase AddRewards(IEnumerable rewards) + => rewards.Aggregate(this, (accum, next) => AddReward(accum, next)); + + public RewardBase AddReward(FungibleAssetValue reward) + => AddReward(this, reward); + + public RewardBase UpdateTotalShares(BigInteger totalShares) + => UpdateTotalShares(this, totalShares); + + public static RewardBase AddReward(RewardBase rewardBase, FungibleAssetValue reward) + => new RewardBase( + rewardBase.Address, + rewardBase.TotalShares, + rewardBase.RewardPortion.TryGetValue(reward.Currency, out var portion) + ? rewardBase.RewardPortion.SetItem( + reward.Currency, + portion + (reward * rewardBase.SigFig).DivRem(rewardBase.TotalShares).Quotient) + : throw new ArgumentException($"Invalid reward currency: {reward.Currency}"), + rewardBase.SigFig); + + public static RewardBase UpdateTotalShares(RewardBase rewardBase, BigInteger totalShares) + { + var newSigFig = Math.Max(rewardBase.SigFig, RecommendedSigFig(totalShares)); + var multiplier = BigInteger.Pow(10, newSigFig - rewardBase.SigFig); + var newPortion = rewardBase.RewardPortion.ToImmutableDictionary( + kvp => kvp.Key, + kvp => (kvp.Value * multiplier).DivRem(rewardBase.TotalShares).Quotient); + + return new RewardBase( + rewardBase.Address, + totalShares, + newPortion, + newSigFig); + } + + public static int RecommendedSigFig(BigInteger totalShares) + => (int)Math.Floor(BigInteger.Log10(totalShares)) + Margin; + + public ImmutableSortedDictionary RewardsDuringPeriod(BigInteger share) + => RewardPortion.Keys.Select(k => RewardsDuringPeriod(share, k)) + .ToImmutableSortedDictionary(f => f.Currency, f => f, _currencyComparer); + + public FungibleAssetValue RewardsDuringPeriod(BigInteger share, Currency currency) + => RewardPortion.TryGetValue(currency, out var portion) + ? (portion * share).DivRem(SigFig).Quotient + : throw new ArgumentException($"Invalid reward currency: {currency}"); + + public override bool Equals(object? obj) + => obj is RewardBase other && Equals(other); + + public bool Equals(RewardBase? other) + => ReferenceEquals(this, other) + || (other is RewardBase rewardBase + && Address == rewardBase.Address + && TotalShares == rewardBase.TotalShares + && RewardPortion.Equals(rewardBase.RewardPortion) + && SigFig == rewardBase.SigFig); + + public override int GetHashCode() + => Address.GetHashCode(); + } +} From 0964d9005fe6edd57d7bf71df16d0c0470d0a78b Mon Sep 17 00:00:00 2001 From: ilgyu Date: Thu, 5 Dec 2024 14:09:32 +0900 Subject: [PATCH 108/136] feat: Implement Getter and Setter for RewardBase --- Lib9c/Delegation/Delegatee.cs | 6 +++++ Lib9c/Delegation/DelegateeMetadata.cs | 10 +++++-- Lib9c/Delegation/DelegationAddress.cs | 18 ++++++------- Lib9c/Delegation/DelegationRepository.cs | 32 +++++++++++++++++++++++ Lib9c/Delegation/IDelegatee.cs | 4 +++ Lib9c/Delegation/IDelegationRepository.cs | 4 +++ 6 files changed, 63 insertions(+), 11 deletions(-) diff --git a/Lib9c/Delegation/Delegatee.cs b/Lib9c/Delegation/Delegatee.cs index fbf1f40991..a09ab958ad 100644 --- a/Lib9c/Delegation/Delegatee.cs +++ b/Lib9c/Delegation/Delegatee.cs @@ -165,6 +165,12 @@ public Address UnbondLockInAddress(Address delegatorAddress) public Address RebondGraceAddress(Address delegatorAddress) => Metadata.RebondGraceAddress(delegatorAddress); + public Address CurrentRewardBaseAddress() + => Metadata.CurrentRewardBaseAddress(); + + public Address RewardBaseAddress(long height) + => Metadata.RewardBaseAddress(height); + public Address CurrentLumpSumRewardsRecordAddress() => Metadata.CurrentLumpSumRewardsRecordAddress(); diff --git a/Lib9c/Delegation/DelegateeMetadata.cs b/Lib9c/Delegation/DelegateeMetadata.cs index 3f9121a9cd..48cda93baf 100644 --- a/Lib9c/Delegation/DelegateeMetadata.cs +++ b/Lib9c/Delegation/DelegateeMetadata.cs @@ -340,11 +340,17 @@ public Address UnbondLockInAddress(Address delegatorAddress) public virtual Address RebondGraceAddress(Address delegatorAddress) => DelegationAddress.RebondGraceAddress(Address, delegatorAddress); + public virtual Address CurrentRewardBaseAddress() + => DelegationAddress.CurrentRewardBaseAddress(Address); + + public virtual Address RewardBaseAddress(long height) + => DelegationAddress.RewardBaseAddress(Address, height); + public virtual Address CurrentLumpSumRewardsRecordAddress() - => DelegationAddress.CurrentLumpSumRewardsRecordAddress(Address); + => DelegationAddress.CurrentRewardBaseAddress(Address); public virtual Address LumpSumRewardsRecordAddress(long height) - => DelegationAddress.LumpSumRewardsRecordAddress(Address, height); + => DelegationAddress.RewardBaseAddress(Address, height); public override bool Equals(object? obj) => obj is IDelegateeMetadata other && Equals(other); diff --git a/Lib9c/Delegation/DelegationAddress.cs b/Lib9c/Delegation/DelegationAddress.cs index 243b21e382..288c125927 100644 --- a/Lib9c/Delegation/DelegationAddress.cs +++ b/Lib9c/Delegation/DelegationAddress.cs @@ -65,29 +65,29 @@ public static Address RebondGraceAddress( delegateeMetadataAddress, delegatorAddress.ByteArray); - public static Address CurrentLumpSumRewardsRecordAddress( + public static Address CurrentRewardBaseAddress( Address delegateeAddress, Address delegateeAccountAddress) => DeriveAddress( - DelegationElementType.LumpSumRewardsRecord, + DelegationElementType.RewardBase, DelegateeMetadataAddress(delegateeAddress, delegateeAccountAddress)); - public static Address CurrentLumpSumRewardsRecordAddress( + public static Address CurrentRewardBaseAddress( Address delegateeMetadataAddress) => DeriveAddress( - DelegationElementType.LumpSumRewardsRecord, + DelegationElementType.RewardBase, delegateeMetadataAddress); - public static Address LumpSumRewardsRecordAddress( + public static Address RewardBaseAddress( Address delegateeAddress, Address delegateeAccountAddress, long height) => DeriveAddress( - DelegationElementType.LumpSumRewardsRecord, + DelegationElementType.RewardBase, DelegateeMetadataAddress(delegateeAddress, delegateeAccountAddress), BitConverter.GetBytes(height)); - public static Address LumpSumRewardsRecordAddress( + public static Address RewardBaseAddress( Address delegateeMetadataAddress, long height) => DeriveAddress( - DelegationElementType.LumpSumRewardsRecord, + DelegationElementType.RewardBase, delegateeMetadataAddress, BitConverter.GetBytes(height)); @@ -138,7 +138,7 @@ private enum DelegationElementType Bond, UnbondLockIn, RebondGrace, - LumpSumRewardsRecord, + RewardBase, RewardPool, DelegationPool, } diff --git a/Lib9c/Delegation/DelegationRepository.cs b/Lib9c/Delegation/DelegationRepository.cs index c4002c2372..f0b17a9055 100644 --- a/Lib9c/Delegation/DelegationRepository.cs +++ b/Lib9c/Delegation/DelegationRepository.cs @@ -19,6 +19,8 @@ public abstract class DelegationRepository : IDelegationRepository protected IAccount unbondLockInAccount; protected IAccount rebondGraceAccount; protected IAccount unbondingSetAccount; + protected IAccount rewardBaseAccount; + // TODO: [Migration] Remove this field after migration. protected IAccount lumpSumRewardsRecordAccount; public DelegationRepository( @@ -32,6 +34,7 @@ public DelegationRepository( Address unbondLockInAccountAddress, Address rebondGraceAccountAddress, Address unbondingSetAccountAddress, + Address rewardBaseAccountAddress, Address lumpSumRewardRecordAccountAddress) { previousWorld = world; @@ -44,6 +47,7 @@ public DelegationRepository( UnbondLockInAccountAddress = unbondLockInAccountAddress; RebondGraceAccountAddress = rebondGraceAccountAddress; UnbondingSetAccountAddress = unbondingSetAccountAddress; + RewardBaseAccountAddress = rewardBaseAccountAddress; LumpSumRewardsRecordAccountAddress = lumpSumRewardRecordAccountAddress; delegateeAccount = world.GetAccount(DelegateeAccountAddress); @@ -54,6 +58,7 @@ public DelegationRepository( unbondLockInAccount = world.GetAccount(UnbondLockInAccountAddress); rebondGraceAccount = world.GetAccount(RebondGraceAccountAddress); unbondingSetAccount = world.GetAccount(UnbondingSetAccountAddress); + rewardBaseAccount = world.GetAccount(RewardBaseAccountAddress); lumpSumRewardsRecordAccount = world.GetAccount(LumpSumRewardsRecordAccountAddress); } @@ -66,6 +71,7 @@ public DelegationRepository( .SetAccount(UnbondLockInAccountAddress, unbondLockInAccount) .SetAccount(RebondGraceAccountAddress, rebondGraceAccount) .SetAccount(UnbondingSetAccountAddress, unbondingSetAccount) + .SetAccount(RewardBaseAccountAddress, rewardBaseAccount) .SetAccount(LumpSumRewardsRecordAccountAddress, lumpSumRewardsRecordAccount); public IActionContext ActionContext { get; } @@ -86,6 +92,8 @@ public DelegationRepository( private Address UnbondingSetAccountAddress { get; } + private Address RewardBaseAccountAddress { get; } + private Address LumpSumRewardsRecordAccountAddress { get; } public abstract IDelegatee GetDelegatee(Address address); @@ -162,6 +170,24 @@ public UnbondingSet GetUnbondingSet() ? new UnbondingSet(bencoded, this) : new UnbondingSet(this); + public RewardBase GetCurrentRewardBase(IDelegatee delegatee) + { + Address address = delegatee.CurrentRewardBaseAddress(); + IValue? value = rewardBaseAccount.GetState(address); + return value is IValue bencoded + ? new RewardBase(address, bencoded) + : throw new FailedLoadStateException("RewardBase not found."); + } + + public RewardBase GetRewardBase(IDelegatee delegatee, long height) + { + Address address = delegatee.RewardBaseAddress(height); + IValue? value = rewardBaseAccount.GetState(address); + return value is IValue bencoded + ? new RewardBase(address, bencoded) + : throw new FailedLoadStateException("RewardBase not found."); + } + public LumpSumRewardsRecord? GetLumpSumRewardsRecord(IDelegatee delegatee, long height) { Address address = delegatee.LumpSumRewardsRecordAddress(height); @@ -225,6 +251,11 @@ public void SetUnbondingSet(UnbondingSet unbondingSet) : unbondingSetAccount.SetState(UnbondingSet.Address, unbondingSet.Bencoded); } + public void SetRewardBase(RewardBase rewardBase) + { + rewardBaseAccount = rewardBaseAccount.SetState(rewardBase.Address, rewardBase.Bencoded); + } + public void SetLumpSumRewardsRecord(LumpSumRewardsRecord lumpSumRewardsRecord) { lumpSumRewardsRecordAccount = lumpSumRewardsRecordAccount.SetState( @@ -245,6 +276,7 @@ public virtual void UpdateWorld(IWorld world) unbondLockInAccount = world.GetAccount(UnbondLockInAccountAddress); rebondGraceAccount = world.GetAccount(RebondGraceAccountAddress); unbondingSetAccount = world.GetAccount(UnbondingSetAccountAddress); + rewardBaseAccount = world.GetAccount(RewardBaseAccountAddress); lumpSumRewardsRecordAccount = world.GetAccount(LumpSumRewardsRecordAccountAddress); } } diff --git a/Lib9c/Delegation/IDelegatee.cs b/Lib9c/Delegation/IDelegatee.cs index 9cb3503613..54144830b1 100644 --- a/Lib9c/Delegation/IDelegatee.cs +++ b/Lib9c/Delegation/IDelegatee.cs @@ -65,6 +65,10 @@ public interface IDelegatee Address RebondGraceAddress(Address delegatorAddress); + Address CurrentRewardBaseAddress(); + + Address RewardBaseAddress(long height); + Address CurrentLumpSumRewardsRecordAddress(); Address LumpSumRewardsRecordAddress(long height); diff --git a/Lib9c/Delegation/IDelegationRepository.cs b/Lib9c/Delegation/IDelegationRepository.cs index 47c988a0b5..a1c2ba3d9b 100644 --- a/Lib9c/Delegation/IDelegationRepository.cs +++ b/Lib9c/Delegation/IDelegationRepository.cs @@ -36,6 +36,8 @@ public interface IDelegationRepository UnbondingSet GetUnbondingSet(); + RewardBase GetRewardBase(IDelegatee delegatee, long height); + LumpSumRewardsRecord? GetLumpSumRewardsRecord(IDelegatee delegatee, long height); LumpSumRewardsRecord? GetCurrentLumpSumRewardsRecord(IDelegatee delegatee); @@ -58,6 +60,8 @@ public interface IDelegationRepository void SetUnbondingSet(UnbondingSet unbondingSet); + void SetRewardBase(RewardBase rewardBase); + void SetLumpSumRewardsRecord(LumpSumRewardsRecord lumpSumRewardsRecord); void TransferAsset(Address sender, Address recipient, FungibleAssetValue value); From e1f0e4dc35dd31d576a23315f09fc1016276e232 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Mon, 9 Dec 2024 13:57:36 +0900 Subject: [PATCH 109/136] feat: Make use of RewardBase --- .Lib9c.Tests/Delegation/DummyRepository.cs | 3 +- .Lib9c.Tests/Delegation/TestRepository.cs | 3 +- Lib9c/Addresses.cs | 12 ++ Lib9c/Delegation/Delegatee.cs | 174 ++++++++++++------ Lib9c/Delegation/DelegationRepository.cs | 5 + Lib9c/Delegation/IDelegationRepository.cs | 8 +- Lib9c/Delegation/RewardBase.cs | 47 ++++- Lib9c/Model/Guild/GuildRepository.cs | 1 + .../ValidatorRepository.cs | 1 + 9 files changed, 190 insertions(+), 64 deletions(-) diff --git a/.Lib9c.Tests/Delegation/DummyRepository.cs b/.Lib9c.Tests/Delegation/DummyRepository.cs index 85332a4785..2293822e19 100644 --- a/.Lib9c.Tests/Delegation/DummyRepository.cs +++ b/.Lib9c.Tests/Delegation/DummyRepository.cs @@ -21,7 +21,8 @@ public DummyRepository(IWorld world, IActionContext context) unbondLockInAccountAddress: new Address("0000000000000000000000000000000000000005"), rebondGraceAccountAddress: new Address("0000000000000000000000000000000000000006"), unbondingSetAccountAddress: new Address("0000000000000000000000000000000000000007"), - lumpSumRewardRecordAccountAddress: new Address("0000000000000000000000000000000000000008")) + rewardBaseAccountAddress: new Address("0000000000000000000000000000000000000008"), + lumpSumRewardRecordAccountAddress: new Address("0000000000000000000000000000000000000009")) { } diff --git a/.Lib9c.Tests/Delegation/TestRepository.cs b/.Lib9c.Tests/Delegation/TestRepository.cs index b072d9ce86..ccd60898c6 100644 --- a/.Lib9c.Tests/Delegation/TestRepository.cs +++ b/.Lib9c.Tests/Delegation/TestRepository.cs @@ -24,7 +24,8 @@ public TestRepository(IWorld world, IActionContext context) unbondLockInAccountAddress: new Address("0000000000000000000000000000000000000005"), rebondGraceAccountAddress: new Address("0000000000000000000000000000000000000006"), unbondingSetAccountAddress: new Address("0000000000000000000000000000000000000007"), - lumpSumRewardRecordAccountAddress: new Address("0000000000000000000000000000000000000008")) + rewardBaseAccountAddress: new Address("0000000000000000000000000000000000000008"), + lumpSumRewardRecordAccountAddress: new Address("0000000000000000000000000000000000000009")) { _context = context; } diff --git a/Lib9c/Addresses.cs b/Lib9c/Addresses.cs index 18097cdec2..5a0ac42632 100644 --- a/Lib9c/Addresses.cs +++ b/Lib9c/Addresses.cs @@ -146,6 +146,12 @@ public static readonly Address GuildLumpSumRewardsRecord public static readonly Address GuildUnbondingSet = new Address("0000000000000000000000000000000000000216"); + /// + /// An address of an account having . + /// + public static readonly Address GuildRewardBase + = new Address("0000000000000000000000000000000000000217"); + #endregion #region Validator @@ -233,6 +239,12 @@ public static readonly Address CommunityPool public static readonly Address NonValidatorDelegatee = new Address("0000000000000000000000000000000000000313"); + /// + /// An address of an account having . + /// + public static readonly Address ValidatorRewardBase + = new Address("0000000000000000000000000000000000000314"); + #endregion #region Migration diff --git a/Lib9c/Delegation/Delegatee.cs b/Lib9c/Delegation/Delegatee.cs index a09ab958ad..c7ffc0cfd5 100644 --- a/Lib9c/Delegation/Delegatee.cs +++ b/Lib9c/Delegation/Delegatee.cs @@ -246,14 +246,23 @@ public void DistributeReward(T delegator, long height) if (!share.IsZero && bond.LastDistributeHeight.HasValue) { - IEnumerable lumpSumRewardsRecords - = GetLumpSumRewardsRecords(bond.LastDistributeHeight); - - foreach (LumpSumRewardsRecord record in lumpSumRewardsRecords) - { - TransferReward(delegator, share, record); + if (Repository.GetCurrentRewardBase(this) is RewardBase rewardBase + && Repository.GetRewardBase(this, bond.LastDistributeHeight.Value) is RewardBase lastRewardBase) + { + TransferReward(delegator, share, rewardBase, lastRewardBase); // TransferRemainders(newRecord); - Repository.SetLumpSumRewardsRecord(record); + } + else + { + IEnumerable lumpSumRewardsRecords + = GetLumpSumRewardsRecords(bond.LastDistributeHeight); + + foreach (LumpSumRewardsRecord record in lumpSumRewardsRecords) + { + TransferReward(delegator, share, record); + // TransferRemainders(newRecord); + Repository.SetLumpSumRewardsRecord(record); + } } } @@ -271,23 +280,31 @@ void IDelegatee.DistributeReward(IDelegator delegator, long height) public void CollectRewards(long height) { var rewards = RewardCurrencies.Select(c => Repository.GetBalance(RewardPoolAddress, c)); - LumpSumRewardsRecord record = Repository.GetCurrentLumpSumRewardsRecord(this) - ?? new LumpSumRewardsRecord( - CurrentLumpSumRewardsRecordAddress(), - height, - TotalShares, - RewardCurrencies); - record = record.AddLumpSumRewards(rewards); - - foreach (var rewardsEach in rewards) + if (Repository.GetCurrentRewardBase(this) is RewardBase rewardBase) + { + rewardBase = rewards.Aggregate(rewardBase, (accum, next) => accum.AddReward(next)); + Repository.SetRewardBase(rewardBase); + } + else { - if (rewardsEach.Sign > 0) + LumpSumRewardsRecord record = Repository.GetCurrentLumpSumRewardsRecord(this) + ?? new LumpSumRewardsRecord( + CurrentLumpSumRewardsRecordAddress(), + height, + TotalShares, + RewardCurrencies); + record = record.AddLumpSumRewards(rewards); + + foreach (var rewardsEach in rewards) { - Repository.TransferAsset(RewardPoolAddress, record.Address, rewardsEach); + if (rewardsEach.Sign > 0) + { + Repository.TransferAsset(RewardPoolAddress, record.Address, rewardsEach); + } } - } - Repository.SetLumpSumRewardsRecord(record); + Repository.SetLumpSumRewardsRecord(record); + } } public virtual void Slash(BigInteger slashFactor, long infractionHeight, long height) @@ -377,47 +394,31 @@ ImmutableDictionary reward private void StartNewRewardPeriod(long height) { - LumpSumRewardsRecord? currentRecord = Repository.GetCurrentLumpSumRewardsRecord(this); - long? lastStartHeight = null; - if (currentRecord is LumpSumRewardsRecord lastRecord) + MigrateLumpSumRewardsRecords(); + + if (Repository.GetCurrentRewardBase(this) is RewardBase rewardBase) { - lastStartHeight = lastRecord.StartHeight; - if (lastStartHeight == height) + rewardBase = rewardBase.UpdateTotalShares(TotalShares); + if (rewardBase.StartHeight == height) { - currentRecord = new( - currentRecord.Address, - currentRecord.StartHeight, - TotalShares, - RewardCurrencies, - currentRecord.LastStartHeight); - - Repository.SetLumpSumRewardsRecord(currentRecord); + Repository.SetRewardBase(rewardBase); return; } - Address archiveAddress = LumpSumRewardsRecordAddress(lastRecord.StartHeight); - - foreach (var rewardCurrency in RewardCurrencies) - { - FungibleAssetValue reward = Repository.GetBalance(lastRecord.Address, rewardCurrency); - if (reward.Sign > 0) - { - Repository.TransferAsset(lastRecord.Address, archiveAddress, reward); - } - } - - lastRecord = lastRecord.MoveAddress(archiveAddress); - Repository.SetLumpSumRewardsRecord(lastRecord); + Address archiveAddress = RewardBaseAddress(rewardBase.StartHeight); + var archivedRewardBase = rewardBase.MoveAddress(archiveAddress); + Repository.SetRewardBase(archivedRewardBase); + } + else + { + rewardBase = new( + CurrentRewardBaseAddress(), + height, + TotalShares, + RewardCurrencies); } - LumpSumRewardsRecord newRecord = new( - CurrentLumpSumRewardsRecordAddress(), - height, - TotalShares, - RewardCurrencies, - lastStartHeight); - - Repository.SetLumpSumRewardsRecord(newRecord); + Repository.SetRewardBase(rewardBase); } private List GetLumpSumRewardsRecords(long? lastRewardHeight) @@ -458,6 +459,31 @@ private void TransferReward(T delegator, BigInteger share, LumpSumRewardsRecord } } + private void TransferReward( + T delegator, + BigInteger share, + RewardBase currentRewardBase, + RewardBase lastRewardBase) + { + var currentCumulative = currentRewardBase.CumulativeRewardDuringPeriod(share); + var lastCumulative = lastRewardBase.CumulativeRewardDuringPeriod(share); + + foreach (var c in currentCumulative) + { + if (c.Value < lastCumulative[c.Key]) + { + throw new InvalidOperationException("Invalid reward base."); + } + + var reward = c.Value - lastCumulative[c.Key]; + + if (reward.Sign > 0) + { + Repository.TransferAsset(RewardPoolAddress, delegator.RewardAddress, reward); + } + } + } + private void TransferRemainders(LumpSumRewardsRecord record) { foreach (var rewardCurrency in RewardCurrencies) @@ -470,5 +496,45 @@ private void TransferRemainders(LumpSumRewardsRecord record) } } } + + private void MigrateLumpSumRewardsRecords() + { + List records = new(); + if (!(Repository.GetCurrentLumpSumRewardsRecord(this) is LumpSumRewardsRecord record)) + { + return; + } + + while (record.LastStartHeight is long lastStartHeight) + { + records.Add(record); + record = Repository.GetLumpSumRewardsRecord(this, lastStartHeight) + ?? throw new InvalidOperationException( + $"Lump sum rewards record for #{lastStartHeight} is missing"); + } + + records.Reverse(); + + RewardBase? rewardBase = null; + foreach (var recordEach in records) + { + if (rewardBase is null) + { + rewardBase = new RewardBase( + RewardBaseAddress(recordEach.StartHeight), + recordEach.StartHeight, + recordEach.TotalShares, + recordEach.LumpSumRewards.Keys); + } + + foreach (var r in recordEach.LumpSumRewards) + { + rewardBase = rewardBase.AddReward(r.Value); + Repository.TransferAsset(recordEach.Address, RewardPoolAddress, Repository.GetBalance(recordEach.Address, r.Key)); + } + + Repository.RemoveLumpSumRewardsRecord(recordEach); + } + } } } diff --git a/Lib9c/Delegation/DelegationRepository.cs b/Lib9c/Delegation/DelegationRepository.cs index f0b17a9055..573c65a795 100644 --- a/Lib9c/Delegation/DelegationRepository.cs +++ b/Lib9c/Delegation/DelegationRepository.cs @@ -262,6 +262,11 @@ public void SetLumpSumRewardsRecord(LumpSumRewardsRecord lumpSumRewardsRecord) lumpSumRewardsRecord.Address, lumpSumRewardsRecord.Bencoded); } + public void RemoveLumpSumRewardsRecord(LumpSumRewardsRecord lumpSumRewardsRecord) + { + lumpSumRewardsRecordAccount = lumpSumRewardsRecordAccount.RemoveState(lumpSumRewardsRecord.Address); + } + public void TransferAsset(Address sender, Address recipient, FungibleAssetValue value) => previousWorld = previousWorld.TransferAsset(ActionContext, sender, recipient, value); diff --git a/Lib9c/Delegation/IDelegationRepository.cs b/Lib9c/Delegation/IDelegationRepository.cs index a1c2ba3d9b..c3520d4c41 100644 --- a/Lib9c/Delegation/IDelegationRepository.cs +++ b/Lib9c/Delegation/IDelegationRepository.cs @@ -36,12 +36,14 @@ public interface IDelegationRepository UnbondingSet GetUnbondingSet(); - RewardBase GetRewardBase(IDelegatee delegatee, long height); + RewardBase? GetCurrentRewardBase(IDelegatee delegatee); - LumpSumRewardsRecord? GetLumpSumRewardsRecord(IDelegatee delegatee, long height); + RewardBase? GetRewardBase(IDelegatee delegatee, long height); LumpSumRewardsRecord? GetCurrentLumpSumRewardsRecord(IDelegatee delegatee); + LumpSumRewardsRecord? GetLumpSumRewardsRecord(IDelegatee delegatee, long height); + FungibleAssetValue GetBalance(Address address, Currency currency); void SetDelegatee(IDelegatee delegatee); @@ -64,6 +66,8 @@ public interface IDelegationRepository void SetLumpSumRewardsRecord(LumpSumRewardsRecord lumpSumRewardsRecord); + void RemoveLumpSumRewardsRecord(LumpSumRewardsRecord lumpSumRewardsRecord); + void TransferAsset(Address sender, Address recipient, FungibleAssetValue value); void UpdateWorld(IWorld world); diff --git a/Lib9c/Delegation/RewardBase.cs b/Lib9c/Delegation/RewardBase.cs index 85b3acd14d..eb3c6f85c2 100644 --- a/Lib9c/Delegation/RewardBase.cs +++ b/Lib9c/Delegation/RewardBase.cs @@ -25,16 +25,33 @@ public class RewardBase : IBencodable, IEquatable public RewardBase( Address address, + long startHeight, BigInteger totalShares, IEnumerable currencies) : this( address, + startHeight, totalShares, currencies.Select(c => c * 0), RecommendedSigFig(totalShares)) { } + public RewardBase( + Address address, + long startHeight, + BigInteger totalShares, + IEnumerable currencies, + int sigFig) + : this( + address, + startHeight, + totalShares, + currencies.Select(c => c * 0), + sigFig) + { + } + public RewardBase(Address address, IValue bencoded) : this(address, (List)bencoded) { @@ -42,11 +59,13 @@ public RewardBase(Address address, IValue bencoded) public RewardBase( Address address, + long startHeight, BigInteger totalShares, IEnumerable rewardPortion, int sigfig) { Address = address; + StartHeight = startHeight; if (totalShares.Sign <= 0) { @@ -78,8 +97,9 @@ public RewardBase(Address address, List bencoded) } Address = address; - TotalShares = (Integer)bencoded[2]; - var rewardPortion = ((List)bencoded[3]).Select(v => new FungibleAssetValue(v)); + StartHeight = (Integer)bencoded[2]; + TotalShares = (Integer)bencoded[3]; + var rewardPortion = ((List)bencoded[4]).Select(v => new FungibleAssetValue(v)); if (!rewardPortion.Select(f => f.Currency).All(new HashSet().Add)) { @@ -87,16 +107,18 @@ public RewardBase(Address address, List bencoded) } RewardPortion = rewardPortion.ToImmutableDictionary(f => f.Currency, f => f); - SigFig = (Integer)bencoded[4]; + SigFig = (Integer)bencoded[5]; } private RewardBase( Address address, + long startHeight, BigInteger totalShares, ImmutableDictionary rewardPortion, int sigfig) { Address = address; + StartHeight = startHeight; TotalShares = totalShares; RewardPortion = rewardPortion; SigFig = sigfig; @@ -104,6 +126,8 @@ private RewardBase( public Address Address { get; } + public long StartHeight { get; } + public BigInteger TotalShares { get; } public int SigFig { get; private set; } @@ -116,6 +140,7 @@ public List Bencoded => List.Empty .Add(StateTypeName) .Add(StateVersion) + .Add(StartHeight) .Add(TotalShares) .Add(new List(RewardPortion .OrderBy(r => r.Key, _currencyComparer) @@ -133,9 +158,18 @@ public RewardBase AddReward(FungibleAssetValue reward) public RewardBase UpdateTotalShares(BigInteger totalShares) => UpdateTotalShares(this, totalShares); + public RewardBase MoveAddress(Address address) + => new RewardBase( + address, + StartHeight, + TotalShares, + RewardPortion, + SigFig); + public static RewardBase AddReward(RewardBase rewardBase, FungibleAssetValue reward) => new RewardBase( rewardBase.Address, + rewardBase.StartHeight, rewardBase.TotalShares, rewardBase.RewardPortion.TryGetValue(reward.Currency, out var portion) ? rewardBase.RewardPortion.SetItem( @@ -154,6 +188,7 @@ public static RewardBase UpdateTotalShares(RewardBase rewardBase, BigInteger tot return new RewardBase( rewardBase.Address, + rewardBase.StartHeight, totalShares, newPortion, newSigFig); @@ -162,11 +197,11 @@ public static RewardBase UpdateTotalShares(RewardBase rewardBase, BigInteger tot public static int RecommendedSigFig(BigInteger totalShares) => (int)Math.Floor(BigInteger.Log10(totalShares)) + Margin; - public ImmutableSortedDictionary RewardsDuringPeriod(BigInteger share) - => RewardPortion.Keys.Select(k => RewardsDuringPeriod(share, k)) + public ImmutableSortedDictionary CumulativeRewardDuringPeriod(BigInteger share) + => RewardPortion.Keys.Select(k => CumulativeRewardDuringPeriod(share, k)) .ToImmutableSortedDictionary(f => f.Currency, f => f, _currencyComparer); - public FungibleAssetValue RewardsDuringPeriod(BigInteger share, Currency currency) + public FungibleAssetValue CumulativeRewardDuringPeriod(BigInteger share, Currency currency) => RewardPortion.TryGetValue(currency, out var portion) ? (portion * share).DivRem(SigFig).Quotient : throw new ArgumentException($"Invalid reward currency: {currency}"); diff --git a/Lib9c/Model/Guild/GuildRepository.cs b/Lib9c/Model/Guild/GuildRepository.cs index f1394e99e5..54bd7d223b 100644 --- a/Lib9c/Model/Guild/GuildRepository.cs +++ b/Lib9c/Model/Guild/GuildRepository.cs @@ -34,6 +34,7 @@ public GuildRepository(IWorld world, IActionContext actionContext) unbondLockInAccountAddress: Addresses.GuildUnbondLockIn, rebondGraceAccountAddress: Addresses.GuildRebondGrace, unbondingSetAccountAddress: Addresses.GuildUnbondingSet, + rewardBaseAccountAddress: Addresses.GuildRewardBase, lumpSumRewardRecordAccountAddress: Addresses.GuildLumpSumRewardsRecord) { _guildAccount = world.GetAccount(guildAddress); diff --git a/Lib9c/ValidatorDelegation/ValidatorRepository.cs b/Lib9c/ValidatorDelegation/ValidatorRepository.cs index 69e7563c38..7e4a6d5760 100644 --- a/Lib9c/ValidatorDelegation/ValidatorRepository.cs +++ b/Lib9c/ValidatorDelegation/ValidatorRepository.cs @@ -32,6 +32,7 @@ public ValidatorRepository(IWorld world, IActionContext actionContext) Addresses.ValidatorUnbondLockIn, Addresses.ValidatorRebondGrace, Addresses.ValidatorUnbondingSet, + Addresses.ValidatorRewardBase, Addresses.ValidatorLumpSumRewardsRecord) { _validatorListAccount = world.GetAccount(validatorListAddress); From 8b191253731f52a52aa75e3f695ac899346ddf9e Mon Sep 17 00:00:00 2001 From: ilgyu Date: Mon, 9 Dec 2024 14:48:00 +0900 Subject: [PATCH 110/136] fix: Fix to return null when RewardBase not exists --- Lib9c/Delegation/DelegationRepository.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib9c/Delegation/DelegationRepository.cs b/Lib9c/Delegation/DelegationRepository.cs index 573c65a795..8275326cf9 100644 --- a/Lib9c/Delegation/DelegationRepository.cs +++ b/Lib9c/Delegation/DelegationRepository.cs @@ -170,22 +170,22 @@ public UnbondingSet GetUnbondingSet() ? new UnbondingSet(bencoded, this) : new UnbondingSet(this); - public RewardBase GetCurrentRewardBase(IDelegatee delegatee) + public RewardBase? GetCurrentRewardBase(IDelegatee delegatee) { Address address = delegatee.CurrentRewardBaseAddress(); IValue? value = rewardBaseAccount.GetState(address); return value is IValue bencoded ? new RewardBase(address, bencoded) - : throw new FailedLoadStateException("RewardBase not found."); + : null; } - public RewardBase GetRewardBase(IDelegatee delegatee, long height) + public RewardBase? GetRewardBase(IDelegatee delegatee, long height) { Address address = delegatee.RewardBaseAddress(height); IValue? value = rewardBaseAccount.GetState(address); return value is IValue bencoded ? new RewardBase(address, bencoded) - : throw new FailedLoadStateException("RewardBase not found."); + : null; } public LumpSumRewardsRecord? GetLumpSumRewardsRecord(IDelegatee delegatee, long height) From 00cc54043322ae869f001bef0cee56af0647972b Mon Sep 17 00:00:00 2001 From: ilgyu Date: Tue, 10 Dec 2024 14:56:49 +0900 Subject: [PATCH 111/136] fix: Fix malfunctions for RewardBase --- Lib9c/Delegation/Delegatee.cs | 34 +++++++++++++++++++++------------- Lib9c/Delegation/Delegator.cs | 1 + Lib9c/Delegation/RewardBase.cs | 21 ++++++++++++--------- 3 files changed, 34 insertions(+), 22 deletions(-) diff --git a/Lib9c/Delegation/Delegatee.cs b/Lib9c/Delegation/Delegatee.cs index c7ffc0cfd5..80e4be54db 100644 --- a/Lib9c/Delegation/Delegatee.cs +++ b/Lib9c/Delegation/Delegatee.cs @@ -246,9 +246,9 @@ public void DistributeReward(T delegator, long height) if (!share.IsZero && bond.LastDistributeHeight.HasValue) { - if (Repository.GetCurrentRewardBase(this) is RewardBase rewardBase - && Repository.GetRewardBase(this, bond.LastDistributeHeight.Value) is RewardBase lastRewardBase) - { + if (Repository.GetCurrentRewardBase(this) is RewardBase rewardBase) + { + var lastRewardBase = Repository.GetRewardBase(this, bond.LastDistributeHeight.Value); TransferReward(delegator, share, rewardBase, lastRewardBase); // TransferRemainders(newRecord); } @@ -392,16 +392,17 @@ ImmutableDictionary reward return reward; } - private void StartNewRewardPeriod(long height) + public void StartNewRewardPeriod(long height) { MigrateLumpSumRewardsRecords(); + RewardBase newRewardBase; if (Repository.GetCurrentRewardBase(this) is RewardBase rewardBase) { - rewardBase = rewardBase.UpdateTotalShares(TotalShares); + newRewardBase = rewardBase.UpdateTotalShares(TotalShares, height); if (rewardBase.StartHeight == height) { - Repository.SetRewardBase(rewardBase); + Repository.SetRewardBase(newRewardBase); return; } @@ -410,15 +411,20 @@ private void StartNewRewardPeriod(long height) Repository.SetRewardBase(archivedRewardBase); } else - { - rewardBase = new( + { + if (TotalShares.IsZero) + { + return; + } + + newRewardBase = new( CurrentRewardBaseAddress(), height, TotalShares, RewardCurrencies); } - Repository.SetRewardBase(rewardBase); + Repository.SetRewardBase(newRewardBase); } private List GetLumpSumRewardsRecords(long? lastRewardHeight) @@ -463,19 +469,21 @@ private void TransferReward( T delegator, BigInteger share, RewardBase currentRewardBase, - RewardBase lastRewardBase) + RewardBase? lastRewardBase) { var currentCumulative = currentRewardBase.CumulativeRewardDuringPeriod(share); - var lastCumulative = lastRewardBase.CumulativeRewardDuringPeriod(share); + var lastCumulative = lastRewardBase?.CumulativeRewardDuringPeriod(share); foreach (var c in currentCumulative) { - if (c.Value < lastCumulative[c.Key]) + var lastCumulativeEach = lastCumulative?[c.Key] ?? c.Key * 0; + + if (c.Value < lastCumulativeEach) { throw new InvalidOperationException("Invalid reward base."); } - var reward = c.Value - lastCumulative[c.Key]; + var reward = c.Value - lastCumulativeEach; if (reward.Sign > 0) { diff --git a/Lib9c/Delegation/Delegator.cs b/Lib9c/Delegation/Delegator.cs index 044d6a7f91..43159d17ea 100644 --- a/Lib9c/Delegation/Delegator.cs +++ b/Lib9c/Delegation/Delegator.cs @@ -221,6 +221,7 @@ public void ClaimReward( T delegatee, long height) { delegatee.DistributeReward(this, height); + delegatee.StartNewRewardPeriod(height); Repository.SetDelegator(this); } diff --git a/Lib9c/Delegation/RewardBase.cs b/Lib9c/Delegation/RewardBase.cs index eb3c6f85c2..15d7fc5311 100644 --- a/Lib9c/Delegation/RewardBase.cs +++ b/Lib9c/Delegation/RewardBase.cs @@ -155,8 +155,8 @@ public RewardBase AddRewards(IEnumerable rewards) public RewardBase AddReward(FungibleAssetValue reward) => AddReward(this, reward); - public RewardBase UpdateTotalShares(BigInteger totalShares) - => UpdateTotalShares(this, totalShares); + public RewardBase UpdateTotalShares(BigInteger totalShares, long startHeight) + => UpdateTotalShares(this, totalShares, startHeight); public RewardBase MoveAddress(Address address) => new RewardBase( @@ -166,7 +166,7 @@ public RewardBase MoveAddress(Address address) RewardPortion, SigFig); - public static RewardBase AddReward(RewardBase rewardBase, FungibleAssetValue reward) + private static RewardBase AddReward(RewardBase rewardBase, FungibleAssetValue reward) => new RewardBase( rewardBase.Address, rewardBase.StartHeight, @@ -174,21 +174,21 @@ public static RewardBase AddReward(RewardBase rewardBase, FungibleAssetValue rew rewardBase.RewardPortion.TryGetValue(reward.Currency, out var portion) ? rewardBase.RewardPortion.SetItem( reward.Currency, - portion + (reward * rewardBase.SigFig).DivRem(rewardBase.TotalShares).Quotient) + portion + (reward * Multiplier(rewardBase.SigFig)).DivRem(rewardBase.TotalShares).Quotient) : throw new ArgumentException($"Invalid reward currency: {reward.Currency}"), rewardBase.SigFig); - public static RewardBase UpdateTotalShares(RewardBase rewardBase, BigInteger totalShares) + private static RewardBase UpdateTotalShares(RewardBase rewardBase, BigInteger totalShares, long startHeight) { var newSigFig = Math.Max(rewardBase.SigFig, RecommendedSigFig(totalShares)); - var multiplier = BigInteger.Pow(10, newSigFig - rewardBase.SigFig); + var multiplier = Multiplier(newSigFig - rewardBase.SigFig); var newPortion = rewardBase.RewardPortion.ToImmutableDictionary( kvp => kvp.Key, - kvp => (kvp.Value * multiplier).DivRem(rewardBase.TotalShares).Quotient); + kvp => kvp.Value * multiplier); return new RewardBase( rewardBase.Address, - rewardBase.StartHeight, + startHeight, totalShares, newPortion, newSigFig); @@ -197,13 +197,16 @@ public static RewardBase UpdateTotalShares(RewardBase rewardBase, BigInteger tot public static int RecommendedSigFig(BigInteger totalShares) => (int)Math.Floor(BigInteger.Log10(totalShares)) + Margin; + public static BigInteger Multiplier(int sigFig) + => BigInteger.Pow(10, sigFig); + public ImmutableSortedDictionary CumulativeRewardDuringPeriod(BigInteger share) => RewardPortion.Keys.Select(k => CumulativeRewardDuringPeriod(share, k)) .ToImmutableSortedDictionary(f => f.Currency, f => f, _currencyComparer); public FungibleAssetValue CumulativeRewardDuringPeriod(BigInteger share, Currency currency) => RewardPortion.TryGetValue(currency, out var portion) - ? (portion * share).DivRem(SigFig).Quotient + ? (portion * share).DivRem(Multiplier(SigFig)).Quotient : throw new ArgumentException($"Invalid reward currency: {currency}"); public override bool Equals(object? obj) From a176e9166872a5e588e627711d8e0a83b372d956 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Tue, 10 Dec 2024 18:20:26 +0900 Subject: [PATCH 112/136] fix: Malfunctions for reward --- Lib9c/Delegation/Delegatee.cs | 27 +++++--- Lib9c/Delegation/DelegateeMetadata.cs | 11 +-- Lib9c/Delegation/DelegationAddress.cs | 13 ++++ Lib9c/Delegation/RewardBase.cs | 99 ++++++++++++++++----------- 4 files changed, 97 insertions(+), 53 deletions(-) diff --git a/Lib9c/Delegation/Delegatee.cs b/Lib9c/Delegation/Delegatee.cs index 80e4be54db..419ecaa972 100644 --- a/Lib9c/Delegation/Delegatee.cs +++ b/Lib9c/Delegation/Delegatee.cs @@ -165,6 +165,9 @@ public Address UnbondLockInAddress(Address delegatorAddress) public Address RebondGraceAddress(Address delegatorAddress) => Metadata.RebondGraceAddress(delegatorAddress); + public Address DistributionPoolAddress() + => Metadata.DistributionPoolAddress(); + public Address CurrentRewardBaseAddress() => Metadata.CurrentRewardBaseAddress(); @@ -283,6 +286,15 @@ public void CollectRewards(long height) if (Repository.GetCurrentRewardBase(this) is RewardBase rewardBase) { rewardBase = rewards.Aggregate(rewardBase, (accum, next) => accum.AddReward(next)); + + foreach (var rewardsEach in rewards) + { + if (rewardsEach.Sign > 0) + { + Repository.TransferAsset(RewardPoolAddress, DistributionPoolAddress(), rewardsEach); + } + } + Repository.SetRewardBase(rewardBase); } else @@ -399,15 +411,15 @@ public void StartNewRewardPeriod(long height) RewardBase newRewardBase; if (Repository.GetCurrentRewardBase(this) is RewardBase rewardBase) { - newRewardBase = rewardBase.UpdateTotalShares(TotalShares, height); - if (rewardBase.StartHeight == height) + newRewardBase = rewardBase.UpdateTotalShares(TotalShares); + if (Repository.GetRewardBase(this, height) is not null) { Repository.SetRewardBase(newRewardBase); return; } - Address archiveAddress = RewardBaseAddress(rewardBase.StartHeight); - var archivedRewardBase = rewardBase.MoveAddress(archiveAddress); + Address archiveAddress = RewardBaseAddress(height); + var archivedRewardBase = rewardBase.AttachHeight(archiveAddress, height); Repository.SetRewardBase(archivedRewardBase); } else @@ -419,7 +431,6 @@ public void StartNewRewardPeriod(long height) newRewardBase = new( CurrentRewardBaseAddress(), - height, TotalShares, RewardCurrencies); } @@ -487,7 +498,7 @@ private void TransferReward( if (reward.Sign > 0) { - Repository.TransferAsset(RewardPoolAddress, delegator.RewardAddress, reward); + Repository.TransferAsset(DistributionPoolAddress(), delegator.RewardAddress, reward); } } } @@ -530,9 +541,9 @@ record = Repository.GetLumpSumRewardsRecord(this, lastStartHeight) { rewardBase = new RewardBase( RewardBaseAddress(recordEach.StartHeight), - recordEach.StartHeight, recordEach.TotalShares, - recordEach.LumpSumRewards.Keys); + recordEach.LumpSumRewards.Keys, + recordEach.StartHeight); } foreach (var r in recordEach.LumpSumRewards) diff --git a/Lib9c/Delegation/DelegateeMetadata.cs b/Lib9c/Delegation/DelegateeMetadata.cs index 48cda93baf..a322ea03d8 100644 --- a/Lib9c/Delegation/DelegateeMetadata.cs +++ b/Lib9c/Delegation/DelegateeMetadata.cs @@ -62,8 +62,8 @@ public DelegateeMetadata( } public DelegateeMetadata( - Address address, - Address accountAddress, + Address delegateeAddress, + Address delegateeAccountAddress, List bencoded) { Currency delegationCurrency; @@ -152,8 +152,8 @@ public DelegateeMetadata( "Total shares must be non-negative."); } - DelegateeAddress = address; - DelegateeAccountAddress = accountAddress; + DelegateeAddress = delegateeAddress; + DelegateeAccountAddress = delegateeAccountAddress; DelegationCurrency = delegationCurrency; RewardCurrencies = rewardCurrencies.ToImmutableSortedSet(_currencyComparer); DelegationPoolAddress = delegationPoolAddress; @@ -340,6 +340,9 @@ public Address UnbondLockInAddress(Address delegatorAddress) public virtual Address RebondGraceAddress(Address delegatorAddress) => DelegationAddress.RebondGraceAddress(Address, delegatorAddress); + public virtual Address DistributionPoolAddress() + => DelegationAddress.DistributionPoolAddress(Address); + public virtual Address CurrentRewardBaseAddress() => DelegationAddress.CurrentRewardBaseAddress(Address); diff --git a/Lib9c/Delegation/DelegationAddress.cs b/Lib9c/Delegation/DelegationAddress.cs index 288c125927..1e7a340b92 100644 --- a/Lib9c/Delegation/DelegationAddress.cs +++ b/Lib9c/Delegation/DelegationAddress.cs @@ -103,6 +103,18 @@ public static Address RewardPoolAddress( DelegationElementType.RewardPool, delegateeMetadataAddress); + public static Address DistributionPoolAddress( + Address delegateeAddress, Address delegateeAccountAddress) + => DeriveAddress( + DelegationElementType.DistributionPool, + DelegateeMetadataAddress(delegateeAddress, delegateeAccountAddress)); + + public static Address DistributionPoolAddress( + Address delegateeMetadataAddress) + => DeriveAddress( + DelegationElementType.DistributionPool, + delegateeMetadataAddress); + public static Address DelegationPoolAddress( Address delegateeAddress, Address delegateeAccountAddress) => DeriveAddress( @@ -141,6 +153,7 @@ private enum DelegationElementType RewardBase, RewardPool, DelegationPool, + DistributionPool, } } } diff --git a/Lib9c/Delegation/RewardBase.cs b/Lib9c/Delegation/RewardBase.cs index 15d7fc5311..0eb7bf54c4 100644 --- a/Lib9c/Delegation/RewardBase.cs +++ b/Lib9c/Delegation/RewardBase.cs @@ -25,30 +25,30 @@ public class RewardBase : IBencodable, IEquatable public RewardBase( Address address, - long startHeight, BigInteger totalShares, - IEnumerable currencies) + IEnumerable currencies, + long? startHeight = null) : this( address, - startHeight, totalShares, currencies.Select(c => c * 0), - RecommendedSigFig(totalShares)) + RecommendedSigFig(totalShares), + startHeight) { } public RewardBase( Address address, - long startHeight, BigInteger totalShares, IEnumerable currencies, - int sigFig) + int sigFig, + long? startHeight = null) : this( address, - startHeight, totalShares, currencies.Select(c => c * 0), - sigFig) + sigFig, + startHeight) { } @@ -59,13 +59,12 @@ public RewardBase(Address address, IValue bencoded) public RewardBase( Address address, - long startHeight, BigInteger totalShares, IEnumerable rewardPortion, - int sigfig) + int sigfig, + long? startHeight = null) { Address = address; - StartHeight = startHeight; if (totalShares.Sign <= 0) { @@ -81,6 +80,7 @@ public RewardBase( RewardPortion = rewardPortion.ToImmutableDictionary(f => f.Currency, f => f); SigFig = sigfig; + StartHeight = startHeight; } @@ -97,9 +97,8 @@ public RewardBase(Address address, List bencoded) } Address = address; - StartHeight = (Integer)bencoded[2]; - TotalShares = (Integer)bencoded[3]; - var rewardPortion = ((List)bencoded[4]).Select(v => new FungibleAssetValue(v)); + TotalShares = (Integer)bencoded[2]; + var rewardPortion = ((List)bencoded[3]).Select(v => new FungibleAssetValue(v)); if (!rewardPortion.Select(f => f.Currency).All(new HashSet().Add)) { @@ -107,26 +106,35 @@ public RewardBase(Address address, List bencoded) } RewardPortion = rewardPortion.ToImmutableDictionary(f => f.Currency, f => f); - SigFig = (Integer)bencoded[5]; + SigFig = (Integer)bencoded[4]; + + try + { + StartHeight = (Integer)bencoded[5]; + } + catch (IndexOutOfRangeException) + { + StartHeight = null; + } } private RewardBase( Address address, - long startHeight, BigInteger totalShares, ImmutableDictionary rewardPortion, - int sigfig) + int sigfig, + long? startHeight = null) { Address = address; - StartHeight = startHeight; TotalShares = totalShares; RewardPortion = rewardPortion; SigFig = sigfig; + StartHeight = startHeight; } public Address Address { get; } - public long StartHeight { get; } + public long? StartHeight { get; } public BigInteger TotalShares { get; } @@ -137,15 +145,23 @@ private RewardBase( public ImmutableDictionary RewardPortion { get; } public List Bencoded - => List.Empty - .Add(StateTypeName) - .Add(StateVersion) - .Add(StartHeight) - .Add(TotalShares) - .Add(new List(RewardPortion - .OrderBy(r => r.Key, _currencyComparer) - .Select(r => r.Value.Serialize()))) - .Add(SigFig); + { + get + { + var bencoded = List.Empty + .Add(StateTypeName) + .Add(StateVersion) + .Add(TotalShares) + .Add(new List(RewardPortion + .OrderBy(r => r.Key, _currencyComparer) + .Select(r => r.Value.Serialize()))) + .Add(SigFig); + + return StartHeight is long height + ? bencoded.Add(height) + : bencoded; + } + } IValue IBencodable.Bencoded => Bencoded; @@ -155,30 +171,32 @@ public RewardBase AddRewards(IEnumerable rewards) public RewardBase AddReward(FungibleAssetValue reward) => AddReward(this, reward); - public RewardBase UpdateTotalShares(BigInteger totalShares, long startHeight) - => UpdateTotalShares(this, totalShares, startHeight); + public RewardBase UpdateTotalShares(BigInteger totalShares) + => UpdateTotalShares(this, totalShares); - public RewardBase MoveAddress(Address address) - => new RewardBase( - address, - StartHeight, - TotalShares, - RewardPortion, - SigFig); + public RewardBase AttachHeight(Address address, long startHeight) + => StartHeight is null + ? new RewardBase( + address, + TotalShares, + RewardPortion, + SigFig, + startHeight) + : throw new InvalidOperationException("StartHeight is already attached."); private static RewardBase AddReward(RewardBase rewardBase, FungibleAssetValue reward) => new RewardBase( rewardBase.Address, - rewardBase.StartHeight, rewardBase.TotalShares, rewardBase.RewardPortion.TryGetValue(reward.Currency, out var portion) ? rewardBase.RewardPortion.SetItem( reward.Currency, portion + (reward * Multiplier(rewardBase.SigFig)).DivRem(rewardBase.TotalShares).Quotient) : throw new ArgumentException($"Invalid reward currency: {reward.Currency}"), - rewardBase.SigFig); + rewardBase.SigFig, + rewardBase.StartHeight); - private static RewardBase UpdateTotalShares(RewardBase rewardBase, BigInteger totalShares, long startHeight) + private static RewardBase UpdateTotalShares(RewardBase rewardBase, BigInteger totalShares) { var newSigFig = Math.Max(rewardBase.SigFig, RecommendedSigFig(totalShares)); var multiplier = Multiplier(newSigFig - rewardBase.SigFig); @@ -188,7 +206,6 @@ private static RewardBase UpdateTotalShares(RewardBase rewardBase, BigInteger to return new RewardBase( rewardBase.Address, - startHeight, totalShares, newPortion, newSigFig); From 31317221d6f8c608a7922091e7720331fbf19133 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Tue, 10 Dec 2024 18:42:12 +0900 Subject: [PATCH 113/136] test: Fix tests for rewards --- .../AllocateGuildRewardTest.cs | 2 +- .Lib9c.Tests/Delegation/DelegatorTest.cs | 65 +++++++++++++------ 2 files changed, 46 insertions(+), 21 deletions(-) diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/AllocateGuildRewardTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/AllocateGuildRewardTest.cs index 789736d50f..9451670e5b 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/AllocateGuildRewardTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/AllocateGuildRewardTest.cs @@ -231,7 +231,7 @@ var expectedProposerReward var validatorAddress = vote.ValidatorPublicKey.Address; var actualDelegatee = actualRepository.GetValidatorDelegatee(validatorAddress); - var validatorRewardAddress = actualDelegatee.CurrentLumpSumRewardsRecordAddress(); + var validatorRewardAddress = actualDelegatee.DistributionPoolAddress(); var actualDelegationBalance = world.GetBalance(validatorAddress, DelegationCurrency); var actualCommission = world.GetBalance(validatorAddress, GuildAllocateRewardCurrency); var actualUnclaimedReward = world.GetBalance(validatorRewardAddress, GuildAllocateRewardCurrency); diff --git a/.Lib9c.Tests/Delegation/DelegatorTest.cs b/.Lib9c.Tests/Delegation/DelegatorTest.cs index 8533601a90..65bc9df9df 100644 --- a/.Lib9c.Tests/Delegation/DelegatorTest.cs +++ b/.Lib9c.Tests/Delegation/DelegatorTest.cs @@ -328,13 +328,16 @@ public void RewardOnDelegate() delegator2Balance = repo.World.GetBalance(delegator2.Address, delegatee.DelegationCurrency); var delegator1RewardBalances = delegatee.RewardCurrencies.Select( c => repo.World.GetBalance(delegator1.Address, c)); - var collectedRewards = DelegationFixture.TotalRewardsOfRecords(delegatee, repo); + var collectedRewards = delegatee.RewardCurrencies.Select( + c => repo.World.GetBalance(delegatee.DistributionPoolAddress(), c)); + var legacyRewards = DelegationFixture.TotalRewardsOfRecords(delegatee, repo); var rewards1 = rewards.Select(r => (r * share1).DivRem(totalShares, out _)); Assert.Equal(delegatorInitialBalance - delegatingFAV1 * 2, delegator1Balance); Assert.Equal(delegatorInitialBalance - delegatingFAV2, delegator2Balance); Assert.Equal(rewards1, delegator1RewardBalances); - Assert.Equal(rewards.Zip(rewards1, (f, s) => f * 2 - s), collectedRewards); + Assert.Equal(rewards.Zip(rewards1, (f, s) => f - s), collectedRewards); + Assert.Equal(rewards, legacyRewards); delegator2.Delegate(delegatee, delegatingFAV2, 11L); delegator2Balance = repo.World.GetBalance(delegator2.Address, delegatee.DelegationCurrency); @@ -342,7 +345,9 @@ public void RewardOnDelegate() c => repo.World.GetBalance(delegator1.Address, c)); var delegator2RewardBalances = delegatee.RewardCurrencies.Select( c => repo.World.GetBalance(delegator2.Address, c)); - collectedRewards = DelegationFixture.TotalRewardsOfRecords(delegatee, repo); + collectedRewards = delegatee.RewardCurrencies.Select( + c => repo.World.GetBalance(delegatee.DistributionPoolAddress(), c)); + legacyRewards = DelegationFixture.TotalRewardsOfRecords(delegatee, repo); var rewards2 = rewards.Select(r => (r * share2).DivRem(totalShares, out _)); Assert.Equal(delegatorInitialBalance - delegatingFAV1 * 2, delegator1Balance); @@ -353,8 +358,9 @@ public void RewardOnDelegate() // Flushing to remainder pool is now inactive. // Assert.Equal(delegatee.RewardCurrencies.Select(c => c * 0), collectedRewards); Assert.Equal( - rewards.Select(r => r * 2).Zip(rewards1.Zip(rewards2, (f, s) => f + s), (f, s) => f - s).ToArray(), + rewards.Zip(rewards1.Zip(rewards2, (f, s) => f + s), (f, s) => f - s).ToArray(), collectedRewards); + Assert.Equal(rewards, legacyRewards); } [Fact] @@ -374,7 +380,7 @@ public void RewardOnUndelegate() repo.MintAsset(delegatee.RewardPoolAddress, reward); } - // EndBlock after delegatee's reward + // BeginBlock after delegatee's reward delegatee.CollectRewards(10L); var delegatingFAV1 = delegatee.DelegationCurrency * 10; @@ -398,21 +404,24 @@ public void RewardOnUndelegate() repo.MintAsset(delegatee.RewardPoolAddress, reward); } - // EndBlock after delegatee's reward - delegatee.CollectRewards(10L); + // BeginBlock after delegatee's reward + delegatee.CollectRewards(11L); var shareToUndelegate = repo.GetBond(delegatee, delegator1.Address).Share / 3; delegator1.Undelegate(delegatee, shareToUndelegate, 11L); delegator1Balance = repo.World.GetBalance(delegator1.Address, delegatee.DelegationCurrency); var delegator1RewardBalances = delegatee.RewardCurrencies.Select( c => repo.World.GetBalance(delegator1.Address, c)); - var collectedRewards = DelegationFixture.TotalRewardsOfRecords(delegatee, repo); + var collectedRewards = delegatee.RewardCurrencies.Select( + c => repo.World.GetBalance(delegatee.DistributionPoolAddress(), c)); + var legacyRewards = DelegationFixture.TotalRewardsOfRecords(delegatee, repo); var rewards1 = rewards.Select(r => (r * share1).DivRem(totalShares, out _)); Assert.Equal(delegatorInitialBalance - delegatingFAV1, delegator1Balance); Assert.Equal(delegatorInitialBalance - delegatingFAV2, delegator2Balance); Assert.Equal(rewards1, delegator1RewardBalances); - Assert.Equal(rewards.Zip(rewards1, (f, s) => f * 2 - s), collectedRewards); + Assert.Equal(rewards.Zip(rewards1, (f, s) => f - s), collectedRewards); + Assert.Equal(rewards, legacyRewards); shareToUndelegate = repo.GetBond(delegatee, delegator2.Address).Share / 2; delegator2.Undelegate(delegatee, shareToUndelegate, 11L); @@ -421,7 +430,9 @@ public void RewardOnUndelegate() c => repo.World.GetBalance(delegator1.Address, c)); var delegator2RewardBalances = delegatee.RewardCurrencies.Select( c => repo.World.GetBalance(delegator2.Address, c)); - collectedRewards = DelegationFixture.TotalRewardsOfRecords(delegatee, repo); + collectedRewards = delegatee.RewardCurrencies.Select( + c => repo.World.GetBalance(delegatee.DistributionPoolAddress(), c)); + legacyRewards = DelegationFixture.TotalRewardsOfRecords(delegatee, repo); var rewards2 = rewards.Select(r => (r * share2).DivRem(totalShares, out _)); Assert.Equal(delegatorInitialBalance - delegatingFAV1, delegator1Balance); @@ -430,10 +441,12 @@ public void RewardOnUndelegate() Assert.Equal(rewards2, delegator2RewardBalances); // Flushing to remainder pool is now inactive. - // Assert.Equal(delegatee.RewardCurrencies.Select(c => c * 0), collectedRewards); Assert.Equal( - rewards.Select(r => r * 2).Zip(rewards1.Zip(rewards2, (f, s) => f + s), (f, s) => f - s).ToArray(), + rewards.Zip(rewards1.Zip(rewards2, (f, s) => f + s), (f, s) => f - s).ToArray(), collectedRewards); + Assert.Equal( + rewards, + legacyRewards); } [Fact] @@ -486,13 +499,16 @@ public void RewardOnRedelegate() delegator1Balance = repo.World.GetBalance(delegator1.Address, delegatee.DelegationCurrency); var delegator1RewardBalances = delegatee.RewardCurrencies.Select( c => repo.World.GetBalance(delegator1.Address, c)); - var collectedRewards = DelegationFixture.TotalRewardsOfRecords(delegatee, repo); + var collectedRewards = delegatee.RewardCurrencies.Select( + c => repo.World.GetBalance(delegatee.DistributionPoolAddress(), c)); + var legacyRewards = DelegationFixture.TotalRewardsOfRecords(delegatee, repo); var rewards1 = rewards.Select(r => (r * share1).DivRem(totalShares, out _)); Assert.Equal(delegatorInitialBalance - delegatingFAV1, delegator1Balance); Assert.Equal(delegatorInitialBalance - delegatingFAV2, delegator2Balance); Assert.Equal(rewards1, delegator1RewardBalances); - Assert.Equal(rewards.Zip(rewards1, (f, s) => f * 2 - s), collectedRewards); + Assert.Equal(rewards.Zip(rewards1, (f, s) => f - s), collectedRewards); + Assert.Equal(rewards, legacyRewards); shareToRedelegate = repo.GetBond(delegatee, delegator2.Address).Share / 2; delegator2.Redelegate(delegatee, dstDelegatee, shareToRedelegate, 11L); @@ -501,7 +517,9 @@ public void RewardOnRedelegate() c => repo.World.GetBalance(delegator1.Address, c)); var delegator2RewardBalances = delegatee.RewardCurrencies.Select( c => repo.World.GetBalance(delegator2.Address, c)); - collectedRewards = DelegationFixture.TotalRewardsOfRecords(delegatee, repo); + collectedRewards = delegatee.RewardCurrencies.Select( + c => repo.World.GetBalance(delegatee.DistributionPoolAddress(), c)); + legacyRewards = DelegationFixture.TotalRewardsOfRecords(delegatee, repo); var rewards2 = rewards.Select(r => (r * share2).DivRem(totalShares, out _)); Assert.Equal(delegatorInitialBalance - delegatingFAV1, delegator1Balance); @@ -512,8 +530,9 @@ public void RewardOnRedelegate() // Flushing to remainder pool is now inactive. // Assert.Equal(delegatee.RewardCurrencies.Select(c => c * 0), collectedRewards); Assert.Equal( - rewards.Select(r => r * 2).Zip(rewards1.Zip(rewards2, (f, s) => f + s), (f, s) => f - s).ToArray(), + rewards.Zip(rewards1.Zip(rewards2, (f, s) => f + s), (f, s) => f - s).ToArray(), collectedRewards); + Assert.Equal(rewards, legacyRewards); } [Fact] @@ -566,13 +585,16 @@ public void RewardOnClaim() delegator1Balance = repo.World.GetBalance(delegator1.Address, delegatee.DelegationCurrency); var delegator1RewardBalances = delegatee.RewardCurrencies.Select( c => repo.World.GetBalance(delegator1.Address, c)); - var collectedRewards = DelegationFixture.TotalRewardsOfRecords(delegatee, repo); + var collectedRewards = delegatee.RewardCurrencies.Select( + c => repo.World.GetBalance(delegatee.DistributionPoolAddress(), c)); + var legacyRewards = DelegationFixture.TotalRewardsOfRecords(delegatee, repo); var rewards1 = rewards.Select(r => (r * share1).DivRem(totalShares, out _)); Assert.Equal(delegatorInitialBalance - delegatingFAV1, delegator1Balance); Assert.Equal(delegatorInitialBalance - delegatingFAV2, delegator2Balance); Assert.Equal(rewards1, delegator1RewardBalances); - Assert.Equal(rewards.Zip(rewards1, (f, s) => f * 2 - s), collectedRewards); + Assert.Equal(rewards.Zip(rewards1, (f, s) => f - s), collectedRewards); + Assert.Equal(rewards, legacyRewards); shareToRedelegate = repo.GetBond(delegatee, delegator2.Address).Share / 2; delegator2.ClaimReward(delegatee, 11L); @@ -581,7 +603,9 @@ public void RewardOnClaim() c => repo.World.GetBalance(delegator1.Address, c)); var delegator2RewardBalances = delegatee.RewardCurrencies.Select( c => repo.World.GetBalance(delegator2.Address, c)); - collectedRewards = DelegationFixture.TotalRewardsOfRecords(delegatee, repo); + collectedRewards = delegatee.RewardCurrencies.Select( + c => repo.World.GetBalance(delegatee.DistributionPoolAddress(), c)); + legacyRewards = DelegationFixture.TotalRewardsOfRecords(delegatee, repo); var rewards2 = rewards.Select(r => (r * share2).DivRem(totalShares, out _)); Assert.Equal(delegatorInitialBalance - delegatingFAV1, delegator1Balance); @@ -592,8 +616,9 @@ public void RewardOnClaim() // Flushing to remainder pool is now inactive. // Assert.Equal(delegatee.RewardCurrencies.Select(c => c * 0), collectedRewards); Assert.Equal( - rewards.Select(r => r * 2).Zip(rewards1.Zip(rewards2, (f, s) => f + s), (f, s) => f - s).ToArray(), + rewards.Zip(rewards1.Zip(rewards2, (f, s) => f + s), (f, s) => f - s).ToArray(), collectedRewards); + Assert.Equal(rewards, legacyRewards); } } } From ea242b53cb33ad408d3597e9e2cc727a8e0d472b Mon Sep 17 00:00:00 2001 From: ilgyu Date: Wed, 11 Dec 2024 02:54:51 +0900 Subject: [PATCH 114/136] fix: Reward migration --- Lib9c/Delegation/Delegatee.cs | 26 ++++++++++++++++++++---- Lib9c/Delegation/DelegationRepository.cs | 16 +++++++-------- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/Lib9c/Delegation/Delegatee.cs b/Lib9c/Delegation/Delegatee.cs index 419ecaa972..c5d194e178 100644 --- a/Lib9c/Delegation/Delegatee.cs +++ b/Lib9c/Delegation/Delegatee.cs @@ -535,25 +535,43 @@ record = Repository.GetLumpSumRewardsRecord(this, lastStartHeight) records.Reverse(); RewardBase? rewardBase = null; + RewardBase? newRewardBase = null; foreach (var recordEach in records) { if (rewardBase is null) { rewardBase = new RewardBase( - RewardBaseAddress(recordEach.StartHeight), + CurrentRewardBaseAddress(), recordEach.TotalShares, - recordEach.LumpSumRewards.Keys, - recordEach.StartHeight); + recordEach.LumpSumRewards.Keys); + } + else + { + newRewardBase = rewardBase.UpdateTotalShares(recordEach.TotalShares); + if (Repository.GetRewardBase(this, recordEach.StartHeight) is not null) + { + Repository.SetRewardBase(newRewardBase); + } + else + { + Address archiveAddress = RewardBaseAddress(recordEach.StartHeight); + var archivedRewardBase = rewardBase.AttachHeight(archiveAddress, recordEach.StartHeight); + Repository.SetRewardBase(archivedRewardBase); + } + + rewardBase = newRewardBase; } foreach (var r in recordEach.LumpSumRewards) { rewardBase = rewardBase.AddReward(r.Value); - Repository.TransferAsset(recordEach.Address, RewardPoolAddress, Repository.GetBalance(recordEach.Address, r.Key)); + Repository.TransferAsset(recordEach.Address, DistributionPoolAddress(), Repository.GetBalance(recordEach.Address, r.Key)); } Repository.RemoveLumpSumRewardsRecord(recordEach); } + + Repository.SetRewardBase(rewardBase!); } } } diff --git a/Lib9c/Delegation/DelegationRepository.cs b/Lib9c/Delegation/DelegationRepository.cs index 8275326cf9..d67bc2f614 100644 --- a/Lib9c/Delegation/DelegationRepository.cs +++ b/Lib9c/Delegation/DelegationRepository.cs @@ -80,21 +80,21 @@ public DelegationRepository( public Address DelegatorAccountAddress { get; } - private Address DelegateeMetadataAccountAddress { get; } + public Address DelegateeMetadataAccountAddress { get; } - private Address DelegatorMetadataAccountAddress { get; } + public Address DelegatorMetadataAccountAddress { get; } - private Address BondAccountAddress { get; } + public Address BondAccountAddress { get; } - private Address UnbondLockInAccountAddress { get; } + public Address UnbondLockInAccountAddress { get; } - private Address RebondGraceAccountAddress { get; } + public Address RebondGraceAccountAddress { get; } - private Address UnbondingSetAccountAddress { get; } + public Address UnbondingSetAccountAddress { get; } - private Address RewardBaseAccountAddress { get; } + public Address RewardBaseAccountAddress { get; } - private Address LumpSumRewardsRecordAccountAddress { get; } + public Address LumpSumRewardsRecordAccountAddress { get; } public abstract IDelegatee GetDelegatee(Address address); From b408d1d0542f81fd504ebd048a3f677d56164e29 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Wed, 11 Dec 2024 02:55:11 +0900 Subject: [PATCH 115/136] test: Add reward migration test --- .Lib9c.Tests/Delegation/DelegationFixture.cs | 6 + .../Migration/LegacyDelegateeMetadata.cs | 221 -------- .../Migration/LegacyLumpSumRewardsRecord.cs | 138 ----- .../Migration/LegacyTestDelegatee.cs | 471 ++++++++++++++++++ .../Migration/LegacyTestDelegator.cs | 228 +++++++++ .../Migration/MigrateLegacyStateTest.cs | 81 --- .../Migration/RewardBaseMigrationTest.cs | 138 +++++ 7 files changed, 843 insertions(+), 440 deletions(-) delete mode 100644 .Lib9c.Tests/Delegation/Migration/LegacyDelegateeMetadata.cs delete mode 100644 .Lib9c.Tests/Delegation/Migration/LegacyLumpSumRewardsRecord.cs create mode 100644 .Lib9c.Tests/Delegation/Migration/LegacyTestDelegatee.cs create mode 100644 .Lib9c.Tests/Delegation/Migration/LegacyTestDelegator.cs delete mode 100644 .Lib9c.Tests/Delegation/Migration/MigrateLegacyStateTest.cs create mode 100644 .Lib9c.Tests/Delegation/Migration/RewardBaseMigrationTest.cs diff --git a/.Lib9c.Tests/Delegation/DelegationFixture.cs b/.Lib9c.Tests/Delegation/DelegationFixture.cs index 961775d217..ceab140717 100644 --- a/.Lib9c.Tests/Delegation/DelegationFixture.cs +++ b/.Lib9c.Tests/Delegation/DelegationFixture.cs @@ -40,12 +40,18 @@ public DelegationFixture() new Address("0x67A44E11506b8f0Bb625fEECccb205b33265Bb48"), TestRepository.DelegateeAccountAddress, TestRepository); TestDelegatee2 = new TestDelegatee( new Address("0xea1C4eedEfC99691DEfc6eF2753FAfa8C17F4584"), TestRepository.DelegateeAccountAddress, TestRepository); + TestRepository.SetDelegator(TestDelegator1); + TestRepository.SetDelegator(TestDelegator2); + TestRepository.SetDelegatee(TestDelegatee1); + TestRepository.SetDelegatee(TestDelegatee2); DummyRepository = new DummyRepository(world, context); DummyDelegatee1 = new DummyDelegatee( new Address("0x67A44E11506b8f0Bb625fEECccb205b33265Bb48"), DummyRepository.DelegateeAccountAddress, DummyRepository); DummyDelegator1 = new DummyDelegator( new Address("0x0054E98312C47E7Fa0ABed45C23Fa187e31C373a"), DummyRepository.DelegateeAccountAddress, DummyRepository); + DummyRepository.SetDelegator(DummyDelegator1); + DummyRepository.SetDelegatee(DummyDelegatee1); } public TestRepository TestRepository { get; } diff --git a/.Lib9c.Tests/Delegation/Migration/LegacyDelegateeMetadata.cs b/.Lib9c.Tests/Delegation/Migration/LegacyDelegateeMetadata.cs deleted file mode 100644 index 0cf0b90457..0000000000 --- a/.Lib9c.Tests/Delegation/Migration/LegacyDelegateeMetadata.cs +++ /dev/null @@ -1,221 +0,0 @@ -#nullable enable -namespace Lib9c.Tests.Delegation.Migration -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Linq; - using System.Numerics; - using Bencodex; - using Bencodex.Types; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume.Delegation; - - public class LegacyDelegateeMetadata : IDelegateeMetadata - { - private readonly IComparer _currencyComparer = new CurrencyComparer(); - private Address? _address; - - public LegacyDelegateeMetadata( - Address delegateeAddress, - Address delegateeAccountAddress, - Currency delegationCurrency, - IEnumerable rewardCurrencies, - Address delegationPoolAddress, - Address rewardPoolAddress, - Address rewardRemainderPoolAddress, - Address slashedPoolAddress, - long unbondingPeriod, - int maxUnbondLockInEntries, - int maxRebondGraceEntries) - : this( - delegateeAddress, - delegateeAccountAddress, - delegationCurrency, - rewardCurrencies, - delegationPoolAddress, - rewardPoolAddress, - rewardRemainderPoolAddress, - slashedPoolAddress, - unbondingPeriod, - maxUnbondLockInEntries, - maxRebondGraceEntries, - ImmutableSortedSet
.Empty, - delegationCurrency * 0, - BigInteger.Zero, - false, - -1L, - false, - ImmutableSortedSet.Empty) - { - } - - public LegacyDelegateeMetadata( - Address delegateeAddress, - Address delegateeAccountAddress, - IValue bencoded) - : this(delegateeAddress, delegateeAccountAddress, (List)bencoded) - { - } - - public LegacyDelegateeMetadata( - Address address, - Address accountAddress, - List bencoded) - : this( - address, - accountAddress, - new Currency(bencoded[0]), - ((List)bencoded[1]).Select(v => new Currency(v)), - new Address(bencoded[2]), - new Address(bencoded[3]), - new Address(bencoded[4]), - new Address(bencoded[5]), - (Integer)bencoded[6], - (Integer)bencoded[7], - (Integer)bencoded[8], - ((List)bencoded[9]).Select(item => new Address(item)), - new FungibleAssetValue(bencoded[10]), - (Integer)bencoded[11], - (Bencodex.Types.Boolean)bencoded[12], - (Integer)bencoded[13], - (Bencodex.Types.Boolean)bencoded[14], - ((List)bencoded[15]).Select(item => new UnbondingRef(item))) - { - } - - private LegacyDelegateeMetadata( - Address delegateeAddress, - Address delegateeAccountAddress, - Currency delegationCurrency, - IEnumerable rewardCurrencies, - Address delegationPoolAddress, - Address rewardPoolAddress, - Address rewardRemainderPoolAddress, - Address slashedPoolAddress, - long unbondingPeriod, - int maxUnbondLockInEntries, - int maxRebondGraceEntries, - IEnumerable
delegators, - FungibleAssetValue totalDelegated, - BigInteger totalShares, - bool jailed, - long jailedUntil, - bool tombstoned, - IEnumerable unbondingRefs) - { - if (!totalDelegated.Currency.Equals(delegationCurrency)) - { - throw new InvalidOperationException("Invalid currency."); - } - - if (totalDelegated.Sign < 0) - { - throw new ArgumentOutOfRangeException( - nameof(totalDelegated), - totalDelegated, - "Total delegated must be non-negative."); - } - - if (totalShares.Sign < 0) - { - throw new ArgumentOutOfRangeException( - nameof(totalShares), - totalShares, - "Total shares must be non-negative."); - } - - DelegateeAddress = delegateeAddress; - DelegateeAccountAddress = delegateeAccountAddress; - DelegationCurrency = delegationCurrency; - RewardCurrencies = rewardCurrencies.ToImmutableSortedSet(_currencyComparer); - DelegationPoolAddress = delegationPoolAddress; - RewardPoolAddress = rewardPoolAddress; - RewardRemainderPoolAddress = rewardRemainderPoolAddress; - SlashedPoolAddress = slashedPoolAddress; - UnbondingPeriod = unbondingPeriod; - MaxUnbondLockInEntries = maxUnbondLockInEntries; - MaxRebondGraceEntries = maxRebondGraceEntries; - Delegators = delegators.ToImmutableSortedSet(); - TotalDelegatedFAV = totalDelegated; - TotalShares = totalShares; - Jailed = jailed; - JailedUntil = jailedUntil; - Tombstoned = tombstoned; - UnbondingRefs = unbondingRefs.ToImmutableSortedSet(); - } - - public Address DelegateeAddress { get; } - - public Address DelegateeAccountAddress { get; } - - public Address Address - => _address ??= DelegationAddress.DelegateeMetadataAddress( - DelegateeAddress, - DelegateeAccountAddress); - - public Currency DelegationCurrency { get; } - - public ImmutableSortedSet RewardCurrencies { get; } - - public Address DelegationPoolAddress { get; internal set; } - - public Address RewardPoolAddress { get; } - - public Address RewardRemainderPoolAddress { get; } - - public Address SlashedPoolAddress { get; } - - public long UnbondingPeriod { get; private set; } - - public int MaxUnbondLockInEntries { get; } - - public int MaxRebondGraceEntries { get; } - - public ImmutableSortedSet
Delegators { get; private set; } - - public FungibleAssetValue TotalDelegatedFAV { get; private set; } - - public BigInteger TotalShares { get; private set; } - - public bool Jailed { get; internal set; } - - public long JailedUntil { get; internal set; } - - public bool Tombstoned { get; internal set; } - - public ImmutableSortedSet UnbondingRefs { get; private set; } - - // TODO : Better serialization - public List Bencoded => List.Empty - .Add(DelegationCurrency.Serialize()) - .Add(new List(RewardCurrencies.Select(c => c.Serialize()))) - .Add(DelegationPoolAddress.Bencoded) - .Add(RewardPoolAddress.Bencoded) - .Add(RewardRemainderPoolAddress.Bencoded) - .Add(SlashedPoolAddress.Bencoded) - .Add(UnbondingPeriod) - .Add(MaxUnbondLockInEntries) - .Add(MaxRebondGraceEntries) - .Add(new List(Delegators.Select(delegator => delegator.Bencoded))) - .Add(TotalDelegatedFAV.Serialize()) - .Add(TotalShares) - .Add(Jailed) - .Add(JailedUntil) - .Add(Tombstoned) - .Add(new List(UnbondingRefs.Select(unbondingRef => unbondingRef.Bencoded))); - - IValue IBencodable.Bencoded => Bencoded; - - public BigInteger ShareFromFAV(FungibleAssetValue fav) - => TotalShares.IsZero - ? fav.RawValue - : TotalShares * fav.RawValue / TotalDelegatedFAV.RawValue; - - public FungibleAssetValue FAVFromShare(BigInteger share) - => TotalShares == share - ? TotalDelegatedFAV - : (TotalDelegatedFAV * share).DivRem(TotalShares).Quotient; - } -} diff --git a/.Lib9c.Tests/Delegation/Migration/LegacyLumpSumRewardsRecord.cs b/.Lib9c.Tests/Delegation/Migration/LegacyLumpSumRewardsRecord.cs deleted file mode 100644 index abb6be9f37..0000000000 --- a/.Lib9c.Tests/Delegation/Migration/LegacyLumpSumRewardsRecord.cs +++ /dev/null @@ -1,138 +0,0 @@ -#nullable enable -namespace Lib9c.Tests.Delegation.Migration -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Linq; - using System.Numerics; - using Bencodex; - using Bencodex.Types; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume.Delegation; - - public class LegacyLumpSumRewardsRecord : IBencodable - { - private readonly IComparer _currencyComparer = new CurrencyComparer(); - - public LegacyLumpSumRewardsRecord( - Address address, - long startHeight, - BigInteger totalShares, - ImmutableSortedSet
delegators, - IEnumerable currencies) - : this( - address, - startHeight, - totalShares, - delegators, - currencies, - null) - { - } - - public LegacyLumpSumRewardsRecord( - Address address, - long startHeight, - BigInteger totalShares, - ImmutableSortedSet
delegators, - IEnumerable currencies, - long? lastStartHeight) - : this( - address, - startHeight, - totalShares, - delegators, - currencies.Select(c => c * 0), - lastStartHeight) - { - } - - public LegacyLumpSumRewardsRecord( - Address address, - long startHeight, - BigInteger totalShares, - ImmutableSortedSet
delegators, - IEnumerable lumpSumRewards, - long? lastStartHeight) - { - Address = address; - StartHeight = startHeight; - TotalShares = totalShares; - Delegators = delegators; - - if (!lumpSumRewards.Select(f => f.Currency).All(new HashSet().Add)) - { - throw new ArgumentException("Duplicated currency in lump sum rewards."); - } - - LumpSumRewards = lumpSumRewards.ToImmutableDictionary(f => f.Currency, f => f); - LastStartHeight = lastStartHeight; - } - - public LegacyLumpSumRewardsRecord(Address address, IValue bencoded) - : this(address, (List)bencoded) - { - } - - public LegacyLumpSumRewardsRecord(Address address, List bencoded) - : this( - address, - (Integer)bencoded[0], - (Integer)bencoded[1], - ((List)bencoded[2]).Select(a => new Address(a)).ToImmutableSortedSet(), - ((List)bencoded[3]).Select(v => new FungibleAssetValue(v)), - (Integer?)bencoded.ElementAtOrDefault(4)) - { - } - - private LegacyLumpSumRewardsRecord( - Address address, - long startHeight, - BigInteger totalShares, - ImmutableSortedSet
delegators, - ImmutableDictionary lumpSumRewards, - long? lastStartHeight) - { - Address = address; - StartHeight = startHeight; - TotalShares = totalShares; - Delegators = delegators; - LumpSumRewards = lumpSumRewards; - LastStartHeight = lastStartHeight; - } - - public Address Address { get; } - - public long StartHeight { get; } - - public BigInteger TotalShares { get; } - - public ImmutableDictionary LumpSumRewards { get; } - - public ImmutableSortedSet
Delegators { get; } - - public long? LastStartHeight { get; } - - public List Bencoded - { - get - { - var bencoded = List.Empty - .Add(StartHeight) - .Add(TotalShares) - .Add(new List(Delegators.Select(a => a.Bencoded))) - .Add(new List(LumpSumRewards - .OrderBy(r => r.Key, _currencyComparer) - .Select(r => r.Value.Serialize()))); - - return LastStartHeight is long lastStartHeight - ? bencoded.Add(lastStartHeight) - : bencoded; - } - } - - IValue IBencodable.Bencoded => Bencoded; - } -} diff --git a/.Lib9c.Tests/Delegation/Migration/LegacyTestDelegatee.cs b/.Lib9c.Tests/Delegation/Migration/LegacyTestDelegatee.cs new file mode 100644 index 0000000000..ef76cd00bc --- /dev/null +++ b/.Lib9c.Tests/Delegation/Migration/LegacyTestDelegatee.cs @@ -0,0 +1,471 @@ +#nullable enable +namespace Lib9c.Tests.Delegation.Migration +{ + using System; + using System.Collections.Generic; + using System.Collections.Immutable; + using System.Linq; + using System.Numerics; + using Bencodex.Types; + using Libplanet.Crypto; + using Libplanet.Types.Assets; + using Nekoyume.Delegation; + + public class LegacyTestDelegatee : IDelegatee + { + public LegacyTestDelegatee( + Address address, + Address accountAddress, + Currency delegationCurrency, + IEnumerable rewardCurrencies, + Address delegationPoolAddress, + Address rewardPoolAddress, + Address rewardRemainderPoolAddress, + Address slashedPoolAddress, + long unbondingPeriod, + int maxUnbondLockInEntries, + int maxRebondGraceEntries, + IDelegationRepository repository) + : this( + new DelegateeMetadata( + address, + accountAddress, + delegationCurrency, + rewardCurrencies, + delegationPoolAddress, + rewardPoolAddress, + rewardRemainderPoolAddress, + slashedPoolAddress, + unbondingPeriod, + maxUnbondLockInEntries, + maxRebondGraceEntries), + repository) + { + } + + public LegacyTestDelegatee( + Address address, + IDelegationRepository repository) + : this(repository.GetDelegateeMetadata(address), repository) + { + } + + private LegacyTestDelegatee(DelegateeMetadata metadata, IDelegationRepository repository) + { + Metadata = metadata; + Repository = repository; + } + + public event EventHandler? DelegationChanged; + + public event EventHandler? Enjailed; + + public event EventHandler? Unjailed; + + public DelegateeMetadata Metadata { get; } + + public IDelegationRepository Repository { get; } + + public Address Address => Metadata.DelegateeAddress; + + public Address AccountAddress => Metadata.DelegateeAccountAddress; + + public Address MetadataAddress => Metadata.Address; + + public Currency DelegationCurrency => Metadata.DelegationCurrency; + + public ImmutableSortedSet RewardCurrencies => Metadata.RewardCurrencies; + + public Address DelegationPoolAddress => Metadata.DelegationPoolAddress; + + public Address RewardPoolAddress => Metadata.RewardPoolAddress; + + public Address RewardRemainderPoolAddress => Metadata.RewardRemainderPoolAddress; + + public Address SlashedPoolAddress => Metadata.SlashedPoolAddress; + + public long UnbondingPeriod => Metadata.UnbondingPeriod; + + public int MaxUnbondLockInEntries => Metadata.MaxUnbondLockInEntries; + + public int MaxRebondGraceEntries => Metadata.MaxRebondGraceEntries; + + public FungibleAssetValue TotalDelegated => Metadata.TotalDelegatedFAV; + + public BigInteger TotalShares => Metadata.TotalShares; + + public bool Jailed => Metadata.Jailed; + + public long JailedUntil => Metadata.JailedUntil; + + public bool Tombstoned => Metadata.Tombstoned; + + public List MetadataBencoded => Metadata.Bencoded; + + public BigInteger ShareFromFAV(FungibleAssetValue fav) + => Metadata.ShareFromFAV(fav); + + public FungibleAssetValue FAVFromShare(BigInteger share) + => Metadata.FAVFromShare(share); + + public BigInteger Bond(IDelegator delegator, FungibleAssetValue fav, long height) + => Bond((LegacyTestDelegator)delegator, fav, height); + + public FungibleAssetValue Unbond(IDelegator delegator, BigInteger share, long height) + => Unbond((LegacyTestDelegator)delegator, share, height); + + public void DistributeReward(IDelegator delegator, long height) + => DistributeReward((LegacyTestDelegator)delegator, height); + + public void Jail(long releaseHeight) + { + Metadata.JailedUntil = releaseHeight; + Metadata.Jailed = true; + Repository.SetDelegateeMetadata(Metadata); + Enjailed?.Invoke(this, EventArgs.Empty); + } + + public void Unjail(long height) + { + if (!Jailed) + { + throw new InvalidOperationException("Cannot unjail non-jailed delegatee."); + } + + if (Tombstoned) + { + throw new InvalidOperationException("Cannot unjail tombstoned delegatee."); + } + + if (JailedUntil >= height) + { + throw new InvalidOperationException("Cannot unjail before jailed until."); + } + + Metadata.JailedUntil = -1L; + Metadata.Jailed = false; + Repository.SetDelegateeMetadata(Metadata); + Unjailed?.Invoke(this, EventArgs.Empty); + } + + public void Tombstone() + { + Jail(long.MaxValue); + Metadata.Tombstoned = true; + Repository.SetDelegateeMetadata(Metadata); + } + + public Address BondAddress(Address delegatorAddress) + => Metadata.BondAddress(delegatorAddress); + + public Address UnbondLockInAddress(Address delegatorAddress) + => Metadata.UnbondLockInAddress(delegatorAddress); + + public Address RebondGraceAddress(Address delegatorAddress) + => Metadata.RebondGraceAddress(delegatorAddress); + + public Address CurrentLumpSumRewardsRecordAddress() + => Metadata.CurrentLumpSumRewardsRecordAddress(); + + public Address LumpSumRewardsRecordAddress(long height) + => Metadata.LumpSumRewardsRecordAddress(height); + + public Address CurrentRewardBaseAddress() => throw new NotImplementedException(); + + public Address RewardBaseAddress(long height) => throw new NotImplementedException(); + + public BigInteger Bond(LegacyTestDelegator delegator, FungibleAssetValue fav, long height) + { + DistributeReward(delegator, height); + + if (!fav.Currency.Equals(DelegationCurrency)) + { + throw new InvalidOperationException( + "Cannot bond with invalid currency."); + } + + if (Tombstoned) + { + throw new InvalidOperationException( + "Cannot bond to tombstoned delegatee."); + } + + Bond bond = Repository.GetBond(this, delegator.Address); + BigInteger share = ShareFromFAV(fav); + bond = bond.AddShare(share); + Metadata.AddShare(share); + Metadata.AddDelegatedFAV(fav); + Repository.SetBond(bond); + StartNewRewardPeriod(height); + Repository.SetDelegateeMetadata(Metadata); + DelegationChanged?.Invoke(this, height); + + return share; + } + + BigInteger IDelegatee.Bond(IDelegator delegator, FungibleAssetValue fav, long height) + => Bond((LegacyTestDelegator)delegator, fav, height); + + public FungibleAssetValue Unbond(LegacyTestDelegator delegator, BigInteger share, long height) + { + DistributeReward(delegator, height); + if (TotalShares.IsZero || TotalDelegated.RawValue.IsZero) + { + throw new InvalidOperationException( + "Cannot unbond without bonding."); + } + + Bond bond = Repository!.GetBond(this, delegator.Address); + FungibleAssetValue fav = FAVFromShare(share); + bond = bond.SubtractShare(share); + if (bond.Share.IsZero) + { + bond = bond.ClearLastDistributeHeight(); + } + + Metadata.RemoveShare(share); + Metadata.RemoveDelegatedFAV(fav); + Repository.SetBond(bond); + StartNewRewardPeriod(height); + Repository.SetDelegateeMetadata(Metadata); + DelegationChanged?.Invoke(this, height); + + return fav; + } + + FungibleAssetValue IDelegatee.Unbond(IDelegator delegator, BigInteger share, long height) + => Unbond((LegacyTestDelegator)delegator, share, height); + + public void DistributeReward(LegacyTestDelegator delegator, long height) + { + Bond bond = Repository.GetBond(this, delegator.Address); + BigInteger share = bond.Share; + + if (!share.IsZero && bond.LastDistributeHeight.HasValue) + { + IEnumerable lumpSumRewardsRecords + = GetLumpSumRewardsRecords(bond.LastDistributeHeight); + + foreach (LumpSumRewardsRecord record in lumpSumRewardsRecords) + { + TransferReward(delegator, share, record); + // TransferRemainders(newRecord); + Repository.SetLumpSumRewardsRecord(record); + } + } + + if (bond.LastDistributeHeight != height) + { + bond = bond.UpdateLastDistributeHeight(height); + } + + Repository.SetBond(bond); + } + + void IDelegatee.DistributeReward(IDelegator delegator, long height) + => DistributeReward((LegacyTestDelegator)delegator, height); + + public void CollectRewards(long height) + { + var rewards = RewardCurrencies.Select(c => Repository.GetBalance(RewardPoolAddress, c)); + LumpSumRewardsRecord record = Repository.GetCurrentLumpSumRewardsRecord(this) + ?? new LumpSumRewardsRecord( + CurrentLumpSumRewardsRecordAddress(), + height, + TotalShares, + RewardCurrencies); + record = record.AddLumpSumRewards(rewards); + + foreach (var rewardsEach in rewards) + { + if (rewardsEach.Sign > 0) + { + Repository.TransferAsset(RewardPoolAddress, record.Address, rewardsEach); + } + } + + Repository.SetLumpSumRewardsRecord(record); + } + + public virtual void Slash(BigInteger slashFactor, long infractionHeight, long height) + { + FungibleAssetValue slashed = TotalDelegated.DivRem(slashFactor, out var rem); + if (rem.Sign > 0) + { + slashed += FungibleAssetValue.FromRawValue(rem.Currency, 1); + } + + if (slashed > Metadata.TotalDelegatedFAV) + { + slashed = Metadata.TotalDelegatedFAV; + } + + Metadata.RemoveDelegatedFAV(slashed); + + foreach (var item in Metadata.UnbondingRefs) + { + var unbonding = UnbondingFactory.GetUnbondingFromRef(item, Repository); + + unbonding = unbonding.Slash(slashFactor, infractionHeight, height, out var slashedFAV); + + if (slashedFAV.HasValue) + { + slashed += slashedFAV.Value; + } + + if (unbonding.IsEmpty) + { + Metadata.RemoveUnbondingRef(item); + } + + switch (unbonding) + { + case UnbondLockIn unbondLockIn: + Repository.SetUnbondLockIn(unbondLockIn); + break; + case RebondGrace rebondGrace: + Repository.SetRebondGrace(rebondGrace); + break; + default: + throw new InvalidOperationException("Invalid unbonding type."); + } + } + + var delegationBalance = Repository.GetBalance(DelegationPoolAddress, DelegationCurrency); + if (delegationBalance < slashed) + { + slashed = delegationBalance; + } + + if (slashed > DelegationCurrency * 0) + { + Repository.TransferAsset(DelegationPoolAddress, SlashedPoolAddress, slashed); + } + + Repository.SetDelegateeMetadata(Metadata); + DelegationChanged?.Invoke(this, height); + } + + void IDelegatee.Slash(BigInteger slashFactor, long infractionHeight, long height) + => Slash(slashFactor, infractionHeight, height); + + public void AddUnbondingRef(UnbondingRef reference) + => Metadata.AddUnbondingRef(reference); + + public void RemoveUnbondingRef(UnbondingRef reference) + => Metadata.RemoveUnbondingRef(reference); + + public ImmutableDictionary CalculateReward( + BigInteger share, + IEnumerable lumpSumRewardsRecords) + { + ImmutableDictionary reward + = RewardCurrencies.ToImmutableDictionary(c => c, c => c * 0); + + foreach (LumpSumRewardsRecord record in lumpSumRewardsRecords) + { + var rewardDuringPeriod = record.RewardsDuringPeriod(share); + reward = rewardDuringPeriod.Aggregate(reward, (acc, pair) + => acc.SetItem(pair.Key, acc[pair.Key] + pair.Value)); + } + + return reward; + } + + private void StartNewRewardPeriod(long height) + { + LumpSumRewardsRecord? currentRecord = Repository.GetCurrentLumpSumRewardsRecord(this); + long? lastStartHeight = null; + if (currentRecord is LumpSumRewardsRecord lastRecord) + { + lastStartHeight = lastRecord.StartHeight; + if (lastStartHeight == height) + { + currentRecord = new ( + currentRecord.Address, + currentRecord.StartHeight, + TotalShares, + RewardCurrencies, + currentRecord.LastStartHeight); + + Repository.SetLumpSumRewardsRecord(currentRecord); + return; + } + + Address archiveAddress = LumpSumRewardsRecordAddress(lastRecord.StartHeight); + + foreach (var rewardCurrency in RewardCurrencies) + { + FungibleAssetValue reward = Repository.GetBalance(lastRecord.Address, rewardCurrency); + if (reward.Sign > 0) + { + Repository.TransferAsset(lastRecord.Address, archiveAddress, reward); + } + } + + lastRecord = lastRecord.MoveAddress(archiveAddress); + Repository.SetLumpSumRewardsRecord(lastRecord); + } + + LumpSumRewardsRecord newRecord = new ( + CurrentLumpSumRewardsRecordAddress(), + height, + TotalShares, + RewardCurrencies, + lastStartHeight); + + Repository.SetLumpSumRewardsRecord(newRecord); + } + + private List GetLumpSumRewardsRecords(long? lastRewardHeight) + { + List records = new (); + if (lastRewardHeight is null + || !(Repository.GetCurrentLumpSumRewardsRecord(this) is LumpSumRewardsRecord record)) + { + return records; + } + + while (record.StartHeight >= lastRewardHeight) + { + records.Add(record); + + if (!(record.LastStartHeight is long lastStartHeight)) + { + break; + } + + record = Repository.GetLumpSumRewardsRecord(this, lastStartHeight) + ?? throw new InvalidOperationException( + $"Lump sum rewards record for #{lastStartHeight} is missing"); + } + + return records; + } + + private void TransferReward(LegacyTestDelegator delegator, BigInteger share, LumpSumRewardsRecord record) + { + ImmutableSortedDictionary reward = record.RewardsDuringPeriod(share); + foreach (var rewardEach in reward) + { + if (rewardEach.Value.Sign > 0) + { + Repository.TransferAsset(record.Address, delegator.RewardAddress, rewardEach.Value); + } + } + } + + private void TransferRemainders(LumpSumRewardsRecord record) + { + foreach (var rewardCurrency in RewardCurrencies) + { + FungibleAssetValue remainder = Repository.GetBalance(record.Address, rewardCurrency); + + if (remainder.Sign > 0) + { + Repository.TransferAsset(record.Address, RewardRemainderPoolAddress, remainder); + } + } + } + } +} diff --git a/.Lib9c.Tests/Delegation/Migration/LegacyTestDelegator.cs b/.Lib9c.Tests/Delegation/Migration/LegacyTestDelegator.cs new file mode 100644 index 0000000000..a2ab3ade94 --- /dev/null +++ b/.Lib9c.Tests/Delegation/Migration/LegacyTestDelegator.cs @@ -0,0 +1,228 @@ +#nullable enable +namespace Lib9c.Tests.Delegation.Migration +{ + using System; + using System.Collections.Immutable; + using System.Numerics; + using Bencodex.Types; + using Libplanet.Crypto; + using Libplanet.Types.Assets; + using Nekoyume.Delegation; + + public class LegacyTestDelegator : IDelegator + { + public LegacyTestDelegator( + Address address, + Address accountAddress, + Address delegationPoolAddress, + Address rewardAddress, + IDelegationRepository repository) + : this( + new DelegatorMetadata( + address, + accountAddress, + delegationPoolAddress, + rewardAddress), + repository) + { + } + + public LegacyTestDelegator( + Address address, + IDelegationRepository repository) + : this(repository.GetDelegatorMetadata(address), repository) + { + } + + private LegacyTestDelegator(DelegatorMetadata metadata, IDelegationRepository repository) + { + Metadata = metadata; + Repository = repository; + } + + public DelegatorMetadata Metadata { get; } + + public IDelegationRepository Repository { get; } + + public Address Address => Metadata.DelegatorAddress; + + public Address AccountAddress => Metadata.DelegatorAccountAddress; + + public Address MetadataAddress => Metadata.Address; + + public Address DelegationPoolAddress => Metadata.DelegationPoolAddress; + + public Address RewardAddress => Metadata.RewardAddress; + + public ImmutableSortedSet
Delegatees => Metadata.Delegatees; + + public List MetadataBencoded => Metadata.Bencoded; + + public void Delegate( + LegacyTestDelegatee delegatee, FungibleAssetValue fav, long height) + { + if (fav.Sign <= 0) + { + throw new ArgumentOutOfRangeException( + nameof(fav), fav, "Fungible asset value must be positive."); + } + + if (delegatee.Tombstoned) + { + throw new InvalidOperationException("Delegatee is tombstoned."); + } + + delegatee.Bond(this, fav, height); + Metadata.AddDelegatee(delegatee.Address); + Repository.TransferAsset(DelegationPoolAddress, delegatee.DelegationPoolAddress, fav); + Repository.SetDelegatorMetadata(Metadata); + } + + void IDelegator.Delegate( + IDelegatee delegatee, FungibleAssetValue fav, long height) + => Delegate((LegacyTestDelegatee)delegatee, fav, height); + + public void Undelegate( + LegacyTestDelegatee delegatee, BigInteger share, long height) + { + if (share.Sign <= 0) + { + throw new ArgumentOutOfRangeException( + nameof(share), share, "Share must be positive."); + } + + if (height <= 0) + { + throw new ArgumentOutOfRangeException( + nameof(height), height, "Height must be positive."); + } + + UnbondLockIn unbondLockIn = Repository.GetUnbondLockIn(delegatee, Address); + + if (unbondLockIn.IsFull) + { + throw new InvalidOperationException("Undelegation is full."); + } + + FungibleAssetValue fav = delegatee.Unbond(this, share, height); + unbondLockIn = unbondLockIn.LockIn( + fav, height, height + delegatee.UnbondingPeriod); + + if (Repository.GetBond(delegatee, Address).Share.IsZero) + { + Metadata.RemoveDelegatee(delegatee.Address); + } + + delegatee.AddUnbondingRef(UnbondingFactory.ToReference(unbondLockIn)); + + Repository.SetUnbondLockIn(unbondLockIn); + Repository.SetUnbondingSet( + Repository.GetUnbondingSet().SetUnbonding(unbondLockIn)); + Repository.SetDelegatorMetadata(Metadata); + } + + void IDelegator.Undelegate( + IDelegatee delegatee, BigInteger share, long height) + => Undelegate((LegacyTestDelegatee)delegatee, share, height); + + public void Redelegate( + LegacyTestDelegatee srcDelegatee, LegacyTestDelegatee dstDelegatee, BigInteger share, long height) + { + if (share.Sign <= 0) + { + throw new ArgumentOutOfRangeException( + nameof(share), share, "Share must be positive."); + } + + if (height <= 0) + { + throw new ArgumentOutOfRangeException( + nameof(height), height, "Height must be positive."); + } + + if (dstDelegatee.Tombstoned) + { + throw new InvalidOperationException("Destination delegatee is tombstoned."); + } + + FungibleAssetValue fav = srcDelegatee.Unbond( + this, share, height); + dstDelegatee.Bond( + this, fav, height); + RebondGrace srcRebondGrace = Repository.GetRebondGrace(srcDelegatee, Address).Grace( + dstDelegatee.Address, + fav, + height, + height + srcDelegatee.UnbondingPeriod); + + if (Repository.GetBond(srcDelegatee, Address).Share.IsZero) + { + Metadata.RemoveDelegatee(srcDelegatee.Address); + } + + Metadata.AddDelegatee(dstDelegatee.Address); + + srcDelegatee.AddUnbondingRef(UnbondingFactory.ToReference(srcRebondGrace)); + + Repository.SetRebondGrace(srcRebondGrace); + Repository.SetUnbondingSet( + Repository.GetUnbondingSet().SetUnbonding(srcRebondGrace)); + Repository.SetDelegatorMetadata(Metadata); + } + + void IDelegator.Redelegate( + IDelegatee srcDelegatee, IDelegatee dstDelegatee, BigInteger share, long height) + => Redelegate((LegacyTestDelegatee)srcDelegatee, (LegacyTestDelegatee)dstDelegatee, share, height); + + public void CancelUndelegate( + LegacyTestDelegatee delegatee, FungibleAssetValue fav, long height) + { + if (fav.Sign <= 0) + { + throw new ArgumentOutOfRangeException( + nameof(fav), fav, "Fungible asset value must be positive."); + } + + if (height <= 0) + { + throw new ArgumentOutOfRangeException( + nameof(height), height, "Height must be positive."); + } + + UnbondLockIn unbondLockIn = Repository.GetUnbondLockIn(delegatee, Address); + + if (unbondLockIn.IsFull) + { + throw new InvalidOperationException("Undelegation is full."); + } + + delegatee.Bond(this, fav, height); + unbondLockIn = unbondLockIn.Cancel(fav, height); + Metadata.AddDelegatee(delegatee.Address); + + if (unbondLockIn.IsEmpty) + { + delegatee.RemoveUnbondingRef(UnbondingFactory.ToReference(unbondLockIn)); + } + + Repository.SetUnbondLockIn(unbondLockIn); + Repository.SetUnbondingSet( + Repository.GetUnbondingSet().SetUnbonding(unbondLockIn)); + Repository.SetDelegatorMetadata(Metadata); + } + + void IDelegator.CancelUndelegate( + IDelegatee delegatee, FungibleAssetValue fav, long height) + => CancelUndelegate((LegacyTestDelegatee)delegatee, fav, height); + + public void ClaimReward( + LegacyTestDelegatee delegatee, long height) + { + delegatee.DistributeReward(this, height); + Repository.SetDelegatorMetadata(Metadata); + } + + void IDelegator.ClaimReward(IDelegatee delegatee, long height) + => ClaimReward((LegacyTestDelegatee)delegatee, height); + } +} diff --git a/.Lib9c.Tests/Delegation/Migration/MigrateLegacyStateTest.cs b/.Lib9c.Tests/Delegation/Migration/MigrateLegacyStateTest.cs deleted file mode 100644 index 4ed115b985..0000000000 --- a/.Lib9c.Tests/Delegation/Migration/MigrateLegacyStateTest.cs +++ /dev/null @@ -1,81 +0,0 @@ -namespace Lib9c.Tests.Delegation.Migration -{ - using System.Collections.Immutable; - using System.Linq; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume.Delegation; - using Xunit; - - public class MigrateLegacyStateTest - { - [Fact] - public void ParseLegacyDelegateeMetadata() - { - var address = new PrivateKey().Address; - var accountAddress = new PrivateKey().Address; - var delegationCurrency = Currency.Uncapped("del", 5, null); - var rewardCurrencies = new Currency[] { Currency.Uncapped("rew", 5, null), }; - var delegationPoolAddress = new PrivateKey().Address; - var rewardPoolAddress = new PrivateKey().Address; - var rewardRemainderPoolAddress = new PrivateKey().Address; - var slashedPoolAddress = new PrivateKey().Address; - var unbondingPeriod = 1L; - var maxUnbondLockInEntries = 2; - var maxRebondGraceEntries = 3; - - var legacyDelegateeMetadataBencoded = new LegacyDelegateeMetadata( - address, - accountAddress, - delegationCurrency, - rewardCurrencies, - delegationPoolAddress, - rewardPoolAddress, - rewardRemainderPoolAddress, - slashedPoolAddress, - unbondingPeriod, - maxUnbondLockInEntries, - maxRebondGraceEntries).Bencoded; - - var delegateeMetadata = new DelegateeMetadata(address, accountAddress, legacyDelegateeMetadataBencoded); - - Assert.Equal(address, delegateeMetadata.DelegateeAddress); - Assert.Equal(accountAddress, delegateeMetadata.DelegateeAccountAddress); - Assert.Equal(delegationCurrency, delegateeMetadata.DelegationCurrency); - Assert.Equal(rewardCurrencies, delegateeMetadata.RewardCurrencies); - Assert.Equal(delegationPoolAddress, delegateeMetadata.DelegationPoolAddress); - Assert.Equal(rewardPoolAddress, delegateeMetadata.RewardPoolAddress); - Assert.Equal(rewardRemainderPoolAddress, delegateeMetadata.RewardRemainderPoolAddress); - Assert.Equal(slashedPoolAddress, delegateeMetadata.SlashedPoolAddress); - Assert.Equal(unbondingPeriod, delegateeMetadata.UnbondingPeriod); - Assert.Equal(maxUnbondLockInEntries, delegateeMetadata.MaxUnbondLockInEntries); - Assert.Equal(maxRebondGraceEntries, delegateeMetadata.MaxRebondGraceEntries); - } - - [Fact] - public void ParseLegacyLumpSumRewardsRecord() - { - var address = new PrivateKey().Address; - var startHeight = 1L; - var totalShares = 2; - var delegators = ImmutableSortedSet.Create
(new PrivateKey().Address); - var currencies = new Currency[] { Currency.Uncapped("cur", 5, null), }; - var lastStartHeight = 3L; - - var legacyLumpSumRewardsRecordBencoded = new LegacyLumpSumRewardsRecord( - address, - startHeight, - totalShares, - delegators, - currencies, - lastStartHeight).Bencoded; - - var lumpSumRewardsRecord = new LumpSumRewardsRecord(address, legacyLumpSumRewardsRecordBencoded); - - Assert.Equal(address, lumpSumRewardsRecord.Address); - Assert.Equal(startHeight, lumpSumRewardsRecord.StartHeight); - Assert.Equal(totalShares, lumpSumRewardsRecord.TotalShares); - Assert.Equal(currencies, lumpSumRewardsRecord.LumpSumRewards.Select(c => c.Key)); - } - } -} diff --git a/.Lib9c.Tests/Delegation/Migration/RewardBaseMigrationTest.cs b/.Lib9c.Tests/Delegation/Migration/RewardBaseMigrationTest.cs new file mode 100644 index 0000000000..3dcca62e6c --- /dev/null +++ b/.Lib9c.Tests/Delegation/Migration/RewardBaseMigrationTest.cs @@ -0,0 +1,138 @@ +#nullable enable +namespace Lib9c.Tests.Delegation.Migration +{ + using System.Linq; + using Nekoyume.Delegation; + using Nekoyume.Extensions; + using Xunit; + + public class RewardBaseMigrationTest + { + private readonly DelegationFixture _fixture; + + public RewardBaseMigrationTest() + { + _fixture = new DelegationFixture(); + } + + public LegacyTestDelegatee LegacyDelegatee + => new LegacyTestDelegatee( + _fixture.TestDelegatee1.Address, + _fixture.TestRepository); + + public LegacyTestDelegator LegacyDelegator1 + => new LegacyTestDelegator( + _fixture.TestDelegator1.Address, + _fixture.TestRepository); + + public LegacyTestDelegator LegacyDelegator2 + => new LegacyTestDelegator( + _fixture.TestDelegator2.Address, + _fixture.TestRepository); + + [Fact] + public void Migrate() + { + var repo = _fixture.TestRepository; + + var delegatorInitialBalance = LegacyDelegatee.DelegationCurrency * 2000; + repo.MintAsset(LegacyDelegator1.Address, delegatorInitialBalance); + repo.MintAsset(LegacyDelegator2.Address, delegatorInitialBalance); + + var rewards = LegacyDelegatee.RewardCurrencies.Select(r => r * 100); + foreach (var reward in rewards) + { + repo.MintAsset(LegacyDelegatee.RewardPoolAddress, reward); + } + + LegacyDelegatee.CollectRewards(7L); + + var delegatingFAV = LegacyDelegatee.DelegationCurrency * 100; + LegacyDelegator1.Delegate(LegacyDelegatee, delegatingFAV, 10L); + + foreach (var reward in rewards) + { + repo.MintAsset(LegacyDelegatee.RewardPoolAddress, reward); + } + + LegacyDelegatee.CollectRewards(13L); + + var delegatingFAV1 = LegacyDelegatee.DelegationCurrency * 100; + LegacyDelegator2.Delegate(LegacyDelegatee, delegatingFAV1, 15L); + + foreach (var reward in rewards) + { + repo.MintAsset(LegacyDelegatee.RewardPoolAddress, reward); + } + + LegacyDelegatee.CollectRewards(17L); + var delegatingFAV2 = LegacyDelegatee.DelegationCurrency * 200; + LegacyDelegator2.Delegate(LegacyDelegatee, delegatingFAV2, 20L); + + foreach (var reward in rewards) + { + repo.MintAsset(LegacyDelegatee.RewardPoolAddress, reward); + } + + LegacyDelegatee.CollectRewards(23L); + var delegatingFAV3 = LegacyDelegatee.DelegationCurrency * 300; + LegacyDelegator2.Delegate(LegacyDelegatee, delegatingFAV3, 25L); + + foreach (var reward in rewards) + { + repo.MintAsset(LegacyDelegatee.RewardPoolAddress, reward); + } + + LegacyDelegatee.CollectRewards(27L); + var delegatingFAV4 = LegacyDelegatee.DelegationCurrency * 400; + LegacyDelegator2.Delegate(LegacyDelegatee, delegatingFAV4, 30L); + + foreach (var reward in rewards) + { + repo.MintAsset(LegacyDelegatee.RewardPoolAddress, reward); + } + + LegacyDelegatee.CollectRewards(23L); + + _fixture.TestRepository.UpdateWorld(_fixture.TestRepository.World.MutateAccount( + _fixture.TestRepository.DelegateeMetadataAccountAddress, + a => a.SetState(LegacyDelegatee.MetadataAddress, LegacyDelegatee.MetadataBencoded))); + _fixture.TestRepository.UpdateWorld(_fixture.TestRepository.World.MutateAccount( + _fixture.TestRepository.DelegatorMetadataAccountAddress, + a => a.SetState(LegacyDelegatee.Metadata.Address, LegacyDelegatee.Metadata.Bencoded))); + _fixture.TestRepository.UpdateWorld(_fixture.TestRepository.World.MutateAccount( + _fixture.TestRepository.DelegatorMetadataAccountAddress, + a => a.SetState(LegacyDelegatee.Metadata.Address, LegacyDelegatee.Metadata.Bencoded))); + + var delegator1 = _fixture.TestRepository.GetDelegator(_fixture.TestDelegator1.Address); + var delegator2 = _fixture.TestRepository.GetDelegator(_fixture.TestDelegator2.Address); + var delegatee = _fixture.TestRepository.GetDelegatee(_fixture.TestDelegatee1.Address); + + var delegatingFAV5 = delegatee.DelegationCurrency * 500; + delegator2.Delegate(delegatee, delegatingFAV5, 35L); + + foreach (var reward in rewards) + { + repo.MintAsset(delegatee.RewardPoolAddress, reward); + } + + delegatee.CollectRewards(37); + + var delegator1RewardBeforeDelegate = repo.GetBalance(delegator1.RewardAddress, DelegationFixture.TestRewardCurrency); + Assert.Equal(DelegationFixture.TestRewardCurrency * 0, delegator1RewardBeforeDelegate); + + delegator1.Delegate(delegatee, delegatingFAV5, 40L); + + var delegator1Reward = repo.GetBalance(delegator1.RewardAddress, DelegationFixture.TestRewardCurrency); + + var expectedReward = DelegationFixture.TestRewardCurrency * 100 + + (DelegationFixture.TestRewardCurrency * 100 * 100).DivRem(200).Quotient + + (DelegationFixture.TestRewardCurrency * 100 * 100).DivRem(400).Quotient + + (DelegationFixture.TestRewardCurrency * 100 * 100).DivRem(700).Quotient + + (DelegationFixture.TestRewardCurrency * 100 * 100).DivRem(1100).Quotient + + (DelegationFixture.TestRewardCurrency * 100 * 100).DivRem(1600).Quotient; + + Assert.Equal(expectedReward.MajorUnit, delegator1Reward.MajorUnit); + } + } +} From c2d6966fad6c97c1124129ee39c3b9181e14f62b Mon Sep 17 00:00:00 2001 From: ilgyu Date: Wed, 11 Dec 2024 02:59:00 +0900 Subject: [PATCH 116/136] chore: Redefine RewardBase.Margin as a constant --- Lib9c/Delegation/RewardBase.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Lib9c/Delegation/RewardBase.cs b/Lib9c/Delegation/RewardBase.cs index 0eb7bf54c4..515524f31b 100644 --- a/Lib9c/Delegation/RewardBase.cs +++ b/Lib9c/Delegation/RewardBase.cs @@ -21,6 +21,7 @@ public class RewardBase : IBencodable, IEquatable { private const string StateTypeName = "reward_base"; private const long StateVersion = 1; + public const int Margin = 2; private readonly IComparer _currencyComparer = new CurrencyComparer(); public RewardBase( @@ -140,8 +141,6 @@ private RewardBase( public int SigFig { get; private set; } - public static int Margin => 2; - public ImmutableDictionary RewardPortion { get; } public List Bencoded From 8d5f9f5b81df5eb7e0431c355b1bd237b5445a27 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Wed, 11 Dec 2024 03:00:29 +0900 Subject: [PATCH 117/136] chore: Fix Equals for RewardBase --- Lib9c/Delegation/RewardBase.cs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/Lib9c/Delegation/RewardBase.cs b/Lib9c/Delegation/RewardBase.cs index 515524f31b..3577ce79a3 100644 --- a/Lib9c/Delegation/RewardBase.cs +++ b/Lib9c/Delegation/RewardBase.cs @@ -22,7 +22,7 @@ public class RewardBase : IBencodable, IEquatable private const string StateTypeName = "reward_base"; private const long StateVersion = 1; public const int Margin = 2; - private readonly IComparer _currencyComparer = new CurrencyComparer(); + private static readonly IComparer _currencyComparer = new CurrencyComparer(); public RewardBase( Address address, @@ -79,7 +79,7 @@ public RewardBase( throw new ArgumentException("Duplicated currency in reward base."); } - RewardPortion = rewardPortion.ToImmutableDictionary(f => f.Currency, f => f); + RewardPortion = rewardPortion.ToImmutableSortedDictionary(f => f.Currency, f => f, _currencyComparer); SigFig = sigfig; StartHeight = startHeight; } @@ -106,7 +106,7 @@ public RewardBase(Address address, List bencoded) throw new ArgumentException("Duplicated currency in reward base."); } - RewardPortion = rewardPortion.ToImmutableDictionary(f => f.Currency, f => f); + RewardPortion = rewardPortion.ToImmutableSortedDictionary(f => f.Currency, f => f, _currencyComparer); SigFig = (Integer)bencoded[4]; try @@ -122,7 +122,7 @@ public RewardBase(Address address, List bencoded) private RewardBase( Address address, BigInteger totalShares, - ImmutableDictionary rewardPortion, + ImmutableSortedDictionary rewardPortion, int sigfig, long? startHeight = null) { @@ -141,7 +141,7 @@ private RewardBase( public int SigFig { get; private set; } - public ImmutableDictionary RewardPortion { get; } + public ImmutableSortedDictionary RewardPortion { get; } public List Bencoded { @@ -199,9 +199,10 @@ private static RewardBase UpdateTotalShares(RewardBase rewardBase, BigInteger to { var newSigFig = Math.Max(rewardBase.SigFig, RecommendedSigFig(totalShares)); var multiplier = Multiplier(newSigFig - rewardBase.SigFig); - var newPortion = rewardBase.RewardPortion.ToImmutableDictionary( + var newPortion = rewardBase.RewardPortion.ToImmutableSortedDictionary( kvp => kvp.Key, - kvp => kvp.Value * multiplier); + kvp => kvp.Value * multiplier, + _currencyComparer); return new RewardBase( rewardBase.Address, @@ -233,7 +234,7 @@ public bool Equals(RewardBase? other) || (other is RewardBase rewardBase && Address == rewardBase.Address && TotalShares == rewardBase.TotalShares - && RewardPortion.Equals(rewardBase.RewardPortion) + && RewardPortion.SequenceEqual(rewardBase.RewardPortion) && SigFig == rewardBase.SigFig); public override int GetHashCode() From 0d10efade9051b7a8c424a12b640e18db9e49bc0 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Wed, 11 Dec 2024 14:01:19 +0900 Subject: [PATCH 118/136] fix: Fix Migration to skip set RewardBase when not needed --- Lib9c/Delegation/Delegatee.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Lib9c/Delegation/Delegatee.cs b/Lib9c/Delegation/Delegatee.cs index c5d194e178..1105887610 100644 --- a/Lib9c/Delegation/Delegatee.cs +++ b/Lib9c/Delegation/Delegatee.cs @@ -571,7 +571,10 @@ record = Repository.GetLumpSumRewardsRecord(this, lastStartHeight) Repository.RemoveLumpSumRewardsRecord(recordEach); } - Repository.SetRewardBase(rewardBase!); + if (rewardBase is RewardBase rewardBaseToSet) + { + Repository.SetRewardBase(rewardBaseToSet); + } } } } From 77605bf7875ed58f2ccdef3df01797aee2b98442 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Wed, 11 Dec 2024 23:32:12 +0900 Subject: [PATCH 119/136] doc: Add docstrings for RewardBase --- Lib9c/Delegation/Delegatee.cs | 4 +- Lib9c/Delegation/RewardBase.cs | 262 ++++++++++++++++++++++++++------- 2 files changed, 209 insertions(+), 57 deletions(-) diff --git a/Lib9c/Delegation/Delegatee.cs b/Lib9c/Delegation/Delegatee.cs index 1105887610..29c589c722 100644 --- a/Lib9c/Delegation/Delegatee.cs +++ b/Lib9c/Delegation/Delegatee.cs @@ -285,7 +285,7 @@ public void CollectRewards(long height) var rewards = RewardCurrencies.Select(c => Repository.GetBalance(RewardPoolAddress, c)); if (Repository.GetCurrentRewardBase(this) is RewardBase rewardBase) { - rewardBase = rewards.Aggregate(rewardBase, (accum, next) => accum.AddReward(next)); + rewardBase = rewardBase.AddRewards(rewards); foreach (var rewardsEach in rewards) { @@ -562,9 +562,9 @@ record = Repository.GetLumpSumRewardsRecord(this, lastStartHeight) rewardBase = newRewardBase; } + rewardBase = rewardBase.AddRewards(recordEach.LumpSumRewards.Values); foreach (var r in recordEach.LumpSumRewards) { - rewardBase = rewardBase.AddReward(r.Value); Repository.TransferAsset(recordEach.Address, DistributionPoolAddress(), Repository.GetBalance(recordEach.Address, r.Key)); } diff --git a/Lib9c/Delegation/RewardBase.cs b/Lib9c/Delegation/RewardBase.cs index 3577ce79a3..f6b19d08a2 100644 --- a/Lib9c/Delegation/RewardBase.cs +++ b/Lib9c/Delegation/RewardBase.cs @@ -21,70 +21,71 @@ public class RewardBase : IBencodable, IEquatable { private const string StateTypeName = "reward_base"; private const long StateVersion = 1; + + /// + /// Margin for significant figure. It's used to calculate the significant figure of the reward base. + /// public const int Margin = 2; private static readonly IComparer _currencyComparer = new CurrencyComparer(); + /// + /// Constructor for new . + /// This constructor is used only for the initial reward base creation. + /// + /// + /// of . + /// + /// + /// of 's creation height. + /// + /// + /// of 's creation height. + /// It initializes the reward portion with 0. + /// public RewardBase( Address address, BigInteger totalShares, - IEnumerable currencies, - long? startHeight = null) + IEnumerable currencies) : this( address, totalShares, currencies.Select(c => c * 0), RecommendedSigFig(totalShares), - startHeight) - { - } - - public RewardBase( - Address address, - BigInteger totalShares, - IEnumerable currencies, - int sigFig, - long? startHeight = null) - : this( - address, - totalShares, - currencies.Select(c => c * 0), - sigFig, - startHeight) + null) { } + /// + /// Constructor for new from bencoded data. + /// + /// + /// of . + /// + /// + /// Bencoded data of . + /// public RewardBase(Address address, IValue bencoded) : this(address, (List)bencoded) { } - public RewardBase( - Address address, - BigInteger totalShares, - IEnumerable rewardPortion, - int sigfig, - long? startHeight = null) - { - Address = address; - - if (totalShares.Sign <= 0) - { - throw new ArgumentOutOfRangeException(nameof(totalShares)); - } - - TotalShares = totalShares; - - if (!rewardPortion.Select(f => f.Currency).All(new HashSet().Add)) - { - throw new ArgumentException("Duplicated currency in reward base."); - } - - RewardPortion = rewardPortion.ToImmutableSortedDictionary(f => f.Currency, f => f, _currencyComparer); - SigFig = sigfig; - StartHeight = startHeight; - } - - + /// + /// Constructor for new from bencoded data. + /// + /// + /// of . + /// + /// + /// Bencoded data of . + /// + /// Thrown when the bencoded data is not valid format for . + /// + /// + /// Thrown when the of the bencoded data is higher than the current version. + /// + /// + /// Thrown when the bencoded data has duplicated currency. + /// public RewardBase(Address address, List bencoded) { if (bencoded[0] is not Text text || text != StateTypeName || bencoded[1] is not Integer integer) @@ -119,6 +120,74 @@ public RewardBase(Address address, List bencoded) } } + /// + /// Constructor for new . + /// + /// + /// of . + /// + /// + /// of 's creation height. + /// + /// + /// Cumulative reward portion of 's creation height. + /// + /// + /// Significant figure of . + /// + /// + /// Start height of that attached when archived. + /// + /// + /// Thrown when the is less than or equal to 0. + /// + /// + /// Thrown when the has duplicated currency. + /// + private RewardBase( + Address address, + BigInteger totalShares, + IEnumerable rewardPortion, + int sigfig, + long? startHeight = null) + { + Address = address; + + if (totalShares.Sign <= 0) + { + throw new ArgumentOutOfRangeException(nameof(totalShares)); + } + + TotalShares = totalShares; + + if (!rewardPortion.Select(f => f.Currency).All(new HashSet().Add)) + { + throw new ArgumentException("Duplicated currency in reward base."); + } + + RewardPortion = rewardPortion.ToImmutableSortedDictionary(f => f.Currency, f => f, _currencyComparer); + SigFig = sigfig; + StartHeight = startHeight; + } + + /// + /// Constructor for new . + /// + /// + /// of . + /// + /// + /// of 's creation height. + /// + /// + /// Cumulative reward portion of 's creation height. + /// + /// + /// Significant figure of . + /// + /// + /// Start height of that attached when archived. + /// private RewardBase( Address address, BigInteger totalShares, @@ -133,14 +202,30 @@ private RewardBase( StartHeight = startHeight; } + /// + /// of . + /// public Address Address { get; } + /// + /// Start height of that attached when archived. + /// public long? StartHeight { get; } + /// + /// of 's creation height. + /// public BigInteger TotalShares { get; } + /// + /// Significant figure of . + /// public int SigFig { get; private set; } + /// + /// Cumulative reward portion of . + /// When it's multiplied by the number of shares, it will be the reward for the period. + /// public ImmutableSortedDictionary RewardPortion { get; } public List Bencoded @@ -164,15 +249,57 @@ public List Bencoded IValue IBencodable.Bencoded => Bencoded; + /// + /// Add rewards to the . + /// + /// + /// Rewards to add. + /// + /// + /// New with added rewards. + /// public RewardBase AddRewards(IEnumerable rewards) => rewards.Aggregate(this, (accum, next) => AddReward(accum, next)); + /// + /// Add reward to the . + /// + /// + /// Reward to add. + /// + /// + /// New with added reward. + /// public RewardBase AddReward(FungibleAssetValue reward) => AddReward(this, reward); + /// + /// Update the total shares of the . + /// + /// + /// New of the height that created. + /// + /// + /// New with updated total shares. + /// public RewardBase UpdateTotalShares(BigInteger totalShares) => UpdateTotalShares(this, totalShares); + /// + /// Attach the start height to the to be archived. + /// + /// + /// of . + /// + /// + /// Start height of that attached when archived. + /// + /// + /// New with attached start height. + /// + /// + /// Thrown when the start height is already attached. + /// public RewardBase AttachHeight(Address address, long startHeight) => StartHeight is null ? new RewardBase( @@ -183,6 +310,39 @@ public RewardBase AttachHeight(Address address, long startHeight) startHeight) : throw new InvalidOperationException("StartHeight is already attached."); + /// + /// Calculate the cumulative reward during the period. + /// + /// + /// The number of shares to calculate the reward. + /// + /// + /// Cumulative reward during the period. + /// + public ImmutableSortedDictionary CumulativeRewardDuringPeriod(BigInteger share) + => RewardPortion.Keys.Select(k => CumulativeRewardDuringPeriod(share, k)) + .ToImmutableSortedDictionary(f => f.Currency, f => f, _currencyComparer); + + /// + /// Calculate the cumulative reward during the period, for the specific currency. + /// + /// + /// The number of shares to calculate the reward. + /// + /// + /// The currency to calculate the reward. + /// + /// + /// Cumulative reward during the period, for the specific currency. + /// + /// + /// Thrown when the is not in the . + /// + public FungibleAssetValue CumulativeRewardDuringPeriod(BigInteger share, Currency currency) + => RewardPortion.TryGetValue(currency, out var portion) + ? (portion * share).DivRem(Multiplier(SigFig)).Quotient + : throw new ArgumentException($"Invalid reward currency: {currency}"); + private static RewardBase AddReward(RewardBase rewardBase, FungibleAssetValue reward) => new RewardBase( rewardBase.Address, @@ -211,20 +371,12 @@ private static RewardBase UpdateTotalShares(RewardBase rewardBase, BigInteger to newSigFig); } - public static int RecommendedSigFig(BigInteger totalShares) + private static int RecommendedSigFig(BigInteger totalShares) => (int)Math.Floor(BigInteger.Log10(totalShares)) + Margin; - public static BigInteger Multiplier(int sigFig) + private static BigInteger Multiplier(int sigFig) => BigInteger.Pow(10, sigFig); - public ImmutableSortedDictionary CumulativeRewardDuringPeriod(BigInteger share) - => RewardPortion.Keys.Select(k => CumulativeRewardDuringPeriod(share, k)) - .ToImmutableSortedDictionary(f => f.Currency, f => f, _currencyComparer); - - public FungibleAssetValue CumulativeRewardDuringPeriod(BigInteger share, Currency currency) - => RewardPortion.TryGetValue(currency, out var portion) - ? (portion * share).DivRem(Multiplier(SigFig)).Quotient - : throw new ArgumentException($"Invalid reward currency: {currency}"); public override bool Equals(object? obj) => obj is RewardBase other && Equals(other); From 107f1ada6abf828213c4c49d6d77240ab7037a9d Mon Sep 17 00:00:00 2001 From: ilgyu Date: Thu, 12 Dec 2024 15:56:31 +0900 Subject: [PATCH 120/136] fix: Fix bug for zero-transfer --- Lib9c/Delegation/Delegatee.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Lib9c/Delegation/Delegatee.cs b/Lib9c/Delegation/Delegatee.cs index 29c589c722..124f951268 100644 --- a/Lib9c/Delegation/Delegatee.cs +++ b/Lib9c/Delegation/Delegatee.cs @@ -565,7 +565,11 @@ record = Repository.GetLumpSumRewardsRecord(this, lastStartHeight) rewardBase = rewardBase.AddRewards(recordEach.LumpSumRewards.Values); foreach (var r in recordEach.LumpSumRewards) { - Repository.TransferAsset(recordEach.Address, DistributionPoolAddress(), Repository.GetBalance(recordEach.Address, r.Key)); + var toTransfer = Repository.GetBalance(recordEach.Address, r.Key); + if (toTransfer.Sign > 0) + { + Repository.TransferAsset(recordEach.Address, DistributionPoolAddress(), toTransfer); + } } Repository.RemoveLumpSumRewardsRecord(recordEach); From 4f8a3b76b3be7891785aabfe376155839e4724f9 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Thu, 12 Dec 2024 18:01:22 +0900 Subject: [PATCH 121/136] chore: Apply reviews for better structure. Co-authored-by: Jeesu Choi --- Lib9c/Delegation/Delegatee.cs | 21 +++++++++++++------ Lib9c/Delegation/RewardBase.cs | 37 ++++++++++++++++++++-------------- 2 files changed, 37 insertions(+), 21 deletions(-) diff --git a/Lib9c/Delegation/Delegatee.cs b/Lib9c/Delegation/Delegatee.cs index 124f951268..1aa75e5211 100644 --- a/Lib9c/Delegation/Delegatee.cs +++ b/Lib9c/Delegation/Delegatee.cs @@ -483,11 +483,12 @@ private void TransferReward( RewardBase? lastRewardBase) { var currentCumulative = currentRewardBase.CumulativeRewardDuringPeriod(share); - var lastCumulative = lastRewardBase?.CumulativeRewardDuringPeriod(share); + var lastCumulative = lastRewardBase?.CumulativeRewardDuringPeriod(share) + ?? ImmutableSortedDictionary.Empty; foreach (var c in currentCumulative) { - var lastCumulativeEach = lastCumulative?[c.Key] ?? c.Key * 0; + var lastCumulativeEach = lastCumulative.GetValueOrDefault(c.Key, defaultValue: c.Key * 0); if (c.Value < lastCumulativeEach) { @@ -518,7 +519,9 @@ private void TransferRemainders(LumpSumRewardsRecord record) private void MigrateLumpSumRewardsRecords() { - List records = new(); + var growSize = 100; + var capacity = 5000; + List records = new(capacity); if (!(Repository.GetCurrentLumpSumRewardsRecord(this) is LumpSumRewardsRecord record)) { return; @@ -526,18 +529,24 @@ private void MigrateLumpSumRewardsRecords() while (record.LastStartHeight is long lastStartHeight) { + if (records.Count == capacity) + { + capacity += growSize; + records.Capacity = capacity; + } + records.Add(record); record = Repository.GetLumpSumRewardsRecord(this, lastStartHeight) ?? throw new InvalidOperationException( $"Lump sum rewards record for #{lastStartHeight} is missing"); } - records.Reverse(); - RewardBase? rewardBase = null; RewardBase? newRewardBase = null; - foreach (var recordEach in records) + for (var i = records.Count - 1; i >= 0; i--) { + var recordEach = records[i]; + if (rewardBase is null) { rewardBase = new RewardBase( diff --git a/Lib9c/Delegation/RewardBase.cs b/Lib9c/Delegation/RewardBase.cs index f6b19d08a2..36bad23bd2 100644 --- a/Lib9c/Delegation/RewardBase.cs +++ b/Lib9c/Delegation/RewardBase.cs @@ -208,14 +208,15 @@ private RewardBase( public Address Address { get; } /// - /// Start height of that attached when archived. + /// of 's creation height. /// - public long? StartHeight { get; } + public BigInteger TotalShares { get; } /// - /// of 's creation height. + /// Cumulative reward portion of . + /// When it's multiplied by the number of shares, it will be the reward for the period. /// - public BigInteger TotalShares { get; } + public ImmutableSortedDictionary RewardPortion { get; } /// /// Significant figure of . @@ -223,10 +224,9 @@ private RewardBase( public int SigFig { get; private set; } /// - /// Cumulative reward portion of . - /// When it's multiplied by the number of shares, it will be the reward for the period. + /// Start height of that attached when archived. /// - public ImmutableSortedDictionary RewardPortion { get; } + public long? StartHeight { get; } public List Bencoded { @@ -344,16 +344,23 @@ public FungibleAssetValue CumulativeRewardDuringPeriod(BigInteger share, Currenc : throw new ArgumentException($"Invalid reward currency: {currency}"); private static RewardBase AddReward(RewardBase rewardBase, FungibleAssetValue reward) - => new RewardBase( + { + if (!rewardBase.RewardPortion.TryGetValue(reward.Currency, out var portion)) + { + throw new ArgumentException( + $"Invalid reward currency: {reward.Currency}", nameof(reward)); + } + + var portionNumerator = reward * Multiplier(rewardBase.SigFig); + var updatedPortion = portion + portionNumerator.DivRem(rewardBase.TotalShares).Quotient; + + return new RewardBase( rewardBase.Address, rewardBase.TotalShares, - rewardBase.RewardPortion.TryGetValue(reward.Currency, out var portion) - ? rewardBase.RewardPortion.SetItem( - reward.Currency, - portion + (reward * Multiplier(rewardBase.SigFig)).DivRem(rewardBase.TotalShares).Quotient) - : throw new ArgumentException($"Invalid reward currency: {reward.Currency}"), + rewardBase.RewardPortion.SetItem(reward.Currency, updatedPortion), rewardBase.SigFig, rewardBase.StartHeight); + } private static RewardBase UpdateTotalShares(RewardBase rewardBase, BigInteger totalShares) { @@ -377,7 +384,6 @@ private static int RecommendedSigFig(BigInteger totalShares) private static BigInteger Multiplier(int sigFig) => BigInteger.Pow(10, sigFig); - public override bool Equals(object? obj) => obj is RewardBase other && Equals(other); @@ -387,7 +393,8 @@ public bool Equals(RewardBase? other) && Address == rewardBase.Address && TotalShares == rewardBase.TotalShares && RewardPortion.SequenceEqual(rewardBase.RewardPortion) - && SigFig == rewardBase.SigFig); + && SigFig == rewardBase.SigFig + && StartHeight == rewardBase.StartHeight); public override int GetHashCode() => Address.GetHashCode(); From 8b5e42cc351a9a95ccea58d94dd611733dc2719d Mon Sep 17 00:00:00 2001 From: ilgyu Date: Thu, 12 Dec 2024 18:26:57 +0900 Subject: [PATCH 122/136] chore: Fix RewardBase to use BigInteger instead of FAV --- Lib9c/Delegation/RewardBase.cs | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/Lib9c/Delegation/RewardBase.cs b/Lib9c/Delegation/RewardBase.cs index 36bad23bd2..f219168f38 100644 --- a/Lib9c/Delegation/RewardBase.cs +++ b/Lib9c/Delegation/RewardBase.cs @@ -49,7 +49,7 @@ public RewardBase( : this( address, totalShares, - currencies.Select(c => c * 0), + currencies.Select(c => (c, BigInteger.Zero)), RecommendedSigFig(totalShares), null) { @@ -100,14 +100,16 @@ public RewardBase(Address address, List bencoded) Address = address; TotalShares = (Integer)bencoded[2]; - var rewardPortion = ((List)bencoded[3]).Select(v => new FungibleAssetValue(v)); + var bencodedRewardPortion = ((List)bencoded[3]).Select(v => (List)v); + var rewardPortion = bencodedRewardPortion.Select( + p => (Currency: new Currency(p[0]), Portion: (BigInteger)(Integer)p[1])); if (!rewardPortion.Select(f => f.Currency).All(new HashSet().Add)) { throw new ArgumentException("Duplicated currency in reward base."); } - RewardPortion = rewardPortion.ToImmutableSortedDictionary(f => f.Currency, f => f, _currencyComparer); + RewardPortion = rewardPortion.ToImmutableSortedDictionary(f => f.Currency, f => f.Portion, _currencyComparer); SigFig = (Integer)bencoded[4]; try @@ -147,7 +149,7 @@ public RewardBase(Address address, List bencoded) private RewardBase( Address address, BigInteger totalShares, - IEnumerable rewardPortion, + IEnumerable<(Currency, BigInteger)> rewardPortion, int sigfig, long? startHeight = null) { @@ -160,12 +162,12 @@ private RewardBase( TotalShares = totalShares; - if (!rewardPortion.Select(f => f.Currency).All(new HashSet().Add)) + if (!rewardPortion.Select(f => f.Item1).All(new HashSet().Add)) { throw new ArgumentException("Duplicated currency in reward base."); } - RewardPortion = rewardPortion.ToImmutableSortedDictionary(f => f.Currency, f => f, _currencyComparer); + RewardPortion = rewardPortion.ToImmutableSortedDictionary(f => f.Item1, f => f.Item2, _currencyComparer); SigFig = sigfig; StartHeight = startHeight; } @@ -191,7 +193,7 @@ private RewardBase( private RewardBase( Address address, BigInteger totalShares, - ImmutableSortedDictionary rewardPortion, + ImmutableSortedDictionary rewardPortion, int sigfig, long? startHeight = null) { @@ -216,7 +218,7 @@ private RewardBase( /// Cumulative reward portion of . /// When it's multiplied by the number of shares, it will be the reward for the period. /// - public ImmutableSortedDictionary RewardPortion { get; } + public ImmutableSortedDictionary RewardPortion { get; } /// /// Significant figure of . @@ -238,7 +240,7 @@ public List Bencoded .Add(TotalShares) .Add(new List(RewardPortion .OrderBy(r => r.Key, _currencyComparer) - .Select(r => r.Value.Serialize()))) + .Select(r => new List(r.Key.Serialize(), new Integer(r.Value))))) .Add(SigFig); return StartHeight is long height @@ -340,7 +342,7 @@ public ImmutableSortedDictionary CumulativeRewardD /// public FungibleAssetValue CumulativeRewardDuringPeriod(BigInteger share, Currency currency) => RewardPortion.TryGetValue(currency, out var portion) - ? (portion * share).DivRem(Multiplier(SigFig)).Quotient + ? FungibleAssetValue.FromRawValue(currency, (portion * share) / (Multiplier(SigFig))) : throw new ArgumentException($"Invalid reward currency: {currency}"); private static RewardBase AddReward(RewardBase rewardBase, FungibleAssetValue reward) @@ -351,8 +353,8 @@ private static RewardBase AddReward(RewardBase rewardBase, FungibleAssetValue re $"Invalid reward currency: {reward.Currency}", nameof(reward)); } - var portionNumerator = reward * Multiplier(rewardBase.SigFig); - var updatedPortion = portion + portionNumerator.DivRem(rewardBase.TotalShares).Quotient; + var portionNumerator = reward.RawValue * Multiplier(rewardBase.SigFig); + var updatedPortion = portion + (portionNumerator / rewardBase.TotalShares); return new RewardBase( rewardBase.Address, From e58e46826d1e940a0e381364f9491a781490fa83 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Thu, 12 Dec 2024 18:46:48 +0900 Subject: [PATCH 123/136] chore: Change MaxAbstainAllowance to 9 --- Lib9c/ValidatorDelegation/AbstainHistory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib9c/ValidatorDelegation/AbstainHistory.cs b/Lib9c/ValidatorDelegation/AbstainHistory.cs index 430d2c78cd..deaedb1f12 100644 --- a/Lib9c/ValidatorDelegation/AbstainHistory.cs +++ b/Lib9c/ValidatorDelegation/AbstainHistory.cs @@ -42,7 +42,7 @@ public AbstainHistory(List bencoded) public static int WindowSize => 10; - public static int MaxAbstainAllowance => 3; + public static int MaxAbstainAllowance => 9; public static Address Address => new Address( ImmutableArray.Create( From e64a0bfec4b83f5be981cc6d8b5459614f766f15 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Thu, 12 Dec 2024 19:14:55 +0900 Subject: [PATCH 124/136] fix: ReleaseUnbondings to set unbondings properly --- .../Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs b/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs index c68def6be6..b8863034bd 100644 --- a/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs +++ b/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs @@ -51,13 +51,13 @@ public override IWorld Execute(IActionContext context) switch (unbonding) { case UnbondLockIn unbondLockIn: - unbondLockIn.Release(context.BlockIndex, out var releasedFAV); + unbondLockIn = unbondLockIn.Release(context.BlockIndex, out var releasedFAV); repository.SetUnbondLockIn(unbondLockIn); repository.UpdateWorld( Unstake(repository.World, context, unbondLockIn, releasedFAV)); break; case RebondGrace rebondGrace: - rebondGrace.Release(context.BlockIndex, out _); + rebondGrace = rebondGrace.Release(context.BlockIndex, out _); repository.SetRebondGrace(rebondGrace); break; default: From d60a473f6add61dc5104a2eb2741d004a41f1c63 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Thu, 12 Dec 2024 21:39:33 +0900 Subject: [PATCH 125/136] chore: Rename parameter name as camelCase --- Lib9c/Delegation/RewardBase.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Lib9c/Delegation/RewardBase.cs b/Lib9c/Delegation/RewardBase.cs index f219168f38..18102d0c26 100644 --- a/Lib9c/Delegation/RewardBase.cs +++ b/Lib9c/Delegation/RewardBase.cs @@ -134,7 +134,7 @@ public RewardBase(Address address, List bencoded) /// /// Cumulative reward portion of 's creation height. /// - /// + /// /// Significant figure of . /// /// @@ -150,7 +150,7 @@ private RewardBase( Address address, BigInteger totalShares, IEnumerable<(Currency, BigInteger)> rewardPortion, - int sigfig, + int sigFig, long? startHeight = null) { Address = address; @@ -168,7 +168,7 @@ private RewardBase( } RewardPortion = rewardPortion.ToImmutableSortedDictionary(f => f.Item1, f => f.Item2, _currencyComparer); - SigFig = sigfig; + SigFig = sigFig; StartHeight = startHeight; } @@ -184,7 +184,7 @@ private RewardBase( /// /// Cumulative reward portion of 's creation height. /// - /// + /// /// Significant figure of . /// /// @@ -194,13 +194,13 @@ private RewardBase( Address address, BigInteger totalShares, ImmutableSortedDictionary rewardPortion, - int sigfig, + int sigFig, long? startHeight = null) { Address = address; TotalShares = totalShares; RewardPortion = rewardPortion; - SigFig = sigfig; + SigFig = sigFig; StartHeight = startHeight; } From 86b8103d547955aaaf149db28559c353babaa6c4 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Thu, 12 Dec 2024 21:40:14 +0900 Subject: [PATCH 126/136] test: Fix reward calculation test method --- .../ValidatorDelegation/ValidatorDelegationTestBase.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs index 8105c0839f..e1b05ca90c 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs @@ -25,6 +25,7 @@ namespace Lib9c.Tests.Action.ValidatorDelegation; using Nekoyume.ValidatorDelegation; using Xunit; using Nekoyume.Action.Guild.Migration.LegacyModels; +using Nekoyume.Delegation; public class ValidatorDelegationTestBase { @@ -592,7 +593,10 @@ protected static FungibleAssetValue CalculateBonusPropserReward( } protected static FungibleAssetValue CalculateClaim(BigInteger share, BigInteger totalShare, FungibleAssetValue totalClaim) - => (totalClaim * share).DivRem(totalShare).Quotient; + { + var multiplier = BigInteger.Pow(10, (int)Math.Floor(BigInteger.Log10(totalShare)) + RewardBase.Margin); + return ((totalClaim * multiplier).DivRem(totalShare).Quotient * share).DivRem(multiplier).Quotient; + } protected static FungibleAssetValue CalculateCommunityFund(ImmutableArray votes, FungibleAssetValue reward) { From e0f4cb08980e86f62b0e3cc1e60891b8cb111dc5 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 13 Dec 2024 09:57:48 +0900 Subject: [PATCH 127/136] fix: Fix release unbondings to update unbonding list --- .../ValidatorDelegation/ReleaseValidatorUnbondings.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs b/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs index b8863034bd..29ca2abd83 100644 --- a/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs +++ b/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs @@ -46,7 +46,7 @@ public override IWorld Execute(IActionContext context) var unbondingSet = repository.GetUnbondingSet(); var unbondings = unbondingSet.UnbondingsToRelease(context.BlockIndex); - foreach (var unbonding in unbondings) + unbondings = unbondings.Select(unbonding => { switch (unbonding) { @@ -55,15 +55,15 @@ public override IWorld Execute(IActionContext context) repository.SetUnbondLockIn(unbondLockIn); repository.UpdateWorld( Unstake(repository.World, context, unbondLockIn, releasedFAV)); - break; + return unbondLockIn; case RebondGrace rebondGrace: rebondGrace = rebondGrace.Release(context.BlockIndex, out _); repository.SetRebondGrace(rebondGrace); - break; + return rebondGrace; default: throw new InvalidOperationException("Invalid unbonding type."); } - } + }).ToImmutableArray(); repository.SetUnbondingSet(unbondingSet.SetUnbondings(unbondings)); From 815fa51377a33450d407ad1297b74d1dd058ea0b Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 13 Dec 2024 11:44:34 +0900 Subject: [PATCH 128/136] refactor: Refactor RewardBase not to store TotalShares --- Lib9c/Delegation/Delegatee.cs | 9 ++-- Lib9c/Delegation/RewardBase.cs | 84 +++++++++++++++++----------------- 2 files changed, 47 insertions(+), 46 deletions(-) diff --git a/Lib9c/Delegation/Delegatee.cs b/Lib9c/Delegation/Delegatee.cs index 1aa75e5211..6590f68b8e 100644 --- a/Lib9c/Delegation/Delegatee.cs +++ b/Lib9c/Delegation/Delegatee.cs @@ -285,7 +285,7 @@ public void CollectRewards(long height) var rewards = RewardCurrencies.Select(c => Repository.GetBalance(RewardPoolAddress, c)); if (Repository.GetCurrentRewardBase(this) is RewardBase rewardBase) { - rewardBase = rewardBase.AddRewards(rewards); + rewardBase = rewardBase.AddRewards(rewards, TotalShares); foreach (var rewardsEach in rewards) { @@ -411,7 +411,7 @@ public void StartNewRewardPeriod(long height) RewardBase newRewardBase; if (Repository.GetCurrentRewardBase(this) is RewardBase rewardBase) { - newRewardBase = rewardBase.UpdateTotalShares(TotalShares); + newRewardBase = rewardBase.UpdateSigFig(TotalShares); if (Repository.GetRewardBase(this, height) is not null) { Repository.SetRewardBase(newRewardBase); @@ -542,7 +542,6 @@ record = Repository.GetLumpSumRewardsRecord(this, lastStartHeight) } RewardBase? rewardBase = null; - RewardBase? newRewardBase = null; for (var i = records.Count - 1; i >= 0; i--) { var recordEach = records[i]; @@ -556,7 +555,7 @@ record = Repository.GetLumpSumRewardsRecord(this, lastStartHeight) } else { - newRewardBase = rewardBase.UpdateTotalShares(recordEach.TotalShares); + var newRewardBase = rewardBase.UpdateSigFig(recordEach.TotalShares); if (Repository.GetRewardBase(this, recordEach.StartHeight) is not null) { Repository.SetRewardBase(newRewardBase); @@ -571,7 +570,7 @@ record = Repository.GetLumpSumRewardsRecord(this, lastStartHeight) rewardBase = newRewardBase; } - rewardBase = rewardBase.AddRewards(recordEach.LumpSumRewards.Values); + rewardBase = rewardBase.AddRewards(recordEach.LumpSumRewards.Values, recordEach.TotalShares); foreach (var r in recordEach.LumpSumRewards) { var toTransfer = Repository.GetBalance(recordEach.Address, r.Key); diff --git a/Lib9c/Delegation/RewardBase.cs b/Lib9c/Delegation/RewardBase.cs index 18102d0c26..7b1eb491ac 100644 --- a/Lib9c/Delegation/RewardBase.cs +++ b/Lib9c/Delegation/RewardBase.cs @@ -48,7 +48,6 @@ public RewardBase( IEnumerable currencies) : this( address, - totalShares, currencies.Select(c => (c, BigInteger.Zero)), RecommendedSigFig(totalShares), null) @@ -99,8 +98,7 @@ public RewardBase(Address address, List bencoded) } Address = address; - TotalShares = (Integer)bencoded[2]; - var bencodedRewardPortion = ((List)bencoded[3]).Select(v => (List)v); + var bencodedRewardPortion = ((List)bencoded[2]).Select(v => (List)v); var rewardPortion = bencodedRewardPortion.Select( p => (Currency: new Currency(p[0]), Portion: (BigInteger)(Integer)p[1])); @@ -110,11 +108,11 @@ public RewardBase(Address address, List bencoded) } RewardPortion = rewardPortion.ToImmutableSortedDictionary(f => f.Currency, f => f.Portion, _currencyComparer); - SigFig = (Integer)bencoded[4]; + SigFig = (Integer)bencoded[3]; try { - StartHeight = (Integer)bencoded[5]; + StartHeight = (Integer)bencoded[4]; } catch (IndexOutOfRangeException) { @@ -124,12 +122,34 @@ public RewardBase(Address address, List bencoded) /// /// Constructor for new . + /// This constructor is used only for the initial reward base creation. /// /// /// of . /// - /// - /// of 's creation height. + /// + /// of 's creation height. + /// It initializes the reward portion with 0. + /// + /// Significant figure of . + /// + private RewardBase( + Address address, + IEnumerable currencies, + int sigFig) + : this( + address, + currencies.Select(c => (c, BigInteger.Zero)), + sigFig, + null) + { + } + + /// + /// Constructor for new . + /// + /// + /// of . /// /// /// Cumulative reward portion of 's creation height. @@ -148,20 +168,12 @@ public RewardBase(Address address, List bencoded) /// private RewardBase( Address address, - BigInteger totalShares, IEnumerable<(Currency, BigInteger)> rewardPortion, int sigFig, long? startHeight = null) { Address = address; - if (totalShares.Sign <= 0) - { - throw new ArgumentOutOfRangeException(nameof(totalShares)); - } - - TotalShares = totalShares; - if (!rewardPortion.Select(f => f.Item1).All(new HashSet().Add)) { throw new ArgumentException("Duplicated currency in reward base."); @@ -178,9 +190,6 @@ private RewardBase( /// /// of . /// - /// - /// of 's creation height. - /// /// /// Cumulative reward portion of 's creation height. /// @@ -192,13 +201,11 @@ private RewardBase( /// private RewardBase( Address address, - BigInteger totalShares, ImmutableSortedDictionary rewardPortion, int sigFig, long? startHeight = null) { Address = address; - TotalShares = totalShares; RewardPortion = rewardPortion; SigFig = sigFig; StartHeight = startHeight; @@ -209,11 +216,6 @@ private RewardBase( /// public Address Address { get; } - /// - /// of 's creation height. - /// - public BigInteger TotalShares { get; } - /// /// Cumulative reward portion of . /// When it's multiplied by the number of shares, it will be the reward for the period. @@ -237,7 +239,6 @@ public List Bencoded var bencoded = List.Empty .Add(StateTypeName) .Add(StateVersion) - .Add(TotalShares) .Add(new List(RewardPortion .OrderBy(r => r.Key, _currencyComparer) .Select(r => new List(r.Key.Serialize(), new Integer(r.Value))))) @@ -257,11 +258,13 @@ public List Bencoded /// /// Rewards to add. /// + /// + /// used as denominator of the portion. /// /// New with added rewards. /// - public RewardBase AddRewards(IEnumerable rewards) - => rewards.Aggregate(this, (accum, next) => AddReward(accum, next)); + public RewardBase AddRewards(IEnumerable rewards, BigInteger totalShares) + => rewards.Aggregate(this, (accum, next) => AddReward(accum, next, totalShares)); /// /// Add reward to the . @@ -269,23 +272,26 @@ public RewardBase AddRewards(IEnumerable rewards) /// /// Reward to add. /// + /// + /// used as denominator of the portion. + /// /// /// New with added reward. /// - public RewardBase AddReward(FungibleAssetValue reward) - => AddReward(this, reward); + public RewardBase AddReward(FungibleAssetValue reward, BigInteger totalShares) + => AddReward(this, reward, totalShares); /// /// Update the total shares of the . /// /// - /// New of the height that created. + /// used as denominator of the portion. /// /// /// New with updated total shares. /// - public RewardBase UpdateTotalShares(BigInteger totalShares) - => UpdateTotalShares(this, totalShares); + public RewardBase UpdateSigFig(BigInteger totalShares) + => UpdateSigFig(this, totalShares); /// /// Attach the start height to the to be archived. @@ -306,7 +312,6 @@ public RewardBase AttachHeight(Address address, long startHeight) => StartHeight is null ? new RewardBase( address, - TotalShares, RewardPortion, SigFig, startHeight) @@ -345,7 +350,7 @@ public FungibleAssetValue CumulativeRewardDuringPeriod(BigInteger share, Currenc ? FungibleAssetValue.FromRawValue(currency, (portion * share) / (Multiplier(SigFig))) : throw new ArgumentException($"Invalid reward currency: {currency}"); - private static RewardBase AddReward(RewardBase rewardBase, FungibleAssetValue reward) + private static RewardBase AddReward(RewardBase rewardBase, FungibleAssetValue reward, BigInteger totalShares) { if (!rewardBase.RewardPortion.TryGetValue(reward.Currency, out var portion)) { @@ -354,17 +359,16 @@ private static RewardBase AddReward(RewardBase rewardBase, FungibleAssetValue re } var portionNumerator = reward.RawValue * Multiplier(rewardBase.SigFig); - var updatedPortion = portion + (portionNumerator / rewardBase.TotalShares); + var updatedPortion = portion + (portionNumerator / totalShares); return new RewardBase( rewardBase.Address, - rewardBase.TotalShares, rewardBase.RewardPortion.SetItem(reward.Currency, updatedPortion), rewardBase.SigFig, rewardBase.StartHeight); } - private static RewardBase UpdateTotalShares(RewardBase rewardBase, BigInteger totalShares) + private static RewardBase UpdateSigFig(RewardBase rewardBase, BigInteger totalShares) { var newSigFig = Math.Max(rewardBase.SigFig, RecommendedSigFig(totalShares)); var multiplier = Multiplier(newSigFig - rewardBase.SigFig); @@ -375,12 +379,11 @@ private static RewardBase UpdateTotalShares(RewardBase rewardBase, BigInteger to return new RewardBase( rewardBase.Address, - totalShares, newPortion, newSigFig); } - private static int RecommendedSigFig(BigInteger totalShares) + public static int RecommendedSigFig(BigInteger totalShares) => (int)Math.Floor(BigInteger.Log10(totalShares)) + Margin; private static BigInteger Multiplier(int sigFig) @@ -393,7 +396,6 @@ public bool Equals(RewardBase? other) => ReferenceEquals(this, other) || (other is RewardBase rewardBase && Address == rewardBase.Address - && TotalShares == rewardBase.TotalShares && RewardPortion.SequenceEqual(rewardBase.RewardPortion) && SigFig == rewardBase.SigFig && StartHeight == rewardBase.StartHeight); From 04db96dbf14f60b6491469f59016351695870b4b Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 13 Dec 2024 13:39:23 +0900 Subject: [PATCH 129/136] doc: Add documents for new features --- .../SetValidatorCommission.cs | 3 + Lib9c/Delegation/Delegatee.cs | 27 +++++++ Lib9c/Delegation/DelegateeMetadata.cs | 78 +++++++++++++++++++ Lib9c/Delegation/DelegationAddress.cs | 71 +++++++++++++++++ Lib9c/Delegation/DelegationRepository.cs | 37 +++++++++ Lib9c/Delegation/IDelegatee.cs | 15 ++++ Lib9c/Delegation/IDelegationRepository.cs | 37 +++++++++ Lib9c/ValidatorDelegation/AbstainHistory.cs | 3 + 8 files changed, 271 insertions(+) diff --git a/Lib9c/Action/ValidatorDelegation/SetValidatorCommission.cs b/Lib9c/Action/ValidatorDelegation/SetValidatorCommission.cs index a0d4b0e278..405d8a3666 100644 --- a/Lib9c/Action/ValidatorDelegation/SetValidatorCommission.cs +++ b/Lib9c/Action/ValidatorDelegation/SetValidatorCommission.cs @@ -7,6 +7,9 @@ namespace Nekoyume.Action.ValidatorDelegation { + /// + /// Set the commission percentage of the validator. + /// [ActionType(TypeIdentifier)] public sealed class SetValidatorCommission : ActionBase { diff --git a/Lib9c/Delegation/Delegatee.cs b/Lib9c/Delegation/Delegatee.cs index 6590f68b8e..8e16dedc10 100644 --- a/Lib9c/Delegation/Delegatee.cs +++ b/Lib9c/Delegation/Delegatee.cs @@ -165,12 +165,32 @@ public Address UnbondLockInAddress(Address delegatorAddress) public Address RebondGraceAddress(Address delegatorAddress) => Metadata.RebondGraceAddress(delegatorAddress); + /// + /// Get the of the distribution pool + /// where the rewards are distributed from. + /// + /// + /// of the distribution pool. + /// public Address DistributionPoolAddress() => Metadata.DistributionPoolAddress(); + /// + /// Get the of the current . + /// + /// + /// of the current . + /// public Address CurrentRewardBaseAddress() => Metadata.CurrentRewardBaseAddress(); + /// + /// Get the of the at the given height. + /// + /// + /// + /// of the at the given height. + /// public Address RewardBaseAddress(long height) => Metadata.RewardBaseAddress(height); @@ -404,6 +424,13 @@ ImmutableDictionary reward return reward; } + /// + /// Start a new reward period. + /// It generates a new and archives the current one. + /// + /// + /// The height of the block where the new reward period starts. + /// public void StartNewRewardPeriod(long height) { MigrateLumpSumRewardsRecords(); diff --git a/Lib9c/Delegation/DelegateeMetadata.cs b/Lib9c/Delegation/DelegateeMetadata.cs index a322ea03d8..c94524f100 100644 --- a/Lib9c/Delegation/DelegateeMetadata.cs +++ b/Lib9c/Delegation/DelegateeMetadata.cs @@ -20,6 +20,47 @@ public class DelegateeMetadata : IDelegateeMetadata private Address? _address; private readonly IComparer _currencyComparer = new CurrencyComparer(); + /// + /// Create a new instance of DelegateeMetadata. + /// + /// + /// The of the . + /// + /// + /// The of the account of the . + /// + /// + /// The used for delegation. + /// + /// + /// The enumerable of s used for reward. + /// + /// + /// The of the delegation pool that stores + /// delegated s. + /// + /// + /// The of the reward pool that gathers + /// rewards to be distributed. + /// + /// + /// The of the reward remainder pool to + /// sends the remainder of the rewards to. + /// + /// + /// The of the pool that sends the slashed + /// s to. + /// + /// + /// The period in blocks that the unbonded s + /// can be withdrawn. + /// + /// + /// The maximum number of entries that can be locked in for unbonding. + /// + /// + /// The maximum number of entries that can be locked in for rebonding. + /// public DelegateeMetadata( Address delegateeAddress, Address delegateeAccountAddress, @@ -340,18 +381,55 @@ public Address UnbondLockInAddress(Address delegatorAddress) public virtual Address RebondGraceAddress(Address delegatorAddress) => DelegationAddress.RebondGraceAddress(Address, delegatorAddress); + /// + /// Get the of the distribution pool + /// where the rewards are distributed from. + /// + /// + /// of the distribution pool. + /// public virtual Address DistributionPoolAddress() => DelegationAddress.DistributionPoolAddress(Address); + /// + /// Get the of the current . + /// + /// + /// of the current . + /// public virtual Address CurrentRewardBaseAddress() => DelegationAddress.CurrentRewardBaseAddress(Address); + /// + /// Get the of the at the given height. + /// + /// + /// + /// of the at the given height. + /// public virtual Address RewardBaseAddress(long height) => DelegationAddress.RewardBaseAddress(Address, height); + /// + /// Get the of the current lump sum rewards record. + /// This will be removed after the migration is done. + /// + /// + /// of the current lump sum rewards record. + /// public virtual Address CurrentLumpSumRewardsRecordAddress() => DelegationAddress.CurrentRewardBaseAddress(Address); + /// + /// Get the of the lump sum rewards record at the given height. + /// This will be removed after the migration is done. + /// + /// + /// The height of the lump sum rewards record. + /// + /// + /// of the lump sum rewards record at the given height. + /// public virtual Address LumpSumRewardsRecordAddress(long height) => DelegationAddress.RewardBaseAddress(Address, height); diff --git a/Lib9c/Delegation/DelegationAddress.cs b/Lib9c/Delegation/DelegationAddress.cs index 1e7a340b92..4a850d65dd 100644 --- a/Lib9c/Delegation/DelegationAddress.cs +++ b/Lib9c/Delegation/DelegationAddress.cs @@ -65,18 +65,54 @@ public static Address RebondGraceAddress( delegateeMetadataAddress, delegatorAddress.ByteArray); + /// + /// Get the of the current . + /// + /// + /// of the . + /// + /// + /// of the account of the . + /// + /// + /// of the current . + /// public static Address CurrentRewardBaseAddress( Address delegateeAddress, Address delegateeAccountAddress) => DeriveAddress( DelegationElementType.RewardBase, DelegateeMetadataAddress(delegateeAddress, delegateeAccountAddress)); + /// + /// Get the of the current . + /// + /// + /// of the . + /// + /// + /// of the current . + /// public static Address CurrentRewardBaseAddress( Address delegateeMetadataAddress) => DeriveAddress( DelegationElementType.RewardBase, delegateeMetadataAddress); + /// + /// Get the of the at the given height. + /// + /// + /// of the . + /// + /// + /// of the account of the . + /// + /// + /// The height of the . + /// + /// + /// of the at the given height. + /// public static Address RewardBaseAddress( Address delegateeAddress, Address delegateeAccountAddress, long height) => DeriveAddress( @@ -84,6 +120,18 @@ public static Address RewardBaseAddress( DelegateeMetadataAddress(delegateeAddress, delegateeAccountAddress), BitConverter.GetBytes(height)); + /// + /// Get the of the at the given height. + /// + /// + /// of the . + /// + /// + /// The height of the . + /// + /// + /// of the at the given height. + /// public static Address RewardBaseAddress( Address delegateeMetadataAddress, long height) => DeriveAddress( @@ -103,12 +151,35 @@ public static Address RewardPoolAddress( DelegationElementType.RewardPool, delegateeMetadataAddress); + /// + /// Get the of the distribution pool + /// where the rewards are distributed from. + /// + /// + /// of the . + /// + /// + /// of the account of the . + /// + /// + /// of the distribution pool. + /// public static Address DistributionPoolAddress( Address delegateeAddress, Address delegateeAccountAddress) => DeriveAddress( DelegationElementType.DistributionPool, DelegateeMetadataAddress(delegateeAddress, delegateeAccountAddress)); + /// + /// Get the of the distribution pool + /// where the rewards are distributed from. + /// + /// + /// of the . + /// + /// + /// of the distribution pool. + /// public static Address DistributionPoolAddress( Address delegateeMetadataAddress) => DeriveAddress( diff --git a/Lib9c/Delegation/DelegationRepository.cs b/Lib9c/Delegation/DelegationRepository.cs index d67bc2f614..26f33397b1 100644 --- a/Lib9c/Delegation/DelegationRepository.cs +++ b/Lib9c/Delegation/DelegationRepository.cs @@ -74,26 +74,59 @@ public DelegationRepository( .SetAccount(RewardBaseAccountAddress, rewardBaseAccount) .SetAccount(LumpSumRewardsRecordAccountAddress, lumpSumRewardsRecordAccount); + /// + /// of the current action. + /// public IActionContext ActionContext { get; } + /// + /// of the account. + /// public Address DelegateeAccountAddress { get; } + /// + /// of the account. + /// public Address DelegatorAccountAddress { get; } + /// + /// of the account. + /// public Address DelegateeMetadataAccountAddress { get; } + /// + /// of the account. + /// public Address DelegatorMetadataAccountAddress { get; } + /// + /// of the account. + /// public Address BondAccountAddress { get; } + /// + /// of the account. + /// public Address UnbondLockInAccountAddress { get; } + /// + /// of the account + /// public Address RebondGraceAccountAddress { get; } + /// + /// of the account. + /// public Address UnbondingSetAccountAddress { get; } + /// + /// of the account. + /// public Address RewardBaseAccountAddress { get; } + /// + /// of the account. + /// public Address LumpSumRewardsRecordAccountAddress { get; } public abstract IDelegatee GetDelegatee(Address address); @@ -170,6 +203,7 @@ public UnbondingSet GetUnbondingSet() ? new UnbondingSet(bencoded, this) : new UnbondingSet(this); + /// public RewardBase? GetCurrentRewardBase(IDelegatee delegatee) { Address address = delegatee.CurrentRewardBaseAddress(); @@ -179,6 +213,7 @@ public UnbondingSet GetUnbondingSet() : null; } + /// public RewardBase? GetRewardBase(IDelegatee delegatee, long height) { Address address = delegatee.RewardBaseAddress(height); @@ -251,6 +286,7 @@ public void SetUnbondingSet(UnbondingSet unbondingSet) : unbondingSetAccount.SetState(UnbondingSet.Address, unbondingSet.Bencoded); } + /// public void SetRewardBase(RewardBase rewardBase) { rewardBaseAccount = rewardBaseAccount.SetState(rewardBase.Address, rewardBase.Bencoded); @@ -262,6 +298,7 @@ public void SetLumpSumRewardsRecord(LumpSumRewardsRecord lumpSumRewardsRecord) lumpSumRewardsRecord.Address, lumpSumRewardsRecord.Bencoded); } + /// public void RemoveLumpSumRewardsRecord(LumpSumRewardsRecord lumpSumRewardsRecord) { lumpSumRewardsRecordAccount = lumpSumRewardsRecordAccount.RemoveState(lumpSumRewardsRecord.Address); diff --git a/Lib9c/Delegation/IDelegatee.cs b/Lib9c/Delegation/IDelegatee.cs index 54144830b1..463f9aaeb0 100644 --- a/Lib9c/Delegation/IDelegatee.cs +++ b/Lib9c/Delegation/IDelegatee.cs @@ -65,8 +65,23 @@ public interface IDelegatee Address RebondGraceAddress(Address delegatorAddress); + /// + /// Get the of the current . + /// + /// + /// The of the current . + /// Address CurrentRewardBaseAddress(); + /// + /// Get the of the at the given height. + /// + /// + /// The height of the . + /// + /// + /// The of the at the given height. + /// Address RewardBaseAddress(long height); Address CurrentLumpSumRewardsRecordAddress(); diff --git a/Lib9c/Delegation/IDelegationRepository.cs b/Lib9c/Delegation/IDelegationRepository.cs index c3520d4c41..cec2148a04 100644 --- a/Lib9c/Delegation/IDelegationRepository.cs +++ b/Lib9c/Delegation/IDelegationRepository.cs @@ -36,8 +36,31 @@ public interface IDelegationRepository UnbondingSet GetUnbondingSet(); + /// + /// Get the current of the . + /// + /// + /// The to get the current . + /// + /// + /// The current of the . + /// RewardBase? GetCurrentRewardBase(IDelegatee delegatee); + /// + /// Get the of the + /// at the given . + /// + /// + /// The to get the of. + /// + /// + /// The height to get the at. + /// + /// + /// The of the + /// at the given . + /// RewardBase? GetRewardBase(IDelegatee delegatee, long height); LumpSumRewardsRecord? GetCurrentLumpSumRewardsRecord(IDelegatee delegatee); @@ -62,10 +85,24 @@ public interface IDelegationRepository void SetUnbondingSet(UnbondingSet unbondingSet); + /// + /// Set the of the . + /// + /// + /// The to set. + /// void SetRewardBase(RewardBase rewardBase); void SetLumpSumRewardsRecord(LumpSumRewardsRecord lumpSumRewardsRecord); + /// + /// Remove the from the . + /// This is used when the is no longer needed. + /// This can be removed when the migration for is done. + /// + /// + /// The to remove. + /// void RemoveLumpSumRewardsRecord(LumpSumRewardsRecord lumpSumRewardsRecord); void TransferAsset(Address sender, Address recipient, FungibleAssetValue value); diff --git a/Lib9c/ValidatorDelegation/AbstainHistory.cs b/Lib9c/ValidatorDelegation/AbstainHistory.cs index deaedb1f12..3e67bab5b3 100644 --- a/Lib9c/ValidatorDelegation/AbstainHistory.cs +++ b/Lib9c/ValidatorDelegation/AbstainHistory.cs @@ -42,6 +42,9 @@ public AbstainHistory(List bencoded) public static int WindowSize => 10; + /// + /// Maximum abstain allowance to slash and jail. + /// public static int MaxAbstainAllowance => 9; public static Address Address => new Address( From 373d8037f103ba26f497bb169ba3b3fa717aa2c8 Mon Sep 17 00:00:00 2001 From: Chanhyuck Ko Date: Fri, 13 Dec 2024 15:41:37 +0900 Subject: [PATCH 130/136] bump libplanet to 5.4.2 --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index f18e8496d7..7a455b0c82 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 5.4.1 + 5.4.2 From 2c67f64b372272201a726fa4bd9c4fc13d3f3163 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 13 Dec 2024 18:31:18 +0900 Subject: [PATCH 131/136] chore: Fix typo --- Lib9c/Delegation/DelegateeMetadata.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib9c/Delegation/DelegateeMetadata.cs b/Lib9c/Delegation/DelegateeMetadata.cs index c94524f100..29dd536f07 100644 --- a/Lib9c/Delegation/DelegateeMetadata.cs +++ b/Lib9c/Delegation/DelegateeMetadata.cs @@ -108,7 +108,7 @@ public DelegateeMetadata( List bencoded) { Currency delegationCurrency; - IEnumerable< Currency > rewardCurrencies; + IEnumerable rewardCurrencies; Address delegationPoolAddress; Address rewardPoolAddress; Address rewardRemainderPoolAddress; From b8afec2f70a331cdd2c1bc6638b50f6a4a4f0edc Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Mon, 16 Dec 2024 15:25:12 +0900 Subject: [PATCH 132/136] add InvalidMaterialItemId --- Lib9c/Action/InvalidItemIdException.cs | 21 +++++++++++++++++++++ Lib9c/Action/Synthesize.cs | 22 ++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 Lib9c/Action/InvalidItemIdException.cs diff --git a/Lib9c/Action/InvalidItemIdException.cs b/Lib9c/Action/InvalidItemIdException.cs new file mode 100644 index 0000000000..1f6818fa54 --- /dev/null +++ b/Lib9c/Action/InvalidItemIdException.cs @@ -0,0 +1,21 @@ +using System; +using System.Runtime.Serialization; + +namespace Nekoyume.Action +{ + [Serializable] + public class InvalidItemIdException : ArgumentOutOfRangeException + { + public InvalidItemIdException() + { + } + + public InvalidItemIdException(string msg) : base(msg) + { + } + + protected InvalidItemIdException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/Lib9c/Action/Synthesize.cs b/Lib9c/Action/Synthesize.cs index 1f5438af8e..19c2205f98 100644 --- a/Lib9c/Action/Synthesize.cs +++ b/Lib9c/Action/Synthesize.cs @@ -30,6 +30,8 @@ namespace Nekoyume.Action [ActionType(TypeIdentifier)] public class Synthesize : GameAction { + public static readonly int[] InvalidMaterialItemId = { 10660004, 10760009, }; + private const string TypeIdentifier = "synthesize"; private const string MaterialsKey = "m"; @@ -116,6 +118,26 @@ public override IWorld Execute(IActionContext context) addressesHex ); + // Check Invalid Item + foreach (var materialItem in materialItems) + { + switch (materialItem) + { + case Equipment equipment: + if (InvalidMaterialItemId.Contains(equipment.Id)) + { + throw new InvalidItemIdException($"{equipment.Id} is invalid item id."); + } + break; + case Costume costume: + if (InvalidMaterialItemId.Contains(costume.Id)) + { + throw new InvalidItemIdException($"{costume.Id} is invalid item id."); + } + break; + } + } + // Unequip items (if necessary) foreach (var materialItem in materialItems) { From e0abdf06b685493cd7ad93bd2483d45e89814432 Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Mon, 16 Dec 2024 18:24:24 +0900 Subject: [PATCH 133/136] add invalid items --- Lib9c/Action/Synthesize.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib9c/Action/Synthesize.cs b/Lib9c/Action/Synthesize.cs index 19c2205f98..0b22a8d542 100644 --- a/Lib9c/Action/Synthesize.cs +++ b/Lib9c/Action/Synthesize.cs @@ -30,7 +30,7 @@ namespace Nekoyume.Action [ActionType(TypeIdentifier)] public class Synthesize : GameAction { - public static readonly int[] InvalidMaterialItemId = { 10660004, 10760009, }; + public static readonly int[] InvalidMaterialItemId = { 10660004, 10760009, 40100042, 40100043, }; private const string TypeIdentifier = "synthesize"; From 7a05af627f625005890c62fca585636cd869d0dd Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Tue, 17 Dec 2024 11:20:57 +0900 Subject: [PATCH 134/136] add summary --- Lib9c/Action/InvalidItemIdException.cs | 12 ++++++++++++ Lib9c/Action/Synthesize.cs | 3 +++ 2 files changed, 15 insertions(+) diff --git a/Lib9c/Action/InvalidItemIdException.cs b/Lib9c/Action/InvalidItemIdException.cs index 1f6818fa54..b98c1989dd 100644 --- a/Lib9c/Action/InvalidItemIdException.cs +++ b/Lib9c/Action/InvalidItemIdException.cs @@ -6,14 +6,26 @@ namespace Nekoyume.Action [Serializable] public class InvalidItemIdException : ArgumentOutOfRangeException { + /// + /// Initializes a new instance of the class. + /// public InvalidItemIdException() { } + /// + /// Initializes a new instance of the class with a specified error message. + /// + /// The message that describes the error. public InvalidItemIdException(string msg) : base(msg) { } + /// + /// Initializes a new instance of the class with a specified error message + /// + /// SerializationInfo + /// StreamingContext protected InvalidItemIdException(SerializationInfo info, StreamingContext context) : base(info, context) { } diff --git a/Lib9c/Action/Synthesize.cs b/Lib9c/Action/Synthesize.cs index 0b22a8d542..5660d3ba02 100644 --- a/Lib9c/Action/Synthesize.cs +++ b/Lib9c/Action/Synthesize.cs @@ -30,6 +30,9 @@ namespace Nekoyume.Action [ActionType(TypeIdentifier)] public class Synthesize : GameAction { + /// + /// The list of invalid item ids for material. + /// public static readonly int[] InvalidMaterialItemId = { 10660004, 10760009, 40100042, 40100043, }; private const string TypeIdentifier = "synthesize"; From 498c51145ddc9a86c79a6c311282aec81318c3e1 Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Tue, 17 Dec 2024 11:31:30 +0900 Subject: [PATCH 135/136] add class summary --- Lib9c/Action/InvalidItemIdException.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Lib9c/Action/InvalidItemIdException.cs b/Lib9c/Action/InvalidItemIdException.cs index b98c1989dd..ca1e7c0b29 100644 --- a/Lib9c/Action/InvalidItemIdException.cs +++ b/Lib9c/Action/InvalidItemIdException.cs @@ -3,6 +3,9 @@ namespace Nekoyume.Action { + /// + /// Represents an exception that is thrown when an invalid item ID is used. + /// [Serializable] public class InvalidItemIdException : ArgumentOutOfRangeException { From 36bdf883f1b72228f1017cebddb1f9ef2c7345d5 Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Tue, 17 Dec 2024 14:34:37 +0900 Subject: [PATCH 136/136] remove redundant casting --- Lib9c/Action/Synthesize.cs | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/Lib9c/Action/Synthesize.cs b/Lib9c/Action/Synthesize.cs index 5660d3ba02..417acc506d 100644 --- a/Lib9c/Action/Synthesize.cs +++ b/Lib9c/Action/Synthesize.cs @@ -124,20 +124,9 @@ public override IWorld Execute(IActionContext context) // Check Invalid Item foreach (var materialItem in materialItems) { - switch (materialItem) + if (InvalidMaterialItemId.Contains(materialItem.Id)) { - case Equipment equipment: - if (InvalidMaterialItemId.Contains(equipment.Id)) - { - throw new InvalidItemIdException($"{equipment.Id} is invalid item id."); - } - break; - case Costume costume: - if (InvalidMaterialItemId.Contains(costume.Id)) - { - throw new InvalidItemIdException($"{costume.Id} is invalid item id."); - } - break; + throw new InvalidItemIdException($"{materialItem.Id} is invalid item id."); } }