Skip to content

Commit

Permalink
Add calculated stake rewards query
Browse files Browse the repository at this point in the history
  • Loading branch information
ipdae committed Jan 10, 2024
1 parent bc959d9 commit 6b31bcd
Show file tree
Hide file tree
Showing 6 changed files with 184 additions and 8 deletions.
2 changes: 1 addition & 1 deletion Lib9c
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using GraphQL.Execution;
using Lib9c;
using Libplanet.Types.Assets;
using Nekoyume.Model.Item;
using NineChronicles.Headless.GraphTypes.Abstractions;
using Xunit;
using static NineChronicles.Headless.Tests.GraphQLTestUtils;

namespace NineChronicles.Headless.Tests.GraphTypes.Abstractions
{
public class CalculatedStakeRewardsTypeTest
{
[Fact]
public async Task Query()
{
const string query = @"
{
favs {
quantity
currency
}
items {
count
fungibleItemId
}
}";
var materialItemSheet = Fixtures.TableSheetsFX.MaterialItemSheet;
var row = materialItemSheet.First;
var item = ItemFactory.CreateMaterial(row);
var fav = 1 * Currencies.Crystal;
var itemResult = new Dictionary<ItemBase, int>
{
[item] = 2
};
var favs = new List<FungibleAssetValue>
{
fav,
fav,
};
var queryResult = await ExecuteQueryAsync<CalculatedStakeRewardsType>(
query,
source: (itemResult, favs));
var data = (Dictionary<string, object>)((ExecutionNode)queryResult.Data!).ToValue()!;
Assert.NotEmpty(data);
Assert.Null(queryResult.Errors);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public async Task Query(StakeStateV2 stakeState, Address stakeStateAddress, long
source: new StakeStateType.StakeStateContext(
stakeState,
stakeStateAddress,
stakeStateAddress,
mockState,
blockIndex, new StateMemoryCache()));
var data = (Dictionary<string, object>)((ExecutionNode)queryResult.Data!).ToValue()!;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System.Collections.Generic;
using GraphQL.Types;
using Libplanet.Types.Assets;
using Nekoyume.Model.Item;
using NineChronicles.Headless.GraphTypes.States.Models.Item;

namespace NineChronicles.Headless.GraphTypes.Abstractions
{
public class CalculatedStakeRewardsType : ObjectGraphType<(Dictionary<ItemBase, int> itemResult, List<FungibleAssetValue> favs)>
{
public CalculatedStakeRewardsType()
{
Field<ListGraphType<FungibleAssetValueType>>(
"favs",
resolve: context => context.Source.favs);
Field<ListGraphType<InventoryItemType>>(
"items",
resolve: context =>
{
var result = new List<Inventory.Item>();
foreach (var pair in context.Source.itemResult)
{
var item = new Inventory.Item(pair.Key, pair.Value);
result.Add(item);
}

return result;
});
}
}
}
1 change: 1 addition & 0 deletions NineChronicles.Headless/GraphTypes/StateQuery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ public StateQuery()
return new StakeStateType.StakeStateContext(
stakeStateV2,
stakeStateAddress,
agentAddress,
ctx.AccountState,
ctx.BlockIndex,
ctx.StateMemoryCache
Expand Down
107 changes: 100 additions & 7 deletions NineChronicles.Headless/GraphTypes/States/StakeStateType.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Bencodex.Types;
using GraphQL;
using GraphQL.Types;
Expand All @@ -8,13 +9,9 @@
using Libplanet.Action.State;
using Libplanet.Crypto;
using Nekoyume.Model.State;
using NineChronicles.Headless.GraphTypes.States.Models;
using NineChronicles.Headless.GraphTypes.States.Models.World;
using NineChronicles.Headless.GraphTypes.States.Models.Item;
using NineChronicles.Headless.GraphTypes.States.Models.Mail;
using NineChronicles.Headless.GraphTypes.States.Models.Quest;
using Nekoyume.Blockchain.Policy;
using Nekoyume;
using Nekoyume.Action;
using Nekoyume.Extensions;
using Nekoyume.Model.Stake;
using Nekoyume.TableData;
using NineChronicles.Headless.GraphTypes.Abstractions;
Expand All @@ -25,15 +22,17 @@ public class StakeStateType : ObjectGraphType<StakeStateType.StakeStateContext>
{
public class StakeStateContext : StateContext
{
public StakeStateContext(StakeStateV2 stakeState, Address address, IAccountState accountState, long blockIndex, StateMemoryCache stateMemoryCache)
public StakeStateContext(StakeStateV2 stakeState, Address address, Address agentAddress, IAccountState accountState, long blockIndex, StateMemoryCache stateMemoryCache)
: base(accountState, blockIndex, stateMemoryCache)
{
StakeState = stakeState;
Address = address;
AgentAddress = agentAddress;
}

public StakeStateV2 StakeState { get; }
public Address Address { get; }
public Address AgentAddress { get; }
}

public StakeStateType()
Expand Down Expand Up @@ -98,6 +97,100 @@ public StakeStateType()
return (stakeRegularRewardSheet, stakeRegularFixedRewardSheet);
}
);
Field<NonNullGraphType<CalculatedStakeRewardsType>>(
"calculatedStakeRewards",
resolve: context =>
{
if (context.Source.StakeState.Contract is not { } contract)
{
return null;
}

IReadOnlyList<IValue?> values = context.Source.GetStates(new[]
{
Addresses.GetSheetAddress(contract.StakeRegularFixedRewardSheetTableName),
Addresses.GetSheetAddress(contract.StakeRegularRewardSheetTableName),
});

if (!(values[0] is Text fsv && values[1] is Text sv))
{
throw new ExecutionError("Could not found stake rewards sheets");
}

StakeRegularFixedRewardSheet stakeRegularFixedRewardSheet = new StakeRegularFixedRewardSheet();
StakeRegularRewardSheet stakeRegularRewardSheet = new StakeRegularRewardSheet();
stakeRegularFixedRewardSheet.Set(fsv);
stakeRegularRewardSheet.Set(sv);

var accountState = context.Source.AccountState;
var ncg = accountState.GetGoldCurrency();
var stakedNcg = accountState.GetBalance(context.Source.Address, ncg);
var stakingLevel = Math.Min(
stakeRegularRewardSheet.FindLevelByStakedAmount(
context.Source.AgentAddress,
stakedNcg),
stakeRegularRewardSheet.Keys.Max());
var itemSheet = accountState.GetItemSheet();
accountState.TryGetStakeStateV2(context.Source.AgentAddress, out var stakeStateV2);
// The first reward is given at the claimable block index.
var rewardSteps = stakeStateV2.ClaimableBlockIndex == context.Source.BlockIndex
? 1
: 1 + (int)Math.DivRem(
context.Source.BlockIndex - stakeStateV2.ClaimableBlockIndex,
stakeStateV2.Contract.RewardInterval,
out _);

var random = new Random();
var result = StakeRewardCalculator.CalculateFixedRewards(stakingLevel, random, stakeRegularFixedRewardSheet,
itemSheet, rewardSteps);
var (itemResult, favResult) = StakeRewardCalculator.CalculateRewards(ncg, stakedNcg, stakingLevel, rewardSteps,
stakeRegularRewardSheet, itemSheet, random);
foreach (var pair in itemResult)
{
var item = pair.Key;
result.TryAdd(item, 0);
result[item] += pair.Value;
}
return (result, favResult);
}
);
}

public class Random : IRandom
{
private readonly System.Random _random;

public Random(int seed = default)
{
_random = new System.Random(seed);
}

public int Seed => 0;

public int Next()
{
return _random.Next();
}

public int Next(int maxValue)
{
return _random.Next(maxValue);
}

public int Next(int minValue, int maxValue)
{
return _random.Next(minValue, maxValue);
}

public void NextBytes(byte[] buffer)
{
_random.NextBytes(buffer);
}

public double NextDouble()
{
return _random.NextDouble();
}
}
}
}

0 comments on commit 6b31bcd

Please sign in to comment.