Skip to content

Commit

Permalink
Merge pull request #2225 from planetarium/feat/revise-staking
Browse files Browse the repository at this point in the history
  • Loading branch information
moreal authored Sep 5, 2023
2 parents 89f82ec + 77760d7 commit 7b37a0e
Show file tree
Hide file tree
Showing 9 changed files with 137 additions and 43 deletions.
2 changes: 1 addition & 1 deletion Lib9c
Submodule Lib9c updated 51 files
+1 −1 .Lib9c.Tests/Action/ClaimStakeReward1Test.cs
+1 −1 .Lib9c.Tests/Action/ClaimStakeReward2Test.cs
+502 −0 .Lib9c.Tests/Action/ClaimStakeReward8Test.cs
+650 −435 .Lib9c.Tests/Action/ClaimStakeRewardTest.cs
+3 −1 .Lib9c.Tests/Action/Factory/ClaimStakeRewardFactoryTest.cs
+36 −2 .Lib9c.Tests/Action/PatchTableSheetTest.cs
+8 −8 .Lib9c.Tests/Action/Scenario/StakeAndClaimStakeReward2ScenarioTest.cs
+8 −8 .Lib9c.Tests/Action/Scenario/StakeAndClaimStakeReward3ScenarioTest.cs
+15 −14 .Lib9c.Tests/Action/Scenario/StakeAndClaimStakeRewardScenarioTest.cs
+257 −0 .Lib9c.Tests/Action/Stake2Test.cs
+472 −188 .Lib9c.Tests/Action/StakeTest.cs
+25 −0 .Lib9c.Tests/Fixtures/TableCSV/GameConfigSheetFixtures.cs
+17 −0 .Lib9c.Tests/Fixtures/TableCSV/Stake/StakePolicySheetFixtures.cs
+22 −0 .Lib9c.Tests/Fixtures/TableCSV/Stake/StakeRegularFixedRewardSheetFixtures.cs
+57 −0 .Lib9c.Tests/Fixtures/TableCSV/Stake/StakeRegularRewardSheetFixtures.cs
+1 −1 .Lib9c.Tests/Lib9c.Tests.csproj
+183 −0 .Lib9c.Tests/Model/Stake/ContractTest.cs
+154 −0 .Lib9c.Tests/Model/Stake/StakeStateUtilsTest.cs
+134 −0 .Lib9c.Tests/Model/Stake/StakeStateV2Test.cs
+118 −0 .Lib9c.Tests/Model/State/GameConfigStateTest.cs
+58 −0 .Lib9c.Tests/TableData/Stake/StakePolicySheetTest.cs
+2 −8 .Lib9c.Tests/TableData/StakeRegularFixedRewardSheetTest.cs
+54 −106 .Lib9c.Tests/TableData/StakeRegularRewardSheetTest.cs
+9 −1 .Lib9c.Tests/TableSheets.cs
+1 −1 .Lib9c.Tests/Util/InitializeUtil.cs
+3 −3 .github/workflows/main.yml
+1 −1 .github/workflows/publish.yml
+72 −17 Lib9c/Action/AccountStateExtensions.cs
+89 −328 Lib9c/Action/ClaimStakeReward.cs
+507 −0 Lib9c/Action/ClaimStakeReward8.cs
+8 −2 Lib9c/Action/Factory/ClaimStakeRewardFactory.cs
+11 −2 Lib9c/Action/PatchTableSheet.cs
+106 −40 Lib9c/Action/Stake.cs
+145 −0 Lib9c/Action/Stake2.cs
+2 −0 Lib9c/ActionObsoleteConfig.cs
+3 −1 Lib9c/Currencies.cs
+13 −0 Lib9c/Exceptions/InvalidStateTypeException.cs
+3 −1 Lib9c/Extensions/SheetsExtensions.cs
+1 −1 Lib9c/Lib9c.csproj
+157 −0 Lib9c/Model/Stake/Contract.cs
+84 −0 Lib9c/Model/Stake/StakeStateUtils.cs
+138 −0 Lib9c/Model/Stake/StakeStateV2.cs
+104 −1 Lib9c/Model/State/GameConfigState.cs
+5 −0 Lib9c/TableCSV/GameConfigSheet.csv
+8 −0 Lib9c/TableCSV/Stake.meta
+5 −0 Lib9c/TableCSV/Stake/StakePolicySheet.csv
+7 −0 Lib9c/TableCSV/Stake/StakePolicySheet.csv.meta
+3 −3 Lib9c/TableData/Sheet.cs
+94 −0 Lib9c/TableData/Stake/StakePolicySheet.cs
+3 −1 Lib9c/TableData/StakeRegularRewardSheet.cs
+1 −1 global.json
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,9 @@ public void ClaimStakeReward(string addressString, int expectedCode)
[InlineData(ClaimStakeReward6.ObsoleteBlockIndex, typeof(ClaimStakeReward6))]
[InlineData(ClaimStakeReward6.ObsoleteBlockIndex + 1, typeof(ClaimStakeReward7))]
[InlineData(ClaimStakeReward7.ObsoleteBlockIndex, typeof(ClaimStakeReward7))]
[InlineData(ClaimStakeReward7.ObsoleteBlockIndex + 1, typeof(ClaimStakeReward))]
[InlineData(ClaimStakeReward7.ObsoleteBlockIndex + 1, typeof(ClaimStakeReward8))]
[InlineData(ClaimStakeReward8.ObsoleteBlockIndex, typeof(ClaimStakeReward8))]
[InlineData(ClaimStakeReward8.ObsoleteBlockIndex + 1, typeof(ClaimStakeReward))]
[InlineData(long.MaxValue, typeof(ClaimStakeReward))]
public void ClaimStakeRewardWithBlockIndex(long blockIndex, Type expectedActionType)
{
Expand All @@ -224,8 +226,8 @@ public void ClaimStakeRewardWithBlockIndex(long blockIndex, Type expectedActionT

[Theory]
[InlineData(0, 0, -1)]
[InlineData(1, 8, 0)]
[InlineData(9, 9, -1)]
[InlineData(1, 9, 0)]
[InlineData(10, 10, -1)]
public void ClaimStakeRewardWithActionVersion(
int actionVersionMin,
int actionVersionMax,
Expand Down
1 change: 1 addition & 0 deletions NineChronicles.Headless.Executable/Commands/TxCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ public void Sign(
nameof(ClaimStakeReward4) => new ClaimStakeReward4(),
nameof(ClaimStakeReward5) => new ClaimStakeReward5(),
nameof(ClaimStakeReward7) => new ClaimStakeReward7(),
nameof(ClaimStakeReward8) => new ClaimStakeReward8(),
nameof(ClaimStakeReward) => new ClaimStakeReward(),
nameof(TransferAsset) => new TransferAsset(),
nameof(MigrateMonsterCollection) => new MigrateMonsterCollection(),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using GraphQL.Execution;
using Libplanet.Crypto;
using Libplanet.Types.Assets;
using Nekoyume;
using Nekoyume.Model.Stake;
using Nekoyume.Model.State;
using Nekoyume.TableData;
using NineChronicles.Headless.GraphTypes.States;
using NineChronicles.Headless.Tests.Common;
using Xunit;
Expand All @@ -15,7 +18,7 @@ public class StakeStateTypeTest
{
[Theory]
[MemberData(nameof(Members))]
public async Task Query(StakeState stakeState, long deposit, long blockIndex, Dictionary<string, object> expected)
public async Task Query(StakeStateV2 stakeState, Address stakeStateAddress, long deposit, long blockIndex, Dictionary<string, object> expected)
{
#pragma warning disable CS0618
// Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319
Expand All @@ -36,7 +39,12 @@ public async Task Query(StakeState stakeState, long deposit, long blockIndex, Di
claimableBlockIndex
}";
var queryResult = await ExecuteQueryAsync<StakeStateType>(
query, source: new StakeStateType.StakeStateContext(stakeState, mockState, blockIndex));
query,
source: new StakeStateType.StakeStateContext(
stakeState,
stakeStateAddress,
mockState,
blockIndex));
var data = (Dictionary<string, object>)((ExecutionNode)queryResult.Data!).ToValue()!;
Assert.Equal(expected, data);
}
Expand All @@ -45,76 +53,83 @@ public async Task Query(StakeState stakeState, long deposit, long blockIndex, Di
{
new object[]
{
new StakeState(Fixtures.StakeStateAddress, 0),
new StakeStateV2(
new Contract("StakeRegularFixedRewardSheet_V1", "StakeRegularRewardSheet_V1", 50400, 201600), 0),
Fixtures.StakeStateAddress,
100,
0,
new Dictionary<string, object>
{
["address"] = Fixtures.StakeStateAddress.ToString(),
["deposit"] = "100.00",
["startedBlockIndex"] = 0,
["startedBlockIndex"] = 0L,
["cancellableBlockIndex"] = StakeState.LockupInterval,
["receivedBlockIndex"] = 0,
["claimableBlockIndex"] = 0 + StakeState.RewardInterval,
["receivedBlockIndex"] = 0L,
["claimableBlockIndex"] = 0L + StakeState.RewardInterval,
}
},
new object[]
{
new StakeState(Fixtures.StakeStateAddress, 100),
new StakeStateV2(new Contract("StakeRegularFixedRewardSheet_V1", "StakeRegularRewardSheet_V1", 50400, 201600), 100),
Fixtures.StakeStateAddress,
100,
0,
new Dictionary<string, object>
{
["address"] = Fixtures.StakeStateAddress.ToString(),
["deposit"] = "100.00",
["startedBlockIndex"] = 100,
["startedBlockIndex"] = 100L,
["cancellableBlockIndex"] = 100 + StakeState.LockupInterval,
["receivedBlockIndex"] = 0,
["receivedBlockIndex"] = 0L,
["claimableBlockIndex"] = 100 + StakeState.RewardInterval,
}
},
new object[]
{
new StakeState(Fixtures.StakeStateAddress, 100),
new StakeStateV2(new Contract("StakeRegularFixedRewardSheet_V1", "StakeRegularRewardSheet_V1", 50400, 201600), 100),
Fixtures.StakeStateAddress,
100,
0,
new Dictionary<string, object>
{
["address"] = Fixtures.StakeStateAddress.ToString(),
["deposit"] = "100.00",
["startedBlockIndex"] = 100,
["startedBlockIndex"] = 100L,
["cancellableBlockIndex"] = StakeState.LockupInterval + 100,
["receivedBlockIndex"] = 0,
["receivedBlockIndex"] = 0L,
["claimableBlockIndex"] = StakeState.RewardInterval + 100,
}
},
new object[]
{
new StakeState(Fixtures.StakeStateAddress, 10, 50412, 201610, new StakeState.StakeAchievements()),
new StakeStateV2(
new Contract("StakeRegularFixedRewardSheet_V1", "StakeRegularRewardSheet_V1", 50400, 201600), 10, 50412),
Fixtures.StakeStateAddress,
100,
0,
new Dictionary<string, object>
{
["address"] = Fixtures.StakeStateAddress.ToString(),
["deposit"] = "100.00",
["startedBlockIndex"] = 10,
["startedBlockIndex"] = 10L,
["cancellableBlockIndex"] = 201610L,
["receivedBlockIndex"] = 50412,
["claimableBlockIndex"] = 100812L,
["receivedBlockIndex"] = 50412L,
["claimableBlockIndex"] = 100810L,
}
},
new object[]
{
new StakeState(Fixtures.StakeStateAddress, 10, 50412, 201610, new StakeState.StakeAchievements()),
new StakeStateV2(new Contract("StakeRegularFixedRewardSheet_V1", "StakeRegularRewardSheet_V1", 50400, 201600), 10, 50412),
Fixtures.StakeStateAddress,
100,
ActionObsoleteConfig.V100290ObsoleteIndex,
new Dictionary<string, object>
{
["address"] = Fixtures.StakeStateAddress.ToString(),
["deposit"] = "100.00",
["startedBlockIndex"] = 10,
["startedBlockIndex"] = 10L,
["cancellableBlockIndex"] = 201610L,
["receivedBlockIndex"] = 50412,
["receivedBlockIndex"] = 50412L,
["claimableBlockIndex"] = 100810L,
}
}
Expand Down
2 changes: 1 addition & 1 deletion NineChronicles.Headless/GraphTypes/ActionQuery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ public ActionQuery(StandaloneContext standaloneContext)
@namespace.StartsWith($"{nameof(Nekoyume)}.{nameof(Nekoyume.TableData)}") &&
!type.IsAbstract &&
typeof(ISheet).IsAssignableFrom(type) &&
type.Name == tableName);
tableName.Split('_').First() == type.Name);
}
catch (Exception)
{
Expand Down
1 change: 1 addition & 0 deletions NineChronicles.Headless/GraphTypes/CurrencyEnumType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public enum CurrencyEnum
CRYSTAL,
NCG,
GARAGE,
MEAD
}

public class CurrencyEnumType : EnumerationGraphType<CurrencyEnum>
Expand Down
48 changes: 43 additions & 5 deletions NineChronicles.Headless/GraphTypes/StateQuery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@
using Nekoyume.Extensions;
using Nekoyume.Model.Arena;
using Nekoyume.Model.Item;
using Nekoyume.Model.Stake;
using Nekoyume.Model.State;
using Nekoyume.TableData;
using Nekoyume.TableData.Crystal;
using Nekoyume.TableData.Stake;
using NineChronicles.Headless.GraphTypes.Abstractions;
using NineChronicles.Headless.GraphTypes.States;
using NineChronicles.Headless.GraphTypes.States.Models;
Expand Down Expand Up @@ -239,10 +241,12 @@ public StateQuery()

StakeStateType.StakeStateContext? GetStakeState(StateContext ctx, Address agentAddress)
{
if (ctx.GetState(StakeState.DeriveAddress(agentAddress)) is Dictionary state)
var stakeStateAddress = StakeState.DeriveAddress(agentAddress);
if (ctx.AccountState.TryGetStakeStateV2(agentAddr: agentAddress, out StakeStateV2 stakeStateV2))
{
return new StakeStateType.StakeStateContext(
new StakeState(state),
stakeStateV2,
stakeStateAddress,
ctx.AccountState,
ctx.BlockIndex
);
Expand All @@ -252,7 +256,7 @@ public StateQuery()
}

Field<StakeStateType>(
name: nameof(StakeState),
name: "stakeState",
description: "State for staking.",
arguments: new QueryArguments(new QueryArgument<NonNullGraphType<AddressType>>
{
Expand Down Expand Up @@ -338,8 +342,42 @@ public StateQuery()
}
);

Field<StakeRewardsType>(
"latestStakeRewards",
description: "The latest stake rewards based on StakePolicySheet.",
resolve: context =>
{
var stakePolicySheetStateValue = context.Source.GetState(Addresses.GetSheetAddress<StakePolicySheet>());
var stakePolicySheet = new StakePolicySheet();
if (stakePolicySheetStateValue is not Text stakePolicySheetStateText)
{
return null;
}

stakePolicySheet.Set(stakePolicySheetStateText);

IReadOnlyList<IValue?> values = context.Source.GetStates(new[]
{
Addresses.GetSheetAddress(stakePolicySheet["StakeRegularFixedRewardSheet"].Value),
Addresses.GetSheetAddress(stakePolicySheet["StakeRegularRewardSheet"].Value),
});

if (!(values[0] is Text fsv && values[1] is Text sv))
{
return null;
}

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

return (stakeRegularRewardSheet, stakeRegularFixedRewardSheet);
}
);
Field<StakeRewardsType>(
"stakeRewards",
deprecationReason: "Since stake3, claim_stake_reward9 actions, each stakers have their own contracts.",
resolve: context =>
{
StakeRegularRewardSheet stakeRegularRewardSheet;
Expand All @@ -348,9 +386,9 @@ public StateQuery()
if (context.Source.BlockIndex < StakeState.StakeRewardSheetV2Index)
{
stakeRegularRewardSheet = new StakeRegularRewardSheet();
stakeRegularRewardSheet.Set(ClaimStakeReward.V1.StakeRegularRewardSheetCsv);
stakeRegularRewardSheet.Set(ClaimStakeReward8.V1.StakeRegularRewardSheetCsv);
stakeRegularFixedRewardSheet = new StakeRegularFixedRewardSheet();
stakeRegularFixedRewardSheet.Set(ClaimStakeReward.V1.StakeRegularFixedRewardSheetCsv);
stakeRegularFixedRewardSheet.Set(ClaimStakeReward8.V1.StakeRegularFixedRewardSheetCsv);
}
else
{
Expand Down
64 changes: 50 additions & 14 deletions NineChronicles.Headless/GraphTypes/States/StakeStateType.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
using System;
using System.Collections.Generic;
using Bencodex.Types;
using GraphQL;
using GraphQL.Types;
using Libplanet.Action;
using Libplanet.Explorer.GraphTypes;
using Libplanet.Action.State;
using Libplanet.Crypto;
using Nekoyume.Model.State;
using NineChronicles.Headless.GraphTypes.States.Models;
using NineChronicles.Headless.GraphTypes.States.Models.World;
Expand All @@ -12,56 +15,89 @@
using NineChronicles.Headless.GraphTypes.States.Models.Quest;
using Nekoyume.Blockchain.Policy;
using Nekoyume;
using Nekoyume.Model.Stake;
using Nekoyume.TableData;
using NineChronicles.Headless.GraphTypes.Abstractions;

namespace NineChronicles.Headless.GraphTypes.States
{
public class StakeStateType : ObjectGraphType<StakeStateType.StakeStateContext>
{
public class StakeStateContext : StateContext
{
public StakeStateContext(StakeState stakeState, IAccountState accountState, long blockIndex)
public StakeStateContext(StakeStateV2 stakeState, Address address, IAccountState accountState, long blockIndex)
: base(accountState, blockIndex)
{
StakeState = stakeState;
Address = address;
}

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

public StakeStateType()
{
Field<NonNullGraphType<AddressType>>(
nameof(StakeState.address),
"address",
description: "The address of current state.",
resolve: context => context.Source.StakeState.address);
resolve: context => context.Source.Address);
Field<NonNullGraphType<StringGraphType>>(
"deposit",
description: "The staked amount.",
resolve: context => context.Source.AccountState.GetBalance(
context.Source.StakeState.address,
context.Source.Address,
new GoldCurrencyState((Dictionary)context.Source.GetState(GoldCurrencyState.Address)!).Currency)
.GetQuantityString(true));
Field<NonNullGraphType<IntGraphType>>(
nameof(StakeState.StartedBlockIndex),
Field<NonNullGraphType<LongGraphType>>(
"startedBlockIndex",
description: "The block index the user started to stake.",
resolve: context => context.Source.StakeState.StartedBlockIndex);
Field<NonNullGraphType<IntGraphType>>(
nameof(StakeState.ReceivedBlockIndex),
Field<NonNullGraphType<LongGraphType>>(
"receivedBlockIndex",
description: "The block index the user received rewards.",
resolve: context => context.Source.StakeState.ReceivedBlockIndex);
Field<NonNullGraphType<LongGraphType>>(
nameof(StakeState.CancellableBlockIndex),
"cancellableBlockIndex",
description: "The block index the user can cancel the staking.",
resolve: context => context.Source.StakeState.CancellableBlockIndex);
Field<NonNullGraphType<LongGraphType>>(
"claimableBlockIndex",
description: "The block index the user can claim rewards.",
resolve: context => context.Source.StakeState.GetClaimableBlockIndex(
context.Source.BlockIndex));
Field<NonNullGraphType<StakeAchievementsType>>(
resolve: context => context.Source.StakeState.ClaimableBlockIndex);
Field<StakeAchievementsType>(
nameof(StakeState.Achievements),
description: "The staking achievements.",
resolve: context => context.Source.StakeState.Achievements);
deprecationReason: "Since StakeStateV2, the achievement became removed.",
resolve: _ => null);
Field<NonNullGraphType<StakeRewardsType>>(
"stakeRewards",
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);

return (stakeRegularRewardSheet, stakeRegularFixedRewardSheet);
}
);
}
}
}
Loading

0 comments on commit 7b37a0e

Please sign in to comment.