Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: introduce several balance APIs #70

Merged
merged 16 commits into from
May 21, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 54 additions & 9 deletions Mimir/Controllers/AgentController.cs
Original file line number Diff line number Diff line change
@@ -1,32 +1,77 @@
using Lib9c;
using Libplanet.Crypto;
using Libplanet.Types.Assets;
using Microsoft.AspNetCore.Mvc;
using Mimir.Models.Agent;
using Mimir.Models.Assets;
using Mimir.Services;
using Mimir.Util;

namespace Mimir.Controllers;

[ApiController]
[Route("{network}/agent")]
[Route("{network}/agent/{address}")]
public class AgentController : ControllerBase
{
[HttpGet("{agentAddress}/avatars")]
#pragma warning disable CS0618 // Type or member is obsolete
private readonly Currency _ncg = Currency.Legacy(
"NCG",
2,
new Address("0x47D082a115c63E7b58B1532d20E631538eaFADde"));
#pragma warning restore CS0618 // Type or member is obsolete

[HttpGet("balances")]
public async Task<List<Balance>?> GetBalances(
string network,
string address,
IStateService stateService)
{
Address agentAddress;
try
{
agentAddress = new Address(address);
}
catch (ArgumentException)
{
Response.StatusCode = StatusCodes.Status400BadRequest;
Atralupus marked this conversation as resolved.
Show resolved Hide resolved
return null;
}

var stateGetter = new StateGetter(stateService);
var ncg = await stateGetter.GetBalanceAsync(agentAddress, _ncg);
var crystal = await stateGetter.GetBalanceAsync(agentAddress, Currencies.Crystal);
return new List<Balance>
{
new(_ncg, ncg),
new(Currencies.Crystal, crystal),
};
}

[HttpGet("avatars")]
public async Task<AvatarsResponse> GetAvatars(
string network,
string agentAddress,
IStateService stateService
)
string address,
IStateService stateService)
{
Address agentAddress;
try
{
agentAddress = new Address(address);
}
catch (ArgumentException)
{
Response.StatusCode = StatusCodes.Status400BadRequest;
return new AvatarsResponse([]);
}

var stateGetter = new StateGetter(stateService);
var avatars = await stateGetter.GetAvatarStatesAsync(new Address(agentAddress));
var avatars = await stateGetter.GetAvatarStatesAsync(agentAddress);
if (avatars is null)
{
Response.StatusCode = StatusCodes.Status404NotFound;
return new AvatarsResponse([]);
}

return new AvatarsResponse(
avatars.Select(e => new Avatar(e)).ToList()
);
return new AvatarsResponse(avatars.Select(e => new Avatar(e)).ToList());
}
}
9 changes: 6 additions & 3 deletions Mimir/Controllers/AvatarController.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
using System.Numerics;
using Bencodex;
using Lib9c;
using Libplanet.Common;
using Libplanet.Crypto;
using Libplanet.Types.Assets;
using Microsoft.AspNetCore.Mvc;
using Mimir.Models.Agent;
using Mimir.Models.Assets;
using Mimir.Models.Avatar;
using Mimir.Repositories;
using Mimir.Services;
Expand Down Expand Up @@ -111,10 +114,10 @@ public override void WriteJson(JsonWriter writer, object? value, JsonSerializer
return avatar;
}

