diff --git a/NineChronicles.Headless.Tests/GraphTypes/ActionQueryTest.cs b/NineChronicles.Headless.Tests/GraphTypes/ActionQueryTest.cs index 54b0d79c5..120983907 100644 --- a/NineChronicles.Headless.Tests/GraphTypes/ActionQueryTest.cs +++ b/NineChronicles.Headless.Tests/GraphTypes/ActionQueryTest.cs @@ -236,7 +236,7 @@ public async Task UnlockWorld() [InlineData("NCG", false)] [InlineData("CRYSTAL", true)] [InlineData("CRYSTAL", false)] - public async Task TransferAsset(string currencyType, bool memo) + public async Task TransferAssetWithCurrencyEnum(string currencyType, bool memo) { var recipient = new PrivateKey().ToAddress(); var sender = new PrivateKey().ToAddress(); @@ -270,6 +270,49 @@ public async Task TransferAsset(string currencyType, bool memo) } } + [Theory] + [InlineData("{ ticker: \"NCG\", minters: [], decimalPlaces: 2 }", true)] + [InlineData("{ ticker: \"NCG\", minters: [], decimalPlaces: 2 }", false)] + [InlineData("{ ticker: \"CRYSTAL\", minters: [], decimalPlaces: 18 }", true)] + [InlineData("{ ticker: \"CRYSTAL\", minters: [], decimalPlaces: 18 }", false)] + public async Task TransferAsset(string valueType, bool memo) + { + var rawState = _standaloneContext.BlockChain!.GetState(Addresses.GoldCurrency); + var goldCurrencyState = new GoldCurrencyState((Dictionary)rawState); + + var recipient = new PrivateKey().ToAddress(); + var sender = new PrivateKey().ToAddress(); + var valueTypeWithMinter = valueType.Replace("[]", + valueType.Contains("NCG") ? $"[\"{goldCurrencyState.Currency.Minters.First()}\"]" : "[]"); + var args = $"recipient: \"{recipient}\", sender: \"{sender}\", rawCurrency: {valueTypeWithMinter}, amount: \"17.5\""; + if (memo) + { + args += ", memo: \"memo\""; + } + + var query = $"{{ transferAsset({args}) }}"; + var queryResult = await ExecuteQueryAsync(query, standaloneContext: _standaloneContext); + var data = (Dictionary)((ExecutionNode)queryResult.Data!).ToValue()!; + var plainValue = _codec.Decode(ByteUtil.ParseHex((string)data["transferAsset"])); + Assert.IsType(plainValue); + var actionBase = DeserializeNCAction(plainValue); + var action = Assert.IsType(actionBase); + + Currency currency = valueType.Contains("NCG") ? goldCurrencyState.Currency : CrystalCalculator.CRYSTAL; + + Assert.Equal(recipient, action.Recipient); + Assert.Equal(sender, action.Sender); + Assert.Equal(FungibleAssetValue.Parse(currency, "17.5"), action.Amount); + if (memo) + { + Assert.Equal("memo", action.Memo); + } + else + { + Assert.Null(action.Memo); + } + } + [Fact] public async Task PatchTableSheet() { diff --git a/NineChronicles.Headless/GraphTypes/ActionQuery.cs b/NineChronicles.Headless/GraphTypes/ActionQuery.cs index 42a06176a..7e092c6ba 100644 --- a/NineChronicles.Headless/GraphTypes/ActionQuery.cs +++ b/NineChronicles.Headless/GraphTypes/ActionQuery.cs @@ -4,6 +4,7 @@ using System.Numerics; using Bencodex; using Bencodex.Types; +using Google.Protobuf.WellKnownTypes; using GraphQL; using GraphQL.Types; using Libplanet.Crypto; @@ -170,11 +171,16 @@ public ActionQuery(StandaloneContext standaloneContext) Description = "A string value to be transferred.", Name = "amount", }, - new QueryArgument> + new QueryArgument { - Description = "A currency type to be transferred.", + Description = "A enum value of currency to be transferred.", Name = "currency", }, + new QueryArgument + { + Description = "A currency to be transferred.", + Name = "rawCurrency", + }, new QueryArgument { Description = "A 80-max length string to note.", @@ -185,10 +191,30 @@ public ActionQuery(StandaloneContext standaloneContext) { var sender = context.GetArgument
("sender"); var recipient = context.GetArgument
("recipient"); - var currencyEnum = context.GetArgument("currency"); - if (!standaloneContext.CurrencyFactory!.TryGetCurrency(currencyEnum, out var currency)) + var nullableRawCurrency = context.GetArgument("rawCurrency"); + var nullableCurrencyEnum = context.GetArgument("currency"); + + Currency currency; + if (nullableRawCurrency is not null && nullableCurrencyEnum is not null) + { + throw new ExecutionError("Only one of currency and rawCurrency must be set."); + } + if (nullableCurrencyEnum is { } currencyEnum) + { + if (!standaloneContext.CurrencyFactory!.TryGetCurrency(currencyEnum, out var currencyFromEnum)) + { + throw new ExecutionError($"Currency {currencyEnum} is not found."); + } + + currency = currencyFromEnum; + } + else if (nullableRawCurrency is { } rawCurrency) + { + currency = rawCurrency; + } + else { - throw new ExecutionError($"Currency {currencyEnum} is not found."); + throw new ExecutionError("Either currency or rawCurrency must be set."); } var amount = FungibleAssetValue.Parse(currency, context.GetArgument("amount"));