Address addr;
Address avatarAddress;
try
{
addr = new Address(address);
avatarAddress = new Address(address);
}
catch (ArgumentException)
{
Expand All @@ -123,7 +126,7 @@ public override void WriteJson(JsonWriter writer, object? value, JsonSerializer
}

var stateGetter = new StateGetter(stateService);
var avatarState = await stateGetter.GetAvatarStateAsync(addr);
var avatarState = await stateGetter.GetAvatarStateAsync(avatarAddress);
if (avatarState is null)
{
Response.StatusCode = StatusCodes.Status404NotFound;
Expand Down
68 changes: 68 additions & 0 deletions Mimir/Controllers/BalanceController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using Lib9c;
using Libplanet.Crypto;
using Libplanet.Types.Assets;
using Microsoft.AspNetCore.Mvc;
using Mimir.Models.Assets;
using Mimir.Services;
using Mimir.Util;

namespace Mimir.Controllers;

[ApiController]
[Route("{network}/balances/{address}")]
public class BalanceController : ControllerBase
{
#pragma warning disable CS0618 // Type or member is obsolete
private readonly Currency _ncg = Currency.Legacy(
"NCG",
2,
new Address("0x47D082a115c63E7b58B1532d20E631538eaFADde"));
#pragma warning restore CS0618 // Type or member is obsolete

[HttpGet("{currencyTicker}")]
public async Task<Balance?> GetBalance(
string network,
string address,
string currencyTicker,
IStateService stateService)
{
Address agentAddress;
try
{
agentAddress = new Address(address);
}
catch (ArgumentException)
{
Response.StatusCode = StatusCodes.Status400BadRequest;
return null;
}

Currency? c = currencyTicker switch
{
"NCG" => _ncg,
"CRYSTAL" => Currencies.Crystal,
_ => null,
};
if (c is null)
{
try
{
c = Currencies.GetMinterlessCurrency(currencyTicker);
}
catch (ArgumentNullException)
{
Response.StatusCode = StatusCodes.Status400BadRequest;
return null;
}
catch (ArgumentException)
{
Response.StatusCode = StatusCodes.Status400BadRequest;
return null;
}
}

var stateGetter = new StateGetter(stateService);
var balance = await stateGetter.GetBalanceAsync(agentAddress, c.Value);
return new Balance(c.Value, balance);
}
}
23 changes: 23 additions & 0 deletions Mimir/Models/Assets/Balance.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using Libplanet.Types.Assets;

namespace Mimir.Models.Assets;

public class Balance
{
public Currency Currency { get; set; }
public string Quantity { get; set; }

public Balance(Currency currency, string quantity)
{
Currency = currency;
Quantity = quantity;
}

// TODO: Specify the BSON schema for the Balance class.
// TODO: Implement the following constructor.
// public Balance(BsonDocument balance)
// {
// Currency = new Currency(balance["Currency"].AsBsonDocument);
// Quantity = balance["Quantity"].AsString;
// }
}
8 changes: 8 additions & 0 deletions Mimir/Queries.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,11 @@ query GetTip {
query GetState($accountAddress: Address! $address: Address!) {
state(accountAddress: $accountAddress, address: $address)
}

query GetBalance($address: Address!, $currencyInput: CurrencyInput!) {
stateQuery {
balance(address: $address, currency: $currencyInput) {
quantity
}
}
}
23 changes: 21 additions & 2 deletions Mimir/Services/HeadlessStateService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using HeadlessGQL;
using Libplanet.Crypto;
using Libplanet.Action.State;
using Libplanet.Types.Assets;
using Libplanet.Types.Blocks;
using StrawberryShake;

Expand Down Expand Up @@ -44,8 +45,26 @@ public class HeadlessStateService(IHeadlessGQLClient client) : IStateService
return Codec.Decode(Convert.FromHexString(result.Data.State));
}

private static void UpdateTipIndex()
public async Task<string> GetBalance(Address address, Currency currency)
{

var currencyInput = new CurrencyInput
{
Ticker = currency.Ticker,
DecimalPlaces = currency.DecimalPlaces,
Minters = currency.Minters?.Select(minter => minter.ToString()).ToList() ?? null,
MaximumSupplyMajorUnit = currency.MaximumSupply?.MajorUnit.ToString() ?? null,
MaximumSupplyMinorUnit = currency.MaximumSupply?.MinorUnit.ToString() ?? null,
TotalSupplyTrackable = currency.TotalSupplyTrackable,
};
var result = await client.GetBalance.ExecuteAsync(
address.ToString(),
currencyInput);
result.EnsureNoErrors();
if (result.Data is null)
{
return "0";
}

return result.Data.StateQuery.Balance.Quantity;
}
}
3 changes: 2 additions & 1 deletion Mimir/Services/IStateService.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System.Threading.Tasks;
using Bencodex.Types;
using Libplanet.Crypto;
using Libplanet.Types.Assets;

namespace Mimir.Services;

Expand All @@ -10,4 +10,5 @@ public interface IStateService
Task<IValue?> GetState(Address address, Address accountAddress);
Task<IValue?[]> GetStates(Address[] addresses);
Task<IValue?[]> GetStates(Address[] addresses, Address accountAddress);
Task<string> GetBalance(Address address, Currency currency);
}
15 changes: 9 additions & 6 deletions Mimir/Util/StateGetter.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
using Libplanet.Crypto;
using Bencodex.Types;
using Libplanet.Action.State;
using Libplanet.Types.Assets;
using Nekoyume.TableData;
using Nekoyume;
using Nekoyume.Action;
using Nekoyume.Model.EnumType;
using Nekoyume.Model.Item;
using Nekoyume.Model.State;
using Mimir.Services;
using Nekoyume.Model.Arena;
using Nekoyume.TableData.Rune;

namespace Mimir.Util;

public class StateGetter(IStateService stateService)
{
public async Task<string> GetBalanceAsync(Address address, Currency currency) =>
await stateService.GetBalance(address, currency);

public async Task<IValue?> GetStateAsync(Address address, Address accountAddress) =>
await stateService.GetState(address, accountAddress) ??
await stateService.GetState(address);
Expand All @@ -39,7 +42,7 @@ await stateService.GetState(address, accountAddress) ??
sheet.Set(sheetValue.Value);
return sheet;
}

public async Task<List<AvatarState>?> GetAvatarStatesAsync(
Address agentAddress,
bool withInventory = true)
Expand All @@ -58,7 +61,7 @@ await stateService.GetState(address, accountAddress) ??
}

var avatars = new List<AvatarState>();
foreach(var avatarAddress in agentState.avatarAddresses.Values)
foreach (var avatarAddress in agentState.avatarAddresses.Values)
{
var avatarState = await GetAvatarStateAsync(avatarAddress, withInventory);
if (avatarState is null)
Expand Down Expand Up @@ -101,7 +104,7 @@ await stateService.GetState(address, accountAddress) ??
{
avatarState.actionPoint = actionPoint;
}

if (await GetStateAsync(avatarAddress, Addresses.DailyReward) is Integer dailyRewardReceivedIndex)
{
avatarState.dailyRewardReceivedIndex = dailyRewardReceivedIndex;
Expand Down Expand Up @@ -132,7 +135,7 @@ await stateService.GetState(address, accountAddress) ??
_ => null,
};
}

public async Task<AllRuneState> GetRuneStatesAsync(Address avatarAddress)
{
AllRuneState allRuneState;
Expand All @@ -158,7 +161,7 @@ public async Task<AllRuneState> GetRuneStatesAsync(Address avatarAddress)

return allRuneState;
}

public async Task<RuneSlotState> GetRuneSlotStateAsync(Address avatarAddress, BattleType battleType)
{
var runeSlotStateAddress = RuneSlotState.DeriveAddress(avatarAddress, battleType);
Expand Down