From 2d9279201947b37216216122353ccb727bf539c8 Mon Sep 17 00:00:00 2001 From: ssun0121 Date: Thu, 23 Nov 2023 20:32:56 +0800 Subject: [PATCH 01/41] feat:add charging address. --- .../Extensions/TransactionResultExtensions.cs | 13 ++++-- .../Dto/CalculateTransactionFeeOutput.cs | 5 ++ .../Services/TransactionAppService.cs | 13 +++++- .../Infrastructure/CalculateFunctionTest.cs | 13 ++++-- .../ExecutionPluginForMethodFeeTest.cs | 31 ++++++++----- ...ExecutionPluginForMethodFeeWithForkTest.cs | 5 +- ...utionPluginForUserContractMethodFeeTest.cs | 46 +++++++++++++------ 7 files changed, 89 insertions(+), 37 deletions(-) diff --git a/src/AElf.Kernel.FeeCalculation/Extensions/TransactionResultExtensions.cs b/src/AElf.Kernel.FeeCalculation/Extensions/TransactionResultExtensions.cs index ff1233b211..cd35c882f8 100644 --- a/src/AElf.Kernel.FeeCalculation/Extensions/TransactionResultExtensions.cs +++ b/src/AElf.Kernel.FeeCalculation/Extensions/TransactionResultExtensions.cs @@ -8,13 +8,18 @@ namespace AElf.Kernel.FeeCalculation.Extensions; public static class TransactionResultExtensions { - public static Dictionary GetChargedTransactionFees(this TransactionResult transactionResult) + public static Dictionary> GetChargedTransactionFees( + this TransactionResult transactionResult) { return transactionResult.Logs .Where(l => l.Name == nameof(TransactionFeeCharged)) - .Select(l => TransactionFeeCharged.Parser.ParseFrom(l.NonIndexed)) - .GroupBy(fee => fee.Symbol, fee => fee.Amount) - .ToDictionary(g => g.Key, g => g.Sum()); + .GroupBy( + log => TransactionFeeCharged.Parser.ParseFrom(log.Indexed[0]).ChargingAddress, + log => TransactionFeeCharged.Parser.ParseFrom(log.NonIndexed)) + .ToDictionary(e => e.Key, + e => e + .GroupBy(fee => fee.Symbol, fee => fee.Amount) + .ToDictionary(g => g.Key, g => g.Sum())); } public static Dictionary GetConsumedResourceTokens(this TransactionResult transactionResult) diff --git a/src/AElf.WebApp.Application.Chain/Dto/CalculateTransactionFeeOutput.cs b/src/AElf.WebApp.Application.Chain/Dto/CalculateTransactionFeeOutput.cs index bbba632175..1c01c7e60c 100644 --- a/src/AElf.WebApp.Application.Chain/Dto/CalculateTransactionFeeOutput.cs +++ b/src/AElf.WebApp.Application.Chain/Dto/CalculateTransactionFeeOutput.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using AElf.Types; namespace AElf.WebApp.Application.Chain.Dto; @@ -9,4 +10,8 @@ public class CalculateTransactionFeeOutput public Dictionary TransactionFee { get; set; } public Dictionary ResourceFee { get; set; } + + public string Error { get; set; } + + public Address TransactionFeeChargingAddress { get; set; } } \ No newline at end of file diff --git a/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs b/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs index 01b8459811..db03099725 100644 --- a/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs +++ b/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs @@ -17,6 +17,7 @@ using Google.Protobuf.WellKnownTypes; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; using Volo.Abp; using Volo.Abp.EventBus.Local; using Volo.Abp.ObjectMapping; @@ -47,17 +48,21 @@ public class TransactionAppService : AElfAppService, ITransactionAppService private readonly ITransactionReadOnlyExecutionService _transactionReadOnlyExecutionService; private readonly ITransactionResultStatusCacheProvider _transactionResultStatusCacheProvider; private readonly IPlainTransactionExecutingService _plainTransactionExecutingService; + private readonly WebAppOptions _webAppOptions; + public TransactionAppService(ITransactionReadOnlyExecutionService transactionReadOnlyExecutionService, IBlockchainService blockchainService, IObjectMapper objectMapper, ITransactionResultStatusCacheProvider transactionResultStatusCacheProvider, - IPlainTransactionExecutingService plainTransactionExecutingService) + IPlainTransactionExecutingService plainTransactionExecutingService, + IOptionsMonitor webAppOptions) { _transactionReadOnlyExecutionService = transactionReadOnlyExecutionService; _blockchainService = blockchainService; _objectMapper = objectMapper; _transactionResultStatusCacheProvider = transactionResultStatusCacheProvider; _plainTransactionExecutingService = plainTransactionExecutingService; + _webAppOptions = webAppOptions.CurrentValue; LocalEventBus = NullLocalEventBus.Instance; Logger = NullLogger.Instance; @@ -292,13 +297,17 @@ private async Task EstimateTransactionFee(Transac var transactionFees = executionReturnSets.FirstOrDefault()?.TransactionResult.GetChargedTransactionFees(); var resourceFees = executionReturnSets.FirstOrDefault()?.TransactionResult.GetConsumedResourceTokens(); + var chargingAddress = transactionFees?.Keys.First(); result.Success = true; - result.TransactionFee = transactionFees; + result.TransactionFeeChargingAddress = chargingAddress; + result.TransactionFee = transactionFees?[chargingAddress]; result.ResourceFee = resourceFees; } else { result.Success = false; + result.Error = TransactionErrorResolver.TakeErrorMessage( + executionReturnSets.FirstOrDefault()?.TransactionResult.Error, _webAppOptions.IsDebugMode); } return result; diff --git a/test/AElf.Kernel.FeeCalculation.Tests/Infrastructure/CalculateFunctionTest.cs b/test/AElf.Kernel.FeeCalculation.Tests/Infrastructure/CalculateFunctionTest.cs index 2e8484cd82..78c3d0419a 100644 --- a/test/AElf.Kernel.FeeCalculation.Tests/Infrastructure/CalculateFunctionTest.cs +++ b/test/AElf.Kernel.FeeCalculation.Tests/Infrastructure/CalculateFunctionTest.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using AElf.Contracts.MultiToken; using AElf.CSharp.Core.Extension; @@ -65,30 +66,34 @@ public void GetChargedTransactionFees_Test() var transactionResult = new TransactionResult(); transactionResult.Logs.Add(new TransactionFeeCharged { + ChargingAddress = SampleAddress.AddressList[0], Amount = 1, Symbol = "ELF" }.ToLogEvent()); transactionResult.Logs.Add(new TransactionFeeCharged { + ChargingAddress = SampleAddress.AddressList[0], Amount = 2, Symbol = "ELF" }.ToLogEvent()); transactionResult.Logs.Add(new TransactionFeeCharged { + ChargingAddress = SampleAddress.AddressList[1], Amount = 3, Symbol = "TEST" }.ToLogEvent()); transactionResult.Logs.Add(new TransactionFeeCharged { + ChargingAddress = SampleAddress.AddressList[1], Amount = 4, Symbol = "TEST" }.ToLogEvent()); var feeDic = transactionResult.GetChargedTransactionFees(); feeDic.Count.ShouldBe(2); - feeDic.Keys.First().ShouldBe("ELF"); - feeDic.Values.First().ShouldBe(3); - feeDic.Keys.Last().ShouldBe("TEST"); - feeDic.Values.Last().ShouldBe(7); + feeDic.Keys.First().ShouldBe(SampleAddress.AddressList[0]); + feeDic.Values.First()["ELF"].ShouldBe(3); + feeDic.Keys.Last().ShouldBe(SampleAddress.AddressList[1]); + feeDic.Values.Last()["TEST"].ShouldBe(7); } [Fact] diff --git a/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForMethodFeeTest.cs b/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForMethodFeeTest.cs index b265f1cca9..1264e6fb14 100644 --- a/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForMethodFeeTest.cs +++ b/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForMethodFeeTest.cs @@ -268,14 +268,14 @@ public async Task ChargeFee_SuccessfulTest() chargingAddress.ShouldContain(dummy.Transaction.From); var transactionFeeDic = dummy.TransactionResult.GetChargedTransactionFees(); - await CheckTransactionFeesMapAsync(transactionFeeDic); + await CheckTransactionFeesMapAsync(DefaultSender,transactionFeeDic); var after = await tokenContractStub.GetBalance.CallAsync(new GetBalanceInput { Owner = DefaultSender, Symbol = "ELF" }); - after.Balance.ShouldBe(before.Balance - transactionFeeDic[before.Symbol]); + after.Balance.ShouldBe(before.Balance - transactionFeeDic[DefaultAddress][before.Symbol]); } private static List
GetChargingAddress(TransactionResult transactionResult) @@ -284,7 +284,7 @@ private static List
GetChargingAddress(TransactionResult transactionRes return relatedLogs.Select(l => TransactionFeeCharged.Parser.ParseFrom(l.Indexed[0]).ChargingAddress).ToList(); } - private async Task CheckTransactionFeesMapAsync(Dictionary transactionFeeDic) + private async Task CheckTransactionFeesMapAsync(Address chargingAddress, Dictionary> transactionFeeDic) { var chain = await _blockchainService.GetChainAsync(); var transactionFeesMap = await _totalTransactionFeesMapProvider.GetTotalTransactionFeesMapAsync(new ChainContext @@ -293,7 +293,13 @@ private async Task CheckTransactionFeesMapAsync(Dictionary transac BlockHeight = chain.BestChainHeight }); foreach (var transactionFee in transactionFeeDic) - transactionFeesMap.Value[transactionFee.Key].ShouldBe(transactionFee.Value); + { + transactionFee.Key.ShouldBe(chargingAddress); + foreach (var value in transactionFee.Value) + { + transactionFeesMap.Value[value.Key].ShouldBe(value.Value); + } + } } [Fact] @@ -324,7 +330,7 @@ await tokenContractStub.Transfer.SendAsync(new TransferInput dummy.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); dummy.TransactionResult.Error.ShouldBe("Pre-Error: Transaction fee not enough."); var transactionFeeDic = dummy.TransactionResult.GetChargedTransactionFees(); - await CheckTransactionFeesMapAsync(transactionFeeDic); + await CheckTransactionFeesMapAsync(Accounts[1].Address,transactionFeeDic); var afterFee = (await tokenContractStub.GetBalance.CallAsync(new GetBalanceInput { @@ -332,7 +338,7 @@ await tokenContractStub.Transfer.SendAsync(new TransferInput Symbol = "ELF" })).Balance; afterFee.ShouldBe(0); - transactionFeeDic["ELF"].ShouldBe(issueAmount); + transactionFeeDic[Accounts[1].Address]["ELF"].ShouldBe(issueAmount); } [Theory] @@ -378,7 +384,7 @@ await tokenContractStub.Transfer.SendAsync(new TransferInput Symbol = chargedSymbol ?? "ELF" })).Balance; - Dictionary transactionFeeDic; + Dictionary> transactionFeeDic; var userTestContractStub = GetTester(_testContractAddress, Accounts[1].KeyPair); if (isChargingSuccessful) @@ -387,8 +393,9 @@ await tokenContractStub.Transfer.SendAsync(new TransferInput dummyResult.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); if (chargedSymbol != null) { - dummyResult.TransactionResult.GetChargedTransactionFees().Keys.ShouldContain(chargedSymbol); - dummyResult.TransactionResult.GetChargedTransactionFees().Values.ShouldContain(chargedAmount); + dummyResult.TransactionResult.GetChargedTransactionFees().Keys.ShouldContain(Accounts[1].Address); + dummyResult.TransactionResult.GetChargedTransactionFees()[Accounts[1].Address].Keys.ShouldContain(chargedSymbol); + dummyResult.TransactionResult.GetChargedTransactionFees()[Accounts[1].Address].Values.ShouldContain(chargedAmount); } transactionFeeDic = dummyResult.TransactionResult.GetChargedTransactionFees(); @@ -399,13 +406,13 @@ await tokenContractStub.Transfer.SendAsync(new TransferInput dummyResult.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); dummyResult.TransactionResult.Error.ShouldBe("Pre-Error: Transaction fee not enough."); if (chargedSymbol != null) - dummyResult.TransactionResult.GetChargedTransactionFees().Keys.ShouldContain(chargedSymbol); + dummyResult.TransactionResult.GetChargedTransactionFees()[Accounts[1].Address].Keys.ShouldContain(chargedSymbol); transactionFeeDic = dummyResult.TransactionResult.GetChargedTransactionFees(); } - await CheckTransactionFeesMapAsync(transactionFeeDic); + await CheckTransactionFeesMapAsync(Accounts[1].Address,transactionFeeDic); if (chargedSymbol != null) - transactionFeeDic[chargedSymbol].ShouldBe(chargedAmount); + transactionFeeDic[Accounts[1].Address][chargedSymbol].ShouldBe(chargedAmount); var finalBalance = (await tokenContractStub.GetBalance.CallAsync(new GetBalanceInput { diff --git a/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForMethodFeeWithForkTest.cs b/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForMethodFeeWithForkTest.cs index 6a6ffe5a73..02aebc36aa 100644 --- a/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForMethodFeeWithForkTest.cs +++ b/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForMethodFeeWithForkTest.cs @@ -55,8 +55,9 @@ await SetMethodFeeWithProposalAsync(new MethodFees Memo = Guid.NewGuid().ToString(), To = SampleAddress.AddressList[0] }); + var fromAddress = Address.FromPublicKey(Tester.KeyPair.PublicKey); var transactionResult = await Tester.GetTransactionResultAsync(result.Item2.GetHash()); - var targetFee = transactionResult.GetChargedTransactionFees().First().Value; + var targetFee = transactionResult.GetChargedTransactionFees()[fromAddress].First().Value; var transactionFeesMap = await GetTransactionFeesMapAsync(new ChainContext { @@ -97,7 +98,7 @@ await SetMethodFeeWithProposalAsync(new MethodFees To = SampleAddress.AddressList[0] }); transactionResult = await Tester.GetTransactionResultAsync(result.Item2.GetHash()); - var fee = transactionResult.GetChargedTransactionFees().First().Value; + var fee = transactionResult.GetChargedTransactionFees()[fromAddress].First().Value; transactionFeesMap = await GetTransactionFeesMapAsync(new ChainContext { BlockHash = result.Item1.GetHash(), BlockHeight = result.Item1.Height diff --git a/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForUserContractMethodFeeTest.cs b/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForUserContractMethodFeeTest.cs index 25e8daa5f4..688abbd017 100644 --- a/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForUserContractMethodFeeTest.cs +++ b/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForUserContractMethodFeeTest.cs @@ -75,14 +75,14 @@ public async Task ChargeUserContractFeeTest_Success() var result = await _testContractStub.TestMethod.SendAsync(new Empty()); result.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); var transactionFeeDic = result.TransactionResult.GetChargedTransactionFees(); - await CheckTransactionFeesMapAsync(transactionFeeDic); + await CheckTransactionFeesMapAsync(DefaultAddress,transactionFeeDic); var after = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput { Owner = DefaultAddress, Symbol = "ELF" }); - after.Balance.ShouldBe(beforeBalance.Balance - transactionFeeDic[beforeBalance.Symbol]); + after.Balance.ShouldBe(beforeBalance.Balance - transactionFeeDic[DefaultAddress][beforeBalance.Symbol]); } [Fact] @@ -97,7 +97,7 @@ public async Task ChargeUserContractFeeTest_Success_BaseFeeIsFree() var result = await _testContractStub.TestMethod.SendAsync(new Empty()); result.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); var transactionFeeDic = result.TransactionResult.GetChargedTransactionFees(); - await CheckTransactionFeesMapAsync(transactionFeeDic); + await CheckTransactionFeesMapAsync(DefaultAddress,transactionFeeDic); var after = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput { @@ -193,7 +193,7 @@ await TokenContractStub.Transfer.SendAsync(new TransferInput Symbol = chargedSymbol ?? "ELF" })).Balance; - Dictionary transactionFeeDic; + Dictionary> transactionFeeDic; var userTestContractStub = GetTester(_testContractAddress, Accounts[1].KeyPair); if (isChargingSuccessful) @@ -202,8 +202,13 @@ await TokenContractStub.Transfer.SendAsync(new TransferInput result.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); if (chargedSymbol != null) { - result.TransactionResult.GetChargedTransactionFees().Keys.ShouldContain(chargedSymbol); - result.TransactionResult.GetChargedTransactionFees().Values.ShouldContain(chargedAmount); + var token = new Dictionary + { + [chargedSymbol] = chargedAmount + }; + result.TransactionResult.GetChargedTransactionFees().Keys.ShouldContain(Accounts[1].Address); + var fee = result.TransactionResult.GetChargedTransactionFees()[Accounts[1].Address]; + fee.ShouldContainKeyAndValue(chargedSymbol,chargedAmount); } transactionFeeDic = result.TransactionResult.GetChargedTransactionFees(); @@ -214,13 +219,21 @@ await TokenContractStub.Transfer.SendAsync(new TransferInput result.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); result.TransactionResult.Error.ShouldBe("Pre-Error: Transaction fee not enough."); if (chargedSymbol != null) - result.TransactionResult.GetChargedTransactionFees().Keys.ShouldContain(chargedSymbol); + { + var token = new Dictionary + { + [chargedSymbol] = chargedAmount + }; + result.TransactionResult.GetChargedTransactionFees().Keys.ShouldContain(Accounts[1].Address); + var fee = result.TransactionResult.GetChargedTransactionFees()[Accounts[1].Address]; + fee.ShouldContainKeyAndValue(chargedSymbol,chargedAmount); + } transactionFeeDic = result.TransactionResult.GetChargedTransactionFees(); } - await CheckTransactionFeesMapAsync(transactionFeeDic); + await CheckTransactionFeesMapAsync(Accounts[1].Address,transactionFeeDic); if (chargedSymbol != null) - transactionFeeDic[chargedSymbol].ShouldBe(chargedAmount); + transactionFeeDic[Accounts[1].Address][chargedSymbol].ShouldBe(chargedAmount); var finalBalance = (await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput { @@ -253,7 +266,7 @@ public async Task ChargeFee_SizeFeeIsFree() var result = await _testContractStub.TestMethod.SendAsync(new Empty()); result.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); var transactionFeeDic = result.TransactionResult.GetChargedTransactionFees(); - await CheckTransactionFeesMapAsync(transactionFeeDic); + await CheckTransactionFeesMapAsync(DefaultAddress, transactionFeeDic); var after = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput { @@ -294,7 +307,7 @@ public async Task ChargeFee_SpecConfigurationFee() var result = await _testContractStub.TestMethod.SendAsync(new Empty()); result.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); var transactionFeeDic = result.TransactionResult.GetChargedTransactionFees(); - await CheckTransactionFeesMapAsync(transactionFeeDic); + await CheckTransactionFeesMapAsync(DefaultAddress, transactionFeeDic); var after = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput { @@ -356,7 +369,7 @@ private async Task Initialize() } } - private async Task CheckTransactionFeesMapAsync(Dictionary transactionFeeDic) + private async Task CheckTransactionFeesMapAsync(Address chargingAddress, Dictionary> transactionFeeDic) { var chain = await _blockchainService.GetChainAsync(); var transactionFeesMap = await _totalTransactionFeesMapProvider.GetTotalTransactionFeesMapAsync(new ChainContext @@ -365,7 +378,14 @@ private async Task CheckTransactionFeesMapAsync(Dictionary transac BlockHeight = chain.BestChainHeight }); foreach (var transactionFee in transactionFeeDic) - transactionFeesMap.Value[transactionFee.Key].ShouldBe(transactionFee.Value); + { + transactionFee.Key.ShouldBe(chargingAddress); + foreach (var value in transactionFee.Value) + { + transactionFeesMap.Value[value.Key].ShouldBe(value.Value); + } + } + } private async Task SetUserContractFeeAsync(int amount) From 61a5920c8dee434c7372a90f8a20071f7d31722a Mon Sep 17 00:00:00 2001 From: ssun0121 Date: Fri, 24 Nov 2023 13:48:45 +0800 Subject: [PATCH 02/41] feat:change type to string. --- .../Dto/CalculateTransactionFeeOutput.cs | 2 +- .../Services/TransactionAppService.cs | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/AElf.WebApp.Application.Chain/Dto/CalculateTransactionFeeOutput.cs b/src/AElf.WebApp.Application.Chain/Dto/CalculateTransactionFeeOutput.cs index 1c01c7e60c..c73eee55d6 100644 --- a/src/AElf.WebApp.Application.Chain/Dto/CalculateTransactionFeeOutput.cs +++ b/src/AElf.WebApp.Application.Chain/Dto/CalculateTransactionFeeOutput.cs @@ -13,5 +13,5 @@ public class CalculateTransactionFeeOutput public string Error { get; set; } - public Address TransactionFeeChargingAddress { get; set; } + public string TransactionFeeChargingAddress { get; set; } } \ No newline at end of file diff --git a/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs b/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs index db03099725..fac39bbcd2 100644 --- a/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs +++ b/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs @@ -299,8 +299,11 @@ private async Task EstimateTransactionFee(Transac var resourceFees = executionReturnSets.FirstOrDefault()?.TransactionResult.GetConsumedResourceTokens(); var chargingAddress = transactionFees?.Keys.First(); result.Success = true; - result.TransactionFeeChargingAddress = chargingAddress; - result.TransactionFee = transactionFees?[chargingAddress]; + result.TransactionFeeChargingAddress = chargingAddress?.ToBase58(); + if (chargingAddress != null) + { + result.TransactionFee = transactionFees[chargingAddress]; + } result.ResourceFee = resourceFees; } else From 4a9536670a2f86b91367e8c211a0e94418ec4ddd Mon Sep 17 00:00:00 2001 From: ssun0121 Date: Tue, 28 Nov 2023 01:29:56 +0800 Subject: [PATCH 03/41] feat:change output structure. --- .../Extensions/TransactionResultExtensions.cs | 10 ++++-- .../Dto/CalculateTransactionFeeOutput.cs | 17 ++++++++-- .../Services/TransactionAppService.cs | 33 +++++++++++++++---- .../ExecutionPluginForResourceFeeTest.cs | 4 +-- .../BlockChainAppServiceTest.cs | 6 ++++ 5 files changed, 55 insertions(+), 15 deletions(-) diff --git a/src/AElf.Kernel.FeeCalculation/Extensions/TransactionResultExtensions.cs b/src/AElf.Kernel.FeeCalculation/Extensions/TransactionResultExtensions.cs index cd35c882f8..138d7853f3 100644 --- a/src/AElf.Kernel.FeeCalculation/Extensions/TransactionResultExtensions.cs +++ b/src/AElf.Kernel.FeeCalculation/Extensions/TransactionResultExtensions.cs @@ -22,12 +22,16 @@ public static Dictionary> GetChargedTransactio .ToDictionary(g => g.Key, g => g.Sum())); } - public static Dictionary GetConsumedResourceTokens(this TransactionResult transactionResult) + public static Dictionary> GetConsumedResourceTokens(this TransactionResult transactionResult) { var relatedLogs = transactionResult.Logs.Where(l => l.Name == nameof(ResourceTokenCharged)).ToList(); - if (!relatedLogs.Any()) return new Dictionary(); + if (!relatedLogs.Any()) return new Dictionary>(); return relatedLogs.Select(l => ResourceTokenCharged.Parser.ParseFrom(l.NonIndexed)) - .ToDictionary(e => e.Symbol, e => e.Amount); + .GroupBy(g => g.ContractAddress) + .ToDictionary(e => e.Key, + e => e + .GroupBy(fee => fee.Symbol, fee => fee.Amount) + .ToDictionary(g => g.Key, g => g.Sum())); } public static Dictionary GetOwningResourceTokens(this TransactionResult transactionResult) diff --git a/src/AElf.WebApp.Application.Chain/Dto/CalculateTransactionFeeOutput.cs b/src/AElf.WebApp.Application.Chain/Dto/CalculateTransactionFeeOutput.cs index c73eee55d6..97dda4aa1c 100644 --- a/src/AElf.WebApp.Application.Chain/Dto/CalculateTransactionFeeOutput.cs +++ b/src/AElf.WebApp.Application.Chain/Dto/CalculateTransactionFeeOutput.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using AElf.Types; @@ -7,11 +8,21 @@ public class CalculateTransactionFeeOutput { public bool Success { get; set; } + [Obsolete("ThisField is deprecated, please use NewField instead.")] public Dictionary TransactionFee { get; set; } - + + [Obsolete("ThisField is deprecated, please use NewField instead.")] public Dictionary ResourceFee { get; set; } + + public List TransactionFeeList { get; set; } + + public List ResourceFeeList { get; set; } public string Error { get; set; } - - public string TransactionFeeChargingAddress { get; set; } +} + +public class FeeDto +{ + public string ChargingAddress { get; set; } + public Dictionary Fee { get; set; } } \ No newline at end of file diff --git a/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs b/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs index fac39bbcd2..529ab1448c 100644 --- a/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs +++ b/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs @@ -297,14 +297,11 @@ private async Task EstimateTransactionFee(Transac var transactionFees = executionReturnSets.FirstOrDefault()?.TransactionResult.GetChargedTransactionFees(); var resourceFees = executionReturnSets.FirstOrDefault()?.TransactionResult.GetConsumedResourceTokens(); - var chargingAddress = transactionFees?.Keys.First(); result.Success = true; - result.TransactionFeeChargingAddress = chargingAddress?.ToBase58(); - if (chargingAddress != null) - { - result.TransactionFee = transactionFees[chargingAddress]; - } - result.ResourceFee = resourceFees; + result.TransactionFee = GetFeeValue(transactionFees); + result.ResourceFee = GetFeeValue(resourceFees); + result.TransactionFeeList = GetFeeList(transactionFees); + result.ResourceFeeList = GetFeeList(resourceFees); } else { @@ -316,6 +313,28 @@ private async Task EstimateTransactionFee(Transac return result; } + private Dictionary GetFeeValue(Dictionary> feeMap) + { + return feeMap?.SelectMany(pair => pair.Value) + .GroupBy(p => p.Key) + .ToDictionary(g => g.Key, g => g.Sum(pair => pair.Value)); + } + + private List GetFeeList(Dictionary> feeMap) + { + var feeList = new List(); + if (feeMap != null) + { + feeList.AddRange(feeMap.Select(fee => new FeeDto + { + ChargingAddress = fee.Key.ToBase58(), + Fee = fee.Value + })); + } + + return feeList; + } + private async Task PublishTransactionsAsync(string[] rawTransactions) { var txIds = new string[rawTransactions.Length]; diff --git a/test/AElf.Kernel.SmartContract.ExecutionPluginForResourceFee.Tests/ExecutionPluginForResourceFeeTest.cs b/test/AElf.Kernel.SmartContract.ExecutionPluginForResourceFee.Tests/ExecutionPluginForResourceFeeTest.cs index 6a47875b59..08d30b7593 100644 --- a/test/AElf.Kernel.SmartContract.ExecutionPluginForResourceFee.Tests/ExecutionPluginForResourceFeeTest.cs +++ b/test/AElf.Kernel.SmartContract.ExecutionPluginForResourceFee.Tests/ExecutionPluginForResourceFeeTest.cs @@ -258,8 +258,8 @@ public async Task CompareConsumptions() write.ShouldBeGreaterThan(write1); traffic.ShouldBe(traffic1); - var consumedTokens1 = txResult1.GetConsumedResourceTokens(); - var consumedTokens2 = txResult2.GetConsumedResourceTokens(); + var consumedTokens1 = txResult1.GetConsumedResourceTokens()[DefaultSender]; + var consumedTokens2 = txResult2.GetConsumedResourceTokens()[DefaultSender]; consumedTokens1["READ"].ShouldBeGreaterThan(consumedTokens2["READ"]); consumedTokens1["WRITE"].ShouldBeGreaterThan(consumedTokens2["WRITE"]); consumedTokens1["TRAFFIC"].ShouldBe(consumedTokens2["TRAFFIC"]); diff --git a/test/AElf.WebApp.Application.Chain.Tests/BlockChainAppServiceTest.cs b/test/AElf.WebApp.Application.Chain.Tests/BlockChainAppServiceTest.cs index b898977dc3..33a1e3681e 100644 --- a/test/AElf.WebApp.Application.Chain.Tests/BlockChainAppServiceTest.cs +++ b/test/AElf.WebApp.Application.Chain.Tests/BlockChainAppServiceTest.cs @@ -1682,6 +1682,12 @@ public async Task CalculateTransactionFee_Success_Test() }; var response = await PostResponseAsObjectAsync("/api/blockChain/CalculateTransactionFee", parameters); response.Success.ShouldBe(true); + response.TransactionFeeList[0].ChargingAddress.ShouldBe(transaction.From.ToBase58()); + response.TransactionFeeList[0].Fee.First().Key.ShouldBe("ELF"); + response.TransactionFeeList[0].Fee.First().Value.ShouldBeGreaterThan(10000000L); + response.TransactionFee.First().Key.ShouldBe("ELF"); + response.TransactionFee.First().Value.ShouldBeGreaterThan(10000000L); + } [Fact] From ac7fb264e39bd5ebe5d50930e0f5e12dd62d44fc Mon Sep 17 00:00:00 2001 From: ssun0121 Date: Tue, 28 Nov 2023 11:02:50 +0800 Subject: [PATCH 04/41] feat:Modifying the description. --- .../Dto/CalculateTransactionFeeOutput.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AElf.WebApp.Application.Chain/Dto/CalculateTransactionFeeOutput.cs b/src/AElf.WebApp.Application.Chain/Dto/CalculateTransactionFeeOutput.cs index 97dda4aa1c..ffbbfc1274 100644 --- a/src/AElf.WebApp.Application.Chain/Dto/CalculateTransactionFeeOutput.cs +++ b/src/AElf.WebApp.Application.Chain/Dto/CalculateTransactionFeeOutput.cs @@ -8,10 +8,10 @@ public class CalculateTransactionFeeOutput { public bool Success { get; set; } - [Obsolete("ThisField is deprecated, please use NewField instead.")] + [Obsolete("This property is deprecated and will be removed in the next version. Use the TransactionFeeList instead.")] public Dictionary TransactionFee { get; set; } - [Obsolete("ThisField is deprecated, please use NewField instead.")] + [Obsolete("This property is deprecated and will be removed in the next version. Use the TransactionFeeList instead.")] public Dictionary ResourceFee { get; set; } public List TransactionFeeList { get; set; } From 6c27a2916441067c9f18f13e8bee49fd9d3de114 Mon Sep 17 00:00:00 2001 From: ssun0121 Date: Tue, 28 Nov 2023 14:13:27 +0800 Subject: [PATCH 05/41] feat:change output property. --- .../Dto/CalculateTransactionFeeOutput.cs | 8 +++---- .../Services/TransactionAppService.cs | 22 ++++++++----------- .../BlockChainAppServiceTest.cs | 6 ++--- 3 files changed, 16 insertions(+), 20 deletions(-) diff --git a/src/AElf.WebApp.Application.Chain/Dto/CalculateTransactionFeeOutput.cs b/src/AElf.WebApp.Application.Chain/Dto/CalculateTransactionFeeOutput.cs index ffbbfc1274..1446177873 100644 --- a/src/AElf.WebApp.Application.Chain/Dto/CalculateTransactionFeeOutput.cs +++ b/src/AElf.WebApp.Application.Chain/Dto/CalculateTransactionFeeOutput.cs @@ -8,15 +8,15 @@ public class CalculateTransactionFeeOutput { public bool Success { get; set; } - [Obsolete("This property is deprecated and will be removed in the next version. Use the TransactionFeeList instead.")] + [Obsolete("This property is deprecated and will be removed in the next version. Use the TransactionFees instead.")] public Dictionary TransactionFee { get; set; } - [Obsolete("This property is deprecated and will be removed in the next version. Use the TransactionFeeList instead.")] + [Obsolete("This property is deprecated and will be removed in the next version. Use the ResourceFees instead.")] public Dictionary ResourceFee { get; set; } - public List TransactionFeeList { get; set; } + public FeeDto TransactionFees { get; set; } - public List ResourceFeeList { get; set; } + public FeeDto ResourceFees { get; set; } public string Error { get; set; } } diff --git a/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs b/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs index 529ab1448c..f4836f8128 100644 --- a/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs +++ b/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs @@ -300,8 +300,8 @@ private async Task EstimateTransactionFee(Transac result.Success = true; result.TransactionFee = GetFeeValue(transactionFees); result.ResourceFee = GetFeeValue(resourceFees); - result.TransactionFeeList = GetFeeList(transactionFees); - result.ResourceFeeList = GetFeeList(resourceFees); + result.TransactionFees = GetFee(transactionFees); + result.ResourceFees = GetFee(resourceFees); } else { @@ -320,19 +320,15 @@ private Dictionary GetFeeValue(Dictionary g.Key, g => g.Sum(pair => pair.Value)); } - private List GetFeeList(Dictionary> feeMap) + private FeeDto GetFee(Dictionary> feeMap) { - var feeList = new List(); - if (feeMap != null) + var fee = feeMap?.Select(f => new FeeDto { - feeList.AddRange(feeMap.Select(fee => new FeeDto - { - ChargingAddress = fee.Key.ToBase58(), - Fee = fee.Value - })); - } - - return feeList; + ChargingAddress = f.Key.ToBase58(), + Fee = f.Value + }).FirstOrDefault(); + + return fee; } private async Task PublishTransactionsAsync(string[] rawTransactions) diff --git a/test/AElf.WebApp.Application.Chain.Tests/BlockChainAppServiceTest.cs b/test/AElf.WebApp.Application.Chain.Tests/BlockChainAppServiceTest.cs index 33a1e3681e..f2b2cf456c 100644 --- a/test/AElf.WebApp.Application.Chain.Tests/BlockChainAppServiceTest.cs +++ b/test/AElf.WebApp.Application.Chain.Tests/BlockChainAppServiceTest.cs @@ -1682,9 +1682,9 @@ public async Task CalculateTransactionFee_Success_Test() }; var response = await PostResponseAsObjectAsync("/api/blockChain/CalculateTransactionFee", parameters); response.Success.ShouldBe(true); - response.TransactionFeeList[0].ChargingAddress.ShouldBe(transaction.From.ToBase58()); - response.TransactionFeeList[0].Fee.First().Key.ShouldBe("ELF"); - response.TransactionFeeList[0].Fee.First().Value.ShouldBeGreaterThan(10000000L); + response.TransactionFees.ChargingAddress.ShouldBe(transaction.From.ToBase58()); + response.TransactionFees.Fee.First().Key.ShouldBe("ELF"); + response.TransactionFees.Fee.First().Value.ShouldBeGreaterThan(10000000L); response.TransactionFee.First().Key.ShouldBe("ELF"); response.TransactionFee.First().Value.ShouldBeGreaterThan(10000000L); From ea085f34032f719debf31581f6276e9b157f8c7a Mon Sep 17 00:00:00 2001 From: ssun0121 Date: Tue, 28 Nov 2023 16:14:11 +0800 Subject: [PATCH 06/41] test:add test. --- .../Infrastructure/CalculateFunctionTest.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/test/AElf.Kernel.FeeCalculation.Tests/Infrastructure/CalculateFunctionTest.cs b/test/AElf.Kernel.FeeCalculation.Tests/Infrastructure/CalculateFunctionTest.cs index 78c3d0419a..211d309fb0 100644 --- a/test/AElf.Kernel.FeeCalculation.Tests/Infrastructure/CalculateFunctionTest.cs +++ b/test/AElf.Kernel.FeeCalculation.Tests/Infrastructure/CalculateFunctionTest.cs @@ -77,6 +77,18 @@ public void GetChargedTransactionFees_Test() Symbol = "ELF" }.ToLogEvent()); transactionResult.Logs.Add(new TransactionFeeCharged + { + ChargingAddress = SampleAddress.AddressList[0], + Amount = 3, + Symbol = "USDT" + }.ToLogEvent()); + transactionResult.Logs.Add(new TransactionFeeCharged + { + ChargingAddress = SampleAddress.AddressList[0], + Amount = 4, + Symbol = "USDT" + }.ToLogEvent()); + transactionResult.Logs.Add(new TransactionFeeCharged { ChargingAddress = SampleAddress.AddressList[1], Amount = 3, @@ -91,7 +103,8 @@ public void GetChargedTransactionFees_Test() var feeDic = transactionResult.GetChargedTransactionFees(); feeDic.Count.ShouldBe(2); feeDic.Keys.First().ShouldBe(SampleAddress.AddressList[0]); - feeDic.Values.First()["ELF"].ShouldBe(3); + feeDic.Values.First()["ELF"].ShouldBe(3); + feeDic.Values.First()["USDT"].ShouldBe(7); feeDic.Keys.Last().ShouldBe(SampleAddress.AddressList[1]); feeDic.Values.Last()["TEST"].ShouldBe(7); } From 5c6c669e315c5cc58218e99d0d250ede6db6cc49 Mon Sep 17 00:00:00 2001 From: kevin Date: Thu, 7 Dec 2023 15:16:06 +0800 Subject: [PATCH 07/41] feat: add ModifyTokenIssuerAndOwner, SetTokenIssuerAndOwnerModificationEnabled and GetTokenIssuerAndOwnerModificationEnabled --- .../TokenContractState.cs | 2 ++ .../TokenContract_Actions.cs | 34 +++++++++++++++++++ protobuf/token_contract_impl.proto | 16 +++++++++ 3 files changed, 52 insertions(+) diff --git a/contract/AElf.Contracts.MultiToken/TokenContractState.cs b/contract/AElf.Contracts.MultiToken/TokenContractState.cs index 92da423908..c32b0ea62f 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContractState.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContractState.cs @@ -63,4 +63,6 @@ public partial class TokenContractState : ContractState public SingletonState
ElectionContractAddress { get; set; } public SingletonState
VoteContractAddress { get; set; } + + public SingletonState TokenIssuerAndOwnerModificationDisabled { get; set; } } \ No newline at end of file diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index 483f073b54..073d2fe457 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -589,4 +589,38 @@ public override Empty CrossChainReceiveToken(CrossChainReceiveTokenInput input) } #endregion + + public override Empty ModifyTokenIssuerAndOwner(ModifyTokenIssuerAndOwnerInput input) + { + Assert(!State.TokenIssuerAndOwnerModificationDisabled.Value, "Set token issuer and owner disabled."); + Assert(string.IsNullOrWhiteSpace(input.Symbol), "Invalid input symbol."); + Assert(input.Issuer != null && !input.Issuer.Value.IsNullOrEmpty(), "Invalid input issuer."); + Assert(input.Owner != null && !input.Owner.Value.IsNullOrEmpty(), "Invalid input owner."); + + var tokenInfo = State.TokenInfos[input.Symbol]; + + Assert(tokenInfo != null, "Token is not found."); + Assert(tokenInfo.Owner == null, "Only set token without owner."); + Assert(tokenInfo.Issuer == Context.Sender, "Only token issuer can set token issuer and owner."); + + tokenInfo.Issuer = input.Issuer; + tokenInfo.Owner = input.Owner; + + return new Empty(); + } + + public override Empty SetTokenIssuerAndOwnerModificationEnabled(BoolValue input) + { + AssertSenderAddressWith(GetDefaultParliamentController().OwnerAddress); + Assert(input != null, "Invalid input."); + + State.TokenIssuerAndOwnerModificationDisabled.Value = input.Value; + + return new Empty(); + } + + public override BoolValue GetTokenIssuerAndOwnerModificationEnabled(Empty input) + { + return new BoolValue{ Value = State.TokenIssuerAndOwnerModificationDisabled.Value}; + } } \ No newline at end of file diff --git a/protobuf/token_contract_impl.proto b/protobuf/token_contract_impl.proto index 97acd5b439..b0689af301 100644 --- a/protobuf/token_contract_impl.proto +++ b/protobuf/token_contract_impl.proto @@ -163,6 +163,16 @@ service TokenContractImpl { rpc GetTransactionFeeDelegateInfo(GetTransactionFeeDelegateInfoInput) returns (token.TransactionFeeDelegations){ option (aelf.is_view) = true; } + + rpc ModifyTokenIssuerAndOwner(ModifyTokenIssuerAndOwnerInput) returns (google.protobuf.Empty) { + } + + rpc SetTokenIssuerAndOwnerModificationEnabled(google.protobuf.BoolValue) returns (google.protobuf.Empty) { + } + + rpc GetTokenIssuerAndOwnerModificationEnabled(google.protobuf.Empty) returns (google.protobuf.BoolValue) { + option (aelf.is_view) = true; + } } message AdvanceResourceTokenInput { @@ -416,3 +426,9 @@ message TransactionFeeDelegateInfoCancelled { aelf.Address caller = 3 ; DelegateTransactionList delegate_transaction_list = 4; } + +message ModifyTokenIssuerAndOwnerInput { + string symbol = 1; + aelf.Address issuer = 2; + aelf.Address owner = 3; +} From a1cb698502129a5daf88c40571667a3e99098d98 Mon Sep 17 00:00:00 2001 From: kevin Date: Thu, 7 Dec 2023 15:39:12 +0800 Subject: [PATCH 08/41] fix: format code --- .../AElf.Contracts.MultiToken/TokenContract_Actions.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index 073d2fe457..6f9b499f91 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -596,9 +596,9 @@ public override Empty ModifyTokenIssuerAndOwner(ModifyTokenIssuerAndOwnerInput i Assert(string.IsNullOrWhiteSpace(input.Symbol), "Invalid input symbol."); Assert(input.Issuer != null && !input.Issuer.Value.IsNullOrEmpty(), "Invalid input issuer."); Assert(input.Owner != null && !input.Owner.Value.IsNullOrEmpty(), "Invalid input owner."); - + var tokenInfo = State.TokenInfos[input.Symbol]; - + Assert(tokenInfo != null, "Token is not found."); Assert(tokenInfo.Owner == null, "Only set token without owner."); Assert(tokenInfo.Issuer == Context.Sender, "Only token issuer can set token issuer and owner."); @@ -613,14 +613,14 @@ public override Empty SetTokenIssuerAndOwnerModificationEnabled(BoolValue input) { AssertSenderAddressWith(GetDefaultParliamentController().OwnerAddress); Assert(input != null, "Invalid input."); - + State.TokenIssuerAndOwnerModificationDisabled.Value = input.Value; - + return new Empty(); } public override BoolValue GetTokenIssuerAndOwnerModificationEnabled(Empty input) { - return new BoolValue{ Value = State.TokenIssuerAndOwnerModificationDisabled.Value}; + return new BoolValue { Value = State.TokenIssuerAndOwnerModificationDisabled.Value }; } } \ No newline at end of file From 82b284a3b30d0105ef203617590ebcbf98c81bdd Mon Sep 17 00:00:00 2001 From: kevin Date: Thu, 7 Dec 2023 22:32:36 +0800 Subject: [PATCH 09/41] fix: ModifyTokenIssuerAndOwner --- contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index 6f9b499f91..08c151f096 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -593,7 +593,7 @@ public override Empty CrossChainReceiveToken(CrossChainReceiveTokenInput input) public override Empty ModifyTokenIssuerAndOwner(ModifyTokenIssuerAndOwnerInput input) { Assert(!State.TokenIssuerAndOwnerModificationDisabled.Value, "Set token issuer and owner disabled."); - Assert(string.IsNullOrWhiteSpace(input.Symbol), "Invalid input symbol."); + Assert(!string.IsNullOrWhiteSpace(input.Symbol), "Invalid input symbol."); Assert(input.Issuer != null && !input.Issuer.Value.IsNullOrEmpty(), "Invalid input issuer."); Assert(input.Owner != null && !input.Owner.Value.IsNullOrEmpty(), "Invalid input owner."); From 242638e12e1c82bb8fbe5b17976dd7933cbfbf33 Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 8 Dec 2023 10:16:59 +0800 Subject: [PATCH 10/41] fix: assert message --- contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index 08c151f096..9b945f4da3 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -600,7 +600,7 @@ public override Empty ModifyTokenIssuerAndOwner(ModifyTokenIssuerAndOwnerInput i var tokenInfo = State.TokenInfos[input.Symbol]; Assert(tokenInfo != null, "Token is not found."); - Assert(tokenInfo.Owner == null, "Only set token without owner."); + Assert(tokenInfo.Owner == null, "Can only set token which does not have owner."); Assert(tokenInfo.Issuer == Context.Sender, "Only token issuer can set token issuer and owner."); tokenInfo.Issuer = input.Issuer; From 913ed5bae1f5981307282c0efc633abd98c3b6cf Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 8 Dec 2023 21:29:31 +0800 Subject: [PATCH 11/41] =?UTF-8?q?fix=EF=BC=9Achange=20SetTokenIssuerAndOwn?= =?UTF-8?q?erModificationEnabled=20to=20SetTokenIssuerAndOwnerModification?= =?UTF-8?q?Disabled=20and=20adjust=20GetTokenIssuerAndOwnerModificationEna?= =?UTF-8?q?bled=20return=20value?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TokenContract_Actions.cs | 11 +++++++---- protobuf/token_contract_impl.proto | 6 +++++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index 9b945f4da3..24e44a6560 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -594,7 +594,7 @@ public override Empty ModifyTokenIssuerAndOwner(ModifyTokenIssuerAndOwnerInput i { Assert(!State.TokenIssuerAndOwnerModificationDisabled.Value, "Set token issuer and owner disabled."); Assert(!string.IsNullOrWhiteSpace(input.Symbol), "Invalid input symbol."); - Assert(input.Issuer != null && !input.Issuer.Value.IsNullOrEmpty(), "Invalid input issuer."); + Assert(input.Issuer != null && !input.Issuer.Value.IsNullOrEmpty(), "InBvalid input issuer."); Assert(input.Owner != null && !input.Owner.Value.IsNullOrEmpty(), "Invalid input owner."); var tokenInfo = State.TokenInfos[input.Symbol]; @@ -609,18 +609,21 @@ public override Empty ModifyTokenIssuerAndOwner(ModifyTokenIssuerAndOwnerInput i return new Empty(); } - public override Empty SetTokenIssuerAndOwnerModificationEnabled(BoolValue input) + public override Empty SetTokenIssuerAndOwnerModificationDisabled(SetTokenIssuerAndOwnerModificationDisabledInput input) { AssertSenderAddressWith(GetDefaultParliamentController().OwnerAddress); Assert(input != null, "Invalid input."); - State.TokenIssuerAndOwnerModificationDisabled.Value = input.Value; + State.TokenIssuerAndOwnerModificationDisabled.Value = input.Disabled; return new Empty(); } public override BoolValue GetTokenIssuerAndOwnerModificationEnabled(Empty input) { - return new BoolValue { Value = State.TokenIssuerAndOwnerModificationDisabled.Value }; + return new BoolValue + { + Value = !State.TokenIssuerAndOwnerModificationDisabled.Value + }; } } \ No newline at end of file diff --git a/protobuf/token_contract_impl.proto b/protobuf/token_contract_impl.proto index b0689af301..ac9840bce9 100644 --- a/protobuf/token_contract_impl.proto +++ b/protobuf/token_contract_impl.proto @@ -167,7 +167,7 @@ service TokenContractImpl { rpc ModifyTokenIssuerAndOwner(ModifyTokenIssuerAndOwnerInput) returns (google.protobuf.Empty) { } - rpc SetTokenIssuerAndOwnerModificationEnabled(google.protobuf.BoolValue) returns (google.protobuf.Empty) { + rpc SetTokenIssuerAndOwnerModificationDisabled(SetTokenIssuerAndOwnerModificationDisabledInput) returns (google.protobuf.Empty) { } rpc GetTokenIssuerAndOwnerModificationEnabled(google.protobuf.Empty) returns (google.protobuf.BoolValue) { @@ -432,3 +432,7 @@ message ModifyTokenIssuerAndOwnerInput { aelf.Address issuer = 2; aelf.Address owner = 3; } + +message SetTokenIssuerAndOwnerModificationDisabledInput{ + bool disabled = 1; +} \ No newline at end of file From c521d69e8a8e4a7cd3b9bbd93b14c12b55e076be Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 8 Dec 2023 22:01:12 +0800 Subject: [PATCH 12/41] fix: improve semantic accuracy --- contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs | 4 ++-- protobuf/token_contract_impl.proto | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index 24e44a6560..f44ba64bd8 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -609,12 +609,12 @@ public override Empty ModifyTokenIssuerAndOwner(ModifyTokenIssuerAndOwnerInput i return new Empty(); } - public override Empty SetTokenIssuerAndOwnerModificationDisabled(SetTokenIssuerAndOwnerModificationDisabledInput input) + public override Empty SetTokenIssuerAndOwnerModificationEnabled(SetTokenIssuerAndOwnerModificationEnabledInput input) { AssertSenderAddressWith(GetDefaultParliamentController().OwnerAddress); Assert(input != null, "Invalid input."); - State.TokenIssuerAndOwnerModificationDisabled.Value = input.Disabled; + State.TokenIssuerAndOwnerModificationDisabled.Value = !input.Enabled; return new Empty(); } diff --git a/protobuf/token_contract_impl.proto b/protobuf/token_contract_impl.proto index ac9840bce9..9a7779ec45 100644 --- a/protobuf/token_contract_impl.proto +++ b/protobuf/token_contract_impl.proto @@ -167,7 +167,7 @@ service TokenContractImpl { rpc ModifyTokenIssuerAndOwner(ModifyTokenIssuerAndOwnerInput) returns (google.protobuf.Empty) { } - rpc SetTokenIssuerAndOwnerModificationDisabled(SetTokenIssuerAndOwnerModificationDisabledInput) returns (google.protobuf.Empty) { + rpc SetTokenIssuerAndOwnerModificationEnabled(SetTokenIssuerAndOwnerModificationEnabledInput) returns (google.protobuf.Empty) { } rpc GetTokenIssuerAndOwnerModificationEnabled(google.protobuf.Empty) returns (google.protobuf.BoolValue) { @@ -433,6 +433,6 @@ message ModifyTokenIssuerAndOwnerInput { aelf.Address owner = 3; } -message SetTokenIssuerAndOwnerModificationDisabledInput{ - bool disabled = 1; +message SetTokenIssuerAndOwnerModificationEnabledInput{ + bool enabled = 1; } \ No newline at end of file From 1864d2e3a5c11911b91c6fe5c2ea426935f4e804 Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 8 Dec 2023 22:07:47 +0800 Subject: [PATCH 13/41] fix: typo --- contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index f44ba64bd8..5994b39bcf 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -594,7 +594,7 @@ public override Empty ModifyTokenIssuerAndOwner(ModifyTokenIssuerAndOwnerInput i { Assert(!State.TokenIssuerAndOwnerModificationDisabled.Value, "Set token issuer and owner disabled."); Assert(!string.IsNullOrWhiteSpace(input.Symbol), "Invalid input symbol."); - Assert(input.Issuer != null && !input.Issuer.Value.IsNullOrEmpty(), "InBvalid input issuer."); + Assert(input.Issuer != null && !input.Issuer.Value.IsNullOrEmpty(), "Invalid input issuer."); Assert(input.Owner != null && !input.Owner.Value.IsNullOrEmpty(), "Invalid input owner."); var tokenInfo = State.TokenInfos[input.Symbol]; From b403f2fcff6a8b8c7c6d3a274509896b352a3e9a Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 8 Dec 2023 22:38:26 +0800 Subject: [PATCH 14/41] fix: remove redundant message --- protobuf/token_contract.proto | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/protobuf/token_contract.proto b/protobuf/token_contract.proto index 5082990a99..d858bf5f1d 100644 --- a/protobuf/token_contract.proto +++ b/protobuf/token_contract.proto @@ -632,19 +632,6 @@ message TotalResourceTokensMap map value = 1; } -message ChangeTokenIssuerInput -{ - // The token symbol. - string symbol = 1; - // The new token issuer for change. - aelf.Address new_token_Issuer = 2; -} - -message ResetExternalInfoInput { - string symbol = 1; - ExternalInfo external_info = 2; -} - message StringList { repeated string value = 1; } @@ -853,12 +840,6 @@ message CrossChainReceived { aelf.Hash transfer_transaction_id =9; } -message ExternalInfoChanged { - option (aelf.is_event) = true; - string symbol = 1; - ExternalInfo external_info = 2; -} - message TransactionFeeDelegationAdded { option (aelf.is_event) = true; aelf.Address delegator = 1 [(aelf.is_indexed) = true]; From 3a0445d7c76329ee2166e738d5e6743cd52798b8 Mon Sep 17 00:00:00 2001 From: kevin Date: Sat, 9 Dec 2023 20:45:29 +0800 Subject: [PATCH 15/41] feat: add ut --- .../TokenContract_Actions.cs | 4 +- .../BVT/TokenApplicationTests.cs | 76 +++++++++++++++++++ 2 files changed, 78 insertions(+), 2 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index 5994b39bcf..249afc33ea 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -600,9 +600,9 @@ public override Empty ModifyTokenIssuerAndOwner(ModifyTokenIssuerAndOwnerInput i var tokenInfo = State.TokenInfos[input.Symbol]; Assert(tokenInfo != null, "Token is not found."); - Assert(tokenInfo.Owner == null, "Can only set token which does not have owner."); Assert(tokenInfo.Issuer == Context.Sender, "Only token issuer can set token issuer and owner."); - + Assert(tokenInfo.Owner == null, "Can only set token which does not have owner."); + tokenInfo.Issuer = input.Issuer; tokenInfo.Owner = input.Owner; diff --git a/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs b/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs index b4aa6e54d7..dba96e1023 100644 --- a/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs +++ b/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs @@ -1227,4 +1227,80 @@ await TokenContractStub.Issue.SendAsync(new IssueInput checkTokenInfo.IsBurnable.ShouldBe(createTokenInput.IsBurnable); checkTokenInfo.ExternalInfo.Value.ShouldBe(createTokenInput.ExternalInfo.Value); } + + [Fact] + public async Task TokenIssuerAndOwnerModification_Test() + { + var result = await TokenContractStub.ModifyTokenIssuerAndOwner.SendWithExceptionAsync(new ModifyTokenIssuerAndOwnerInput()); + result.TransactionResult.Error.ShouldContain("Invalid input symbol."); + + result = await TokenContractStub.ModifyTokenIssuerAndOwner.SendWithExceptionAsync(new ModifyTokenIssuerAndOwnerInput + { + Symbol = "TEST" + }); + result.TransactionResult.Error.ShouldContain("Invalid input issuer."); + + result = await TokenContractStub.ModifyTokenIssuerAndOwner.SendWithExceptionAsync(new ModifyTokenIssuerAndOwnerInput + { + Symbol = "TEST", + Issuer = DefaultAddress + }); + result.TransactionResult.Error.ShouldContain("Invalid input owner."); + + result = await TokenContractStub.ModifyTokenIssuerAndOwner.SendWithExceptionAsync(new ModifyTokenIssuerAndOwnerInput + { + Symbol = "TEST", + Issuer = DefaultAddress, + Owner = DefaultAddress + }); + result.TransactionResult.Error.ShouldContain("Token is not found."); + + result = await TokenContractStubUser.ModifyTokenIssuerAndOwner.SendWithExceptionAsync(new ModifyTokenIssuerAndOwnerInput + { + Symbol = DefaultSymbol, + Issuer = DefaultAddress, + Owner = DefaultAddress + }); + result.TransactionResult.Error.ShouldContain("Only token issuer can set token issuer and owner."); + + result = await TokenContractStub.ModifyTokenIssuerAndOwner.SendWithExceptionAsync(new ModifyTokenIssuerAndOwnerInput + { + Symbol = DefaultSymbol, + Issuer = DefaultAddress, + Owner = DefaultAddress + }); + result.TransactionResult.Error.ShouldContain("Can only set token which does not have owner."); + + var output = await TokenContractStub.GetTokenIssuerAndOwnerModificationEnabled.CallAsync(new Empty()); + output.Value.ShouldBeTrue(); + + result = await TokenContractStub.SetTokenIssuerAndOwnerModificationEnabled.SendWithExceptionAsync( + new SetTokenIssuerAndOwnerModificationEnabledInput + { + Enabled = false + }); + result.TransactionResult.Error.ShouldContain("Unauthorized behavior."); + + var defaultParliament = await ParliamentContractStub.GetDefaultOrganizationAddress.CallAsync(new Empty()); + var proposalId = await CreateProposalAsync(TokenContractAddress, + defaultParliament, nameof(TokenContractStub.SetTokenIssuerAndOwnerModificationEnabled), + new SetTokenIssuerAndOwnerModificationEnabledInput + { + Enabled = false + }); + await ApproveWithMinersAsync(proposalId); + await ParliamentContractStub.Release.SendAsync(proposalId); + + output = await TokenContractStub.GetTokenIssuerAndOwnerModificationEnabled.CallAsync(new Empty()); + output.Value.ShouldBeFalse(); + + result = await TokenContractStub.ModifyTokenIssuerAndOwner.SendWithExceptionAsync(new ModifyTokenIssuerAndOwnerInput + { + Symbol = DefaultSymbol, + Issuer = DefaultAddress, + Owner = DefaultAddress + }); + result.TransactionResult.Error.ShouldContain("Set token issuer and owner disabled."); + + } } \ No newline at end of file From 2bf43ff805b76c02fcef487b05a356558839e692 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Mon, 8 Jan 2024 23:42:52 +0800 Subject: [PATCH 16/41] Add ProfitContract state to limit the max profit receiving period count. --- .../AElf.Contracts.Profit/ProfitContract.cs | 32 ++++++++++---- .../ProfitContractConstants.cs | 2 +- .../ProfitContractState.cs | 2 + contract/AElf.Contracts.Profit/ViewMethods.cs | 8 ++++ protobuf/profit_contract.proto | 7 ++++ .../ProfitContractTestConstants.cs | 1 + .../ProfitTests.cs | 42 +++++++++++++++++++ 7 files changed, 86 insertions(+), 8 deletions(-) diff --git a/contract/AElf.Contracts.Profit/ProfitContract.cs b/contract/AElf.Contracts.Profit/ProfitContract.cs index c3c09b3aba..76d75044a6 100644 --- a/contract/AElf.Contracts.Profit/ProfitContract.cs +++ b/contract/AElf.Contracts.Profit/ProfitContract.cs @@ -807,6 +807,16 @@ public override Empty ClaimProfits(ClaimProfitsInput input) return new Empty(); } + public override Empty SetMaximumProfitReceivingPeriodCount(Int32Value input) + { + ValidateContractState(State.ParliamentContract, SmartContractConstants.ParliamentContractSystemName); + Assert(Context.Sender == State.ParliamentContract.GetDefaultOrganizationAddress.Call(new Empty()), + "No permission."); + Assert(input.Value > 0, "Invalid maximum profit receiving period count."); + State.MaximumProfitReceivingPeriodCount.Value = input.Value; + return new Empty(); + } + private Dictionary ProfitAllPeriods(Scheme scheme, ProfitDetail profitDetail, Address beneficiary, bool isView = false, string targetSymbol = null) { @@ -818,13 +828,12 @@ private Dictionary ProfitAllPeriods(Scheme scheme, ProfitDetail pr foreach (var symbol in symbols) { var totalAmount = 0L; - for (var period = profitDetail.LastProfitPeriod; - period <= (profitDetail.EndPeriod == long.MaxValue - ? Math.Min(scheme.CurrentPeriod - 1, - profitDetail.LastProfitPeriod.Add(ProfitContractConstants - .MaximumProfitReceivingPeriodCountOfOneTime)) - : Math.Min(scheme.CurrentPeriod - 1, profitDetail.EndPeriod)); - period++) + var maximumProfitReceivingPeriodCount = GetMaximumProfitReceivingPeriodCount(); + var targetPeriod = Math.Min(scheme.CurrentPeriod - 1, profitDetail.EndPeriod); + var maxProfitPeriod = profitDetail.EndPeriod == long.MaxValue + ? Math.Min(scheme.CurrentPeriod - 1, profitDetail.LastProfitPeriod.Add(maximumProfitReceivingPeriodCount)) + : Math.Min(targetPeriod, profitDetail.LastProfitPeriod.Add(maximumProfitReceivingPeriodCount)); + for (var period = profitDetail.LastProfitPeriod; period <= maxProfitPeriod; period++) { var periodToPrint = period; var detailToPrint = profitDetail; @@ -885,6 +894,15 @@ private Dictionary ProfitAllPeriods(Scheme scheme, ProfitDetail pr return profitsMap; } + + private int GetMaximumProfitReceivingPeriodCount() + { + var maxPeriodCount = State.MaximumProfitReceivingPeriodCount.Value; + maxPeriodCount = maxPeriodCount == 0 + ? ProfitContractConstants.DefaultMaximumProfitReceivingPeriodCountOfOneTime + : maxPeriodCount; + return maxPeriodCount; + } private void ValidateContractState(ContractReferenceState state, string contractSystemName) { diff --git a/contract/AElf.Contracts.Profit/ProfitContractConstants.cs b/contract/AElf.Contracts.Profit/ProfitContractConstants.cs index 59b80b971f..0aaf080aa5 100644 --- a/contract/AElf.Contracts.Profit/ProfitContractConstants.cs +++ b/contract/AElf.Contracts.Profit/ProfitContractConstants.cs @@ -6,5 +6,5 @@ public class ProfitContractConstants public const int DefaultProfitReceivingDuePeriodCount = 10; public const int MaximumProfitReceivingDuePeriodCount = 1024; public const int TokenAmountLimit = 5; - public const int MaximumProfitReceivingPeriodCountOfOneTime = 100; + public const int DefaultMaximumProfitReceivingPeriodCountOfOneTime = 100; } \ No newline at end of file diff --git a/contract/AElf.Contracts.Profit/ProfitContractState.cs b/contract/AElf.Contracts.Profit/ProfitContractState.cs index 7a525e3ff6..b0046830e0 100644 --- a/contract/AElf.Contracts.Profit/ProfitContractState.cs +++ b/contract/AElf.Contracts.Profit/ProfitContractState.cs @@ -17,4 +17,6 @@ public partial class ProfitContractState : ContractState public MappedState TransactionFees { get; set; } public SingletonState MethodFeeController { get; set; } + + public SingletonState MaximumProfitReceivingPeriodCount { get; set; } } \ No newline at end of file diff --git a/contract/AElf.Contracts.Profit/ViewMethods.cs b/contract/AElf.Contracts.Profit/ViewMethods.cs index 31d8c0d4d6..5780d28b2e 100644 --- a/contract/AElf.Contracts.Profit/ViewMethods.cs +++ b/contract/AElf.Contracts.Profit/ViewMethods.cs @@ -133,4 +133,12 @@ public override ReceivedProfitsMap GetProfitsMap(ClaimProfitsInput input) Value = { profitsDict } }; } + + public override Int32Value GetMaximumProfitReceivingPeriodCount(Empty input) + { + return new Int32Value + { + Value = GetMaximumProfitReceivingPeriodCount() + }; + } } \ No newline at end of file diff --git a/protobuf/profit_contract.proto b/protobuf/profit_contract.proto index 1326da62ff..0f84a870e5 100644 --- a/protobuf/profit_contract.proto +++ b/protobuf/profit_contract.proto @@ -63,6 +63,9 @@ service ProfitContract { // Reset the manager of a scheme. rpc ResetManager (ResetManagerInput) returns (google.protobuf.Empty) { } + + rpc SetMaximumProfitReceivingPeriodCount(google.protobuf.Int32Value) returns (google.protobuf.Empty) { + } // Get all schemes managed by the specified manager. rpc GetManagingSchemeIds (GetManagingSchemeIdsInput) returns (CreatedSchemeIds) { @@ -98,6 +101,10 @@ service ProfitContract { rpc GetProfitsMap (ClaimProfitsInput) returns (ReceivedProfitsMap) { option (aelf.is_view) = true; } + + rpc GetMaximumProfitReceivingPeriodCount(google.protobuf.Empty) returns (google.protobuf.Int32Value) { + option (aelf.is_view) = true; + } } message CreateSchemeInput { diff --git a/test/AElf.Contracts.Profit.Tests/ProfitContractTestConstants.cs b/test/AElf.Contracts.Profit.Tests/ProfitContractTestConstants.cs index d4f937117a..bda82297c0 100644 --- a/test/AElf.Contracts.Profit.Tests/ProfitContractTestConstants.cs +++ b/test/AElf.Contracts.Profit.Tests/ProfitContractTestConstants.cs @@ -5,4 +5,5 @@ public class ProfitContractTestConstants public const string NativeTokenSymbol = "ELF"; public const long NativeTokenTotalSupply = 1_000_000_000_00000000; public const int MaximumProfitReceivingDuePeriodCount = 1024; + public const int DefaultMaximumProfitReceivingPeriodCountOfOneTime = 100; } \ No newline at end of file diff --git a/test/AElf.Contracts.Profit.Tests/ProfitTests.cs b/test/AElf.Contracts.Profit.Tests/ProfitTests.cs index 34979069da..d1a847f207 100644 --- a/test/AElf.Contracts.Profit.Tests/ProfitTests.cs +++ b/test/AElf.Contracts.Profit.Tests/ProfitTests.cs @@ -3,6 +3,7 @@ using AElf.Contracts.MultiToken; using AElf.CSharp.Core; using AElf.Types; +using Google.Protobuf.WellKnownTypes; using Shouldly; using Xunit; @@ -1680,6 +1681,47 @@ await ProfitContractStub.ClaimProfits.SendAsync(new ClaimProfitsInput } } + + [Fact] + public async Task MaximumProfitReceivingPeriodCount_Test() + { + var maximumProfitReceivingPeriodCount = + await ProfitContractStub.GetMaximumProfitReceivingPeriodCount.CallAsync(new Empty()); + maximumProfitReceivingPeriodCount.Value.ShouldBe(ProfitContractTestConstants + .DefaultMaximumProfitReceivingPeriodCountOfOneTime); + var maxPeriodCount = 10; + var result = await ProfitContractStub.SetMaximumProfitReceivingPeriodCount.SendWithExceptionAsync(new Int32Value + { + Value = maxPeriodCount + }); + result.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); + result.TransactionResult.Error.ShouldContain("No permission"); + + var defaultOrganizationAddress = + await ParliamentContractStub.GetDefaultOrganizationAddress.CallAsync(new Empty()); + var proposalId = await CreateProposalAsync(ProfitContractAddress, + defaultOrganizationAddress, nameof(ProfitContractStub.SetMaximumProfitReceivingPeriodCount), new Int32Value + { + Value = 0 + }); + await ApproveWithMinersAsync(proposalId); + result = await ParliamentContractStub.Release.SendWithExceptionAsync(proposalId); + result.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); + result.TransactionResult.Error.ShouldContain("Invalid maximum profit receiving period count"); + + proposalId = await CreateProposalAsync(ProfitContractAddress, + defaultOrganizationAddress, nameof(ProfitContractStub.SetMaximumProfitReceivingPeriodCount), new Int32Value + { + Value = maxPeriodCount + }); + await ApproveWithMinersAsync(proposalId); + await ParliamentContractStub.Release.SendAsync(proposalId); + + maximumProfitReceivingPeriodCount = + await ProfitContractStub.GetMaximumProfitReceivingPeriodCount.CallAsync(new Empty()); + maximumProfitReceivingPeriodCount.Value.ShouldBe(maxPeriodCount); + } + private async Task ContributeProfits(Hash schemeId, long amount = 100) { await ProfitContractStub.ContributeProfits.SendAsync(new ContributeProfitsInput From b3011d117eb663a0b4199b3d6ddb57f68a8886f5 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Wed, 10 Jan 2024 22:22:35 +0800 Subject: [PATCH 17/41] Change the logic of calculating max profit receiving period count --- .../AElf.Contracts.Profit/ProfitContract.cs | 24 ++++++++++++------- contract/AElf.Contracts.Profit/ViewMethods.cs | 20 +++++++++------- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/contract/AElf.Contracts.Profit/ProfitContract.cs b/contract/AElf.Contracts.Profit/ProfitContract.cs index 76d75044a6..a75bc7d909 100644 --- a/contract/AElf.Contracts.Profit/ProfitContract.cs +++ b/contract/AElf.Contracts.Profit/ProfitContract.cs @@ -769,18 +769,19 @@ public override Empty ClaimProfits(ClaimProfitsInput input) Context.LogDebug(() => $"Profitable details: {profitableDetails.Aggregate("\n", (profit1, profit2) => profit1.ToString() + "\n" + profit2)}"); + var profitableDetailCount = + Math.Min(ProfitContractConstants.ProfitReceivingLimitForEachTime, profitableDetails.Count); + var maxProfitReceivingPeriodCount = GetMaximumPeriodCountForProfitableDetail(profitableDetailCount); // Only can get profit from last profit period to actual last period (profit.CurrentPeriod - 1), // because current period not released yet. - for (var i = 0; - i < Math.Min(ProfitContractConstants.ProfitReceivingLimitForEachTime, profitableDetails.Count); - i++) + for (var i = 0; i < profitableDetailCount; i++) { var profitDetail = profitableDetails[i]; if (profitDetail.LastProfitPeriod == 0) // This detail never performed profit before. profitDetail.LastProfitPeriod = profitDetail.StartPeriod; - ProfitAllPeriods(scheme, profitDetail, beneficiary); + ProfitAllPeriods(scheme, profitDetail, beneficiary, maxProfitReceivingPeriodCount); } var profitDetailsToRemove = profitableDetails @@ -807,6 +808,14 @@ public override Empty ClaimProfits(ClaimProfitsInput input) return new Empty(); } + private int GetMaximumPeriodCountForProfitableDetail(int profitableDetailCount) + { + var maxPeriodCount = GetMaximumProfitReceivingPeriodCount(); + return maxPeriodCount > profitableDetailCount && profitableDetailCount > 0 + ? maxPeriodCount.Div(profitableDetailCount) + : 1; + } + public override Empty SetMaximumProfitReceivingPeriodCount(Int32Value input) { ValidateContractState(State.ParliamentContract, SmartContractConstants.ParliamentContractSystemName); @@ -817,7 +826,7 @@ public override Empty SetMaximumProfitReceivingPeriodCount(Int32Value input) return new Empty(); } - private Dictionary ProfitAllPeriods(Scheme scheme, ProfitDetail profitDetail, Address beneficiary, + private Dictionary ProfitAllPeriods(Scheme scheme, ProfitDetail profitDetail, Address beneficiary, int maxProfitReceivingPeriodCount, bool isView = false, string targetSymbol = null) { var profitsMap = new Dictionary(); @@ -828,11 +837,10 @@ private Dictionary ProfitAllPeriods(Scheme scheme, ProfitDetail pr foreach (var symbol in symbols) { var totalAmount = 0L; - var maximumProfitReceivingPeriodCount = GetMaximumProfitReceivingPeriodCount(); var targetPeriod = Math.Min(scheme.CurrentPeriod - 1, profitDetail.EndPeriod); var maxProfitPeriod = profitDetail.EndPeriod == long.MaxValue - ? Math.Min(scheme.CurrentPeriod - 1, profitDetail.LastProfitPeriod.Add(maximumProfitReceivingPeriodCount)) - : Math.Min(targetPeriod, profitDetail.LastProfitPeriod.Add(maximumProfitReceivingPeriodCount)); + ? Math.Min(scheme.CurrentPeriod - 1, maxProfitReceivingPeriodCount) + : Math.Min(targetPeriod, profitDetail.LastProfitPeriod.Add(maxProfitReceivingPeriodCount)); for (var period = profitDetail.LastProfitPeriod; period <= maxProfitPeriod; period++) { var periodToPrint = period; diff --git a/contract/AElf.Contracts.Profit/ViewMethods.cs b/contract/AElf.Contracts.Profit/ViewMethods.cs index 5780d28b2e..154c9a49be 100644 --- a/contract/AElf.Contracts.Profit/ViewMethods.cs +++ b/contract/AElf.Contracts.Profit/ViewMethods.cs @@ -79,14 +79,16 @@ public override Int64Value GetProfitAmount(GetProfitAmountInput input) var amount = 0L; - for (var i = 0; - i < Math.Min(ProfitContractConstants.ProfitReceivingLimitForEachTime, availableDetails.Count); - i++) + var profitableDetailCount = + Math.Min(ProfitContractConstants.ProfitReceivingLimitForEachTime, availableDetails.Count); + var maxProfitReceivingPeriodCount = GetMaximumPeriodCountForProfitableDetail(profitableDetailCount); + + for (var i = 0;i < profitableDetailCount; i++) { var profitDetail = availableDetails[i]; if (profitDetail.LastProfitPeriod == 0) profitDetail.LastProfitPeriod = profitDetail.StartPeriod; - var profitsDict = ProfitAllPeriods(profitItem, profitDetail, beneficiary, true, + var profitsDict = ProfitAllPeriods(profitItem, profitDetail, beneficiary,maxProfitReceivingPeriodCount, true, input.Symbol); amount = amount.Add(profitsDict[input.Symbol]); } @@ -111,16 +113,18 @@ public override ReceivedProfitsMap GetProfitsMap(ClaimProfitsInput input) ? d.EndPeriod >= d.StartPeriod : d.EndPeriod >= d.LastProfitPeriod) ).ToList(); + + var profitableDetailCount = + Math.Min(ProfitContractConstants.ProfitReceivingLimitForEachTime, availableDetails.Count); + var maxProfitReceivingPeriodCount = GetMaximumPeriodCountForProfitableDetail(profitableDetailCount); var profitsDict = new Dictionary(); - for (var i = 0; - i < Math.Min(ProfitContractConstants.ProfitReceivingLimitForEachTime, availableDetails.Count); - i++) + for (var i = 0; i < profitableDetailCount; i++) { var profitDetail = availableDetails[i]; if (profitDetail.LastProfitPeriod == 0) profitDetail.LastProfitPeriod = profitDetail.StartPeriod; - var profitsDictForEachProfitDetail = ProfitAllPeriods(scheme, profitDetail, beneficiary, true); + var profitsDictForEachProfitDetail = ProfitAllPeriods(scheme, profitDetail, beneficiary, maxProfitReceivingPeriodCount,true); foreach (var kv in profitsDictForEachProfitDetail) if (profitsDict.ContainsKey(kv.Key)) profitsDict[kv.Key] = profitsDict[kv.Key].Add(kv.Value); From 4ed4215c5a27f2474de8a0ab0cc8c0f0f247178b Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Thu, 11 Jan 2024 16:19:40 +0800 Subject: [PATCH 18/41] Fix wrong period of profits. --- contract/AElf.Contracts.Profit/ProfitContract.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contract/AElf.Contracts.Profit/ProfitContract.cs b/contract/AElf.Contracts.Profit/ProfitContract.cs index a75bc7d909..002fe8540b 100644 --- a/contract/AElf.Contracts.Profit/ProfitContract.cs +++ b/contract/AElf.Contracts.Profit/ProfitContract.cs @@ -839,7 +839,7 @@ private Dictionary ProfitAllPeriods(Scheme scheme, ProfitDetail pr var totalAmount = 0L; var targetPeriod = Math.Min(scheme.CurrentPeriod - 1, profitDetail.EndPeriod); var maxProfitPeriod = profitDetail.EndPeriod == long.MaxValue - ? Math.Min(scheme.CurrentPeriod - 1, maxProfitReceivingPeriodCount) + ? Math.Min(scheme.CurrentPeriod - 1, profitDetail.LastProfitPeriod.Add(maxProfitReceivingPeriodCount)) : Math.Min(targetPeriod, profitDetail.LastProfitPeriod.Add(maxProfitReceivingPeriodCount)); for (var period = profitDetail.LastProfitPeriod; period <= maxProfitPeriod; period++) { From c05c2c331359bfabddc5d8c1947389280af1a35e Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Fri, 12 Jan 2024 18:30:15 +0800 Subject: [PATCH 19/41] Add comments for method 'GetMaximumPeriodCountForProfitableDetail' --- contract/AElf.Contracts.Profit/ProfitContract.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/contract/AElf.Contracts.Profit/ProfitContract.cs b/contract/AElf.Contracts.Profit/ProfitContract.cs index 002fe8540b..037e31ecb5 100644 --- a/contract/AElf.Contracts.Profit/ProfitContract.cs +++ b/contract/AElf.Contracts.Profit/ProfitContract.cs @@ -808,11 +808,27 @@ public override Empty ClaimProfits(ClaimProfitsInput input) return new Empty(); } + /// + /// This method calculates the maximum period count for a profitable detail + /// based on the number of profitable details and the maximum profit receiving period + /// For example: + /// If the number of profitable detail is 10 and the maximum profit receiving period is 100, + /// the maximum period count for a profitable detail will be 10. + /// If the number of profitable detail is 10 and the maximum profit receiving period is 5, + /// the maximum period count for a profitable detail will be 1. + /// + /// The number of profitable details + /// private int GetMaximumPeriodCountForProfitableDetail(int profitableDetailCount) { + // Get the maximum profit receiving period count var maxPeriodCount = GetMaximumProfitReceivingPeriodCount(); + // Check if the maximum period count is greater than the profitable detail count + // and if the profitable detail count is greater than 0 return maxPeriodCount > profitableDetailCount && profitableDetailCount > 0 + // Divide the maximum period count by the profitable detail count ? maxPeriodCount.Div(profitableDetailCount) + // If the conditions are not met, return 1 as the maximum period count : 1; } From 456d45e03b52782267609ff8421f85857da4fcfe Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Tue, 16 Jan 2024 17:25:06 +0800 Subject: [PATCH 20/41] Add one time claimable profit amount to return value --- .../AElf.Contracts.Profit/ProfitContract.cs | 2 +- contract/AElf.Contracts.Profit/ViewMethods.cs | 60 ++++++++++++++----- protobuf/profit_contract.proto | 13 +++- .../ProfitTests.cs | 18 ++++-- 4 files changed, 67 insertions(+), 26 deletions(-) diff --git a/contract/AElf.Contracts.Profit/ProfitContract.cs b/contract/AElf.Contracts.Profit/ProfitContract.cs index 037e31ecb5..a1f43d1af4 100644 --- a/contract/AElf.Contracts.Profit/ProfitContract.cs +++ b/contract/AElf.Contracts.Profit/ProfitContract.cs @@ -842,7 +842,7 @@ public override Empty SetMaximumProfitReceivingPeriodCount(Int32Value input) return new Empty(); } - private Dictionary ProfitAllPeriods(Scheme scheme, ProfitDetail profitDetail, Address beneficiary, int maxProfitReceivingPeriodCount, + private Dictionary ProfitAllPeriods(Scheme scheme, ProfitDetail profitDetail, Address beneficiary, long maxProfitReceivingPeriodCount, bool isView = false, string targetSymbol = null) { var profitsMap = new Dictionary(); diff --git a/contract/AElf.Contracts.Profit/ViewMethods.cs b/contract/AElf.Contracts.Profit/ViewMethods.cs index 154c9a49be..79de28d260 100644 --- a/contract/AElf.Contracts.Profit/ViewMethods.cs +++ b/contract/AElf.Contracts.Profit/ViewMethods.cs @@ -59,14 +59,14 @@ private Hash GeneratePeriodVirtualAddressFromHash(Hash schemeId, long period) return HashHelper.XorAndCompute(schemeId, HashHelper.ComputeFrom(period)); } - public override Int64Value GetProfitAmount(GetProfitAmountInput input) + public override GetProfitAmountOutput GetProfitAmount(GetProfitAmountInput input) { var profitItem = State.SchemeInfos[input.SchemeId]; Assert(profitItem != null, "Scheme not found."); var beneficiary = input.Beneficiary ?? Context.Sender; var profitDetails = State.ProfitDetailsMap[input.SchemeId][beneficiary]; - if (profitDetails == null) return new Int64Value { Value = 0 }; + if (profitDetails == null) return new GetProfitAmountOutput { AllProfitAmount = 0, OneTimeClaimableProfitAmount = 0 }; var profitVirtualAddress = Context.ConvertVirtualAddressToContractAddress(input.SchemeId); @@ -77,7 +77,8 @@ public override Int64Value GetProfitAmount(GetProfitAmountInput input) : d.EndPeriod >= d.LastProfitPeriod) ).ToList(); - var amount = 0L; + var allProfitAmount = 0L; + var claimableProfitAmount = 0L; var profitableDetailCount = Math.Min(ProfitContractConstants.ProfitReceivingLimitForEachTime, availableDetails.Count); @@ -88,12 +89,29 @@ public override Int64Value GetProfitAmount(GetProfitAmountInput input) var profitDetail = availableDetails[i]; if (profitDetail.LastProfitPeriod == 0) profitDetail.LastProfitPeriod = profitDetail.StartPeriod; - var profitsDict = ProfitAllPeriods(profitItem, profitDetail, beneficiary,maxProfitReceivingPeriodCount, true, + var totalProfitsDictForEachProfitDetail = ProfitAllPeriods(profitItem, profitDetail, beneficiary, + profitDetail.EndPeriod.Sub(profitDetail.LastProfitPeriod), true, input.Symbol); + allProfitAmount = + allProfitAmount.Add(totalProfitsDictForEachProfitDetail.TryGetValue(input.Symbol, out var value) + ? value + : 0); + if(i >= profitableDetailCount) continue; + var claimableProfitsDictForEachProfitDetail = ProfitAllPeriods(profitItem, profitDetail, beneficiary, + maxProfitReceivingPeriodCount, true, input.Symbol); - amount = amount.Add(profitsDict[input.Symbol]); + + claimableProfitAmount = + claimableProfitAmount.Add( + claimableProfitsDictForEachProfitDetail.TryGetValue(input.Symbol, out var claimableValue) + ? claimableValue + : 0); } - return new Int64Value { Value = amount }; + return new GetProfitAmountOutput + { + AllProfitAmount = allProfitAmount, + OneTimeClaimableProfitAmount = claimableProfitAmount + }; } public override ReceivedProfitsMap GetProfitsMap(ClaimProfitsInput input) @@ -118,25 +136,35 @@ public override ReceivedProfitsMap GetProfitsMap(ClaimProfitsInput input) Math.Min(ProfitContractConstants.ProfitReceivingLimitForEachTime, availableDetails.Count); var maxProfitReceivingPeriodCount = GetMaximumPeriodCountForProfitableDetail(profitableDetailCount); - var profitsDict = new Dictionary(); - for (var i = 0; i < profitableDetailCount; i++) + var allProfitsDict = new Dictionary(); + var claimableProfitsDict = new Dictionary(); + for (var i = 0; i < availableDetails.Count; i++) { var profitDetail = availableDetails[i]; if (profitDetail.LastProfitPeriod == 0) profitDetail.LastProfitPeriod = profitDetail.StartPeriod; - - var profitsDictForEachProfitDetail = ProfitAllPeriods(scheme, profitDetail, beneficiary, maxProfitReceivingPeriodCount,true); - foreach (var kv in profitsDictForEachProfitDetail) - if (profitsDict.ContainsKey(kv.Key)) - profitsDict[kv.Key] = profitsDict[kv.Key].Add(kv.Value); - else - profitsDict[kv.Key] = kv.Value; + + var totalProfitsDictForEachProfitDetail = ProfitAllPeriods(scheme, profitDetail, beneficiary, profitDetail.EndPeriod.Sub(profitDetail.LastProfitPeriod),true); + AddProfitToDict(allProfitsDict, totalProfitsDictForEachProfitDetail); + if(i >= profitableDetailCount) continue; + var claimableProfitsDictForEachProfitDetail = ProfitAllPeriods(scheme, profitDetail, beneficiary, maxProfitReceivingPeriodCount,true); + AddProfitToDict(claimableProfitsDict, claimableProfitsDictForEachProfitDetail); } return new ReceivedProfitsMap { - Value = { profitsDict } + AllProfitsMap = { allProfitsDict }, + OneTimeClaimableProfitsMap = { claimableProfitsDict } }; } + + private void AddProfitToDict(Dictionary profitsDict, Dictionary profitsToAdd) + { + foreach (var kv in profitsToAdd) + if (profitsDict.ContainsKey(kv.Key)) + profitsDict[kv.Key] = profitsDict[kv.Key].Add(kv.Value); + else + profitsDict[kv.Key] = kv.Value; + } public override Int32Value GetMaximumProfitReceivingPeriodCount(Empty input) { diff --git a/protobuf/profit_contract.proto b/protobuf/profit_contract.proto index 0f84a870e5..45b39a8886 100644 --- a/protobuf/profit_contract.proto +++ b/protobuf/profit_contract.proto @@ -93,7 +93,7 @@ service ProfitContract { } // Query the amount of profit according to token symbol. (up to 10 periods). - rpc GetProfitAmount (GetProfitAmountInput) returns (google.protobuf.Int64Value) { + rpc GetProfitAmount (GetProfitAmountInput) returns (GetProfitAmountOutput) { option (aelf.is_view) = true; } @@ -315,9 +315,16 @@ message GetProfitAmountInput { aelf.Address beneficiary = 3; } +message GetProfitAmountOutput{ + int64 all_profit_amount = 1; + int64 one_time_claimable_profit_amount = 2; +} + message ReceivedProfitsMap { - // The collection of profits received, token symbol -> amount. - map value = 1; + // The collection of all profits received, token symbol -> amount. + map all_profits_map = 1; + // The collection of claimable profits received, token symbol -> amount. + map one_time_claimable_profits_map = 2; } message SchemeCreated { diff --git a/test/AElf.Contracts.Profit.Tests/ProfitTests.cs b/test/AElf.Contracts.Profit.Tests/ProfitTests.cs index d1a847f207..69ce56e25f 100644 --- a/test/AElf.Contracts.Profit.Tests/ProfitTests.cs +++ b/test/AElf.Contracts.Profit.Tests/ProfitTests.cs @@ -1605,13 +1605,15 @@ await creator.DistributeProfits.SendAsync(new DistributeProfitsInput Symbol = tokenSymbol, SchemeId = schemeId }); - profitAmount.Value.ShouldBe(amount); + profitAmount.AllProfitAmount.ShouldBe(amount); + profitAmount.OneTimeClaimableProfitAmount.ShouldBe(amount); var profitMap = await ProfitContractStub.GetProfitsMap.CallAsync(new ClaimProfitsInput { SchemeId = schemeId, Beneficiary = receiver }); - profitMap.Value[tokenSymbol].ShouldBe(amount); + profitMap.AllProfitsMap[tokenSymbol].ShouldBe(amount); + profitMap.OneTimeClaimableProfitsMap[tokenSymbol].ShouldBe(amount); } // after claim @@ -1633,13 +1635,15 @@ await ProfitContractStub.ClaimProfits.SendAsync(new ClaimProfitsInput Symbol = tokenSymbol, SchemeId = schemeId }); - profitAmount.Value.ShouldBe(0); + profitAmount.AllProfitAmount.ShouldBe(0); + profitAmount.OneTimeClaimableProfitAmount.ShouldBe(0); var profitMap = await ProfitContractStub.GetProfitsMap.CallAsync(new ClaimProfitsInput { SchemeId = schemeId, Beneficiary = receiver }); - profitMap.Value.ShouldNotContainKey(tokenSymbol); + profitMap.AllProfitsMap.ShouldNotContainKey(tokenSymbol); + profitMap.OneTimeClaimableProfitsMap.ShouldNotContainKey(tokenSymbol); } //second time @@ -1659,13 +1663,15 @@ await creator.DistributeProfits.SendAsync(new DistributeProfitsInput Symbol = tokenSymbol, SchemeId = schemeId }); - profitAmount.Value.ShouldBe(amount); + profitAmount.AllProfitAmount.ShouldBe(amount); + profitAmount.OneTimeClaimableProfitAmount.ShouldBe(amount); var profitMap = await ProfitContractStub.GetProfitsMap.CallAsync(new ClaimProfitsInput { SchemeId = schemeId, Beneficiary = receiver }); - profitMap.Value[tokenSymbol].ShouldBe(amount); + profitMap.AllProfitsMap[tokenSymbol].ShouldBe(amount); + profitMap.OneTimeClaimableProfitsMap[tokenSymbol].ShouldBe(amount); await ProfitContractStub.ClaimProfits.SendAsync(new ClaimProfitsInput { From 89c8604a4d461d7edf6e77eb8ad593bbf9567b2b Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Wed, 17 Jan 2024 19:37:35 +0800 Subject: [PATCH 21/41] Fix build error --- contract/AElf.Contracts.Profit/ViewMethods.cs | 2 +- .../TokenHolderContract.cs | 2 +- .../BVT/ElectionTests.cs | 18 ++++++++--------- .../Full/MainChainMinerElection.cs | 2 +- .../Full/ReleaseProfitsFromTreasury.cs | 20 +++++++++---------- .../TokenHolderTests.cs | 6 +++--- 6 files changed, 25 insertions(+), 25 deletions(-) diff --git a/contract/AElf.Contracts.Profit/ViewMethods.cs b/contract/AElf.Contracts.Profit/ViewMethods.cs index 79de28d260..d5ba00e3a4 100644 --- a/contract/AElf.Contracts.Profit/ViewMethods.cs +++ b/contract/AElf.Contracts.Profit/ViewMethods.cs @@ -84,7 +84,7 @@ public override GetProfitAmountOutput GetProfitAmount(GetProfitAmountInput input Math.Min(ProfitContractConstants.ProfitReceivingLimitForEachTime, availableDetails.Count); var maxProfitReceivingPeriodCount = GetMaximumPeriodCountForProfitableDetail(profitableDetailCount); - for (var i = 0;i < profitableDetailCount; i++) + for (var i = 0;i < availableDetails.Count; i++) { var profitDetail = availableDetails[i]; if (profitDetail.LastProfitPeriod == 0) profitDetail.LastProfitPeriod = profitDetail.StartPeriod; diff --git a/contract/AElf.Contracts.TokenHolder/TokenHolderContract.cs b/contract/AElf.Contracts.TokenHolder/TokenHolderContract.cs index e2a60ab703..1c5a4e976a 100644 --- a/contract/AElf.Contracts.TokenHolder/TokenHolderContract.cs +++ b/contract/AElf.Contracts.TokenHolder/TokenHolderContract.cs @@ -271,7 +271,7 @@ public override ReceivedProfitsMap GetProfitsMap(ClaimProfitsInput input) }); return new ReceivedProfitsMap { - Value = { profitsMap.Value } + Value = { profitsMap.AllProfitsMap } }; } diff --git a/test/AElf.Contracts.Election.Tests/BVT/ElectionTests.cs b/test/AElf.Contracts.Election.Tests/BVT/ElectionTests.cs index 2ba363f220..50127529b8 100644 --- a/test/AElf.Contracts.Election.Tests/BVT/ElectionTests.cs +++ b/test/AElf.Contracts.Election.Tests/BVT/ElectionTests.cs @@ -653,7 +653,7 @@ await ElectionContractStub.GetElectorVoteWithRecords.CallAsync( SchemeId = ProfitItemsIds[ProfitType.CitizenWelfare], Symbol = EconomicContractsTestConstants.NativeTokenSymbol }); - profitAmountList.Add(profitAmount.Value); + profitAmountList.Add(profitAmount.AllProfitAmount); } weightList.First().Div(weightList.Last()).ShouldBe(profitAmountList.First().Div(profitAmountList.Last())); @@ -737,7 +737,7 @@ await ElectionContractStub.GetElectorVoteWithRecords.CallAsync( SchemeId = ProfitItemsIds[ProfitType.CitizenWelfare], Symbol = EconomicContractsTestConstants.NativeTokenSymbol }); - profitAmountList.Add(profitAmount.Value); + profitAmountList.Add(profitAmount.AllProfitAmount); } profitAmountList.First().ShouldBe(profitAmountList.Last()); @@ -851,8 +851,8 @@ await ProfitContractStub.GetProfitDetails.CallAsync(new GetProfitDetailsInput SchemeId = ProfitItemsIds[ProfitType.CitizenWelfare], Symbol = EconomicContractsTestConstants.NativeTokenSymbol }); - profitList.Add(profitAmount.Value); - allProfitAmount += profitAmount.Value; + profitList.Add(profitAmount.AllProfitAmount); + allProfitAmount += profitAmount.AllProfitAmount; } profitList.First().ShouldBe(profitList.Last()); @@ -888,7 +888,7 @@ await originVoterElectionStub.Withdraw.SendAsync( SchemeId = ProfitItemsIds[ProfitType.CitizenWelfare], Symbol = EconomicContractsTestConstants.NativeTokenSymbol }); - originProfitAmount.Value.ShouldBe(profitAmountList[term.Value - 1].Div(2)); + originProfitAmount.AllProfitAmount.ShouldBe(profitAmountList[term.Value - 1].Div(2)); var changeProfitAmount = await ProfitContractStub.GetProfitAmount.CallAsync(new GetProfitAmountInput { @@ -896,7 +896,7 @@ await originVoterElectionStub.Withdraw.SendAsync( SchemeId = ProfitItemsIds[ProfitType.CitizenWelfare], Symbol = EconomicContractsTestConstants.NativeTokenSymbol }); - changeProfitAmount.Value.ShouldBe(profitAmountList[term.Value - 1].Div(2) + changeProfitAmount.AllProfitAmount.ShouldBe(profitAmountList[term.Value - 1].Div(2) .Add(citizenAmount.AmountsMap[EconomicContractsTestConstants.NativeTokenSymbol])); } @@ -993,7 +993,7 @@ await ProfitContractStub.GetProfitDetails.CallAsync(new GetProfitDetailsInput var profitsClaimedEvents = claimResult.TransactionResult.Logs.Where(l => l.Name.Equals("ProfitsClaimed")) .Select(l => l.NonIndexed); var logEvents = profitsClaimedEvents.Select(e => ProfitsClaimed.Parser.ParseFrom(e)); - logEvents.Sum(e => e.Amount).ShouldBe(profitAmount.Value); + logEvents.Sum(e => e.Amount).ShouldBe(profitAmount.AllProfitAmount); } [Fact] @@ -1177,14 +1177,14 @@ await ElectionContractStub.GetElectorVoteWithRecords.CallAsync( SchemeId = ProfitItemsIds[ProfitType.CitizenWelfare], Symbol = EconomicContractsTestConstants.NativeTokenSymbol }); - profitAll += profitAmount.Value; + profitAll += profitAmount.AllProfitAmount; var claimResult = await voter.ClaimProfits.SendAsync(new ClaimProfitsInput { SchemeId = ProfitItemsIds[ProfitType.CitizenWelfare] }); claimResult.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); var afterBalance = await GetNativeTokenBalance(votersKeyPairs[i].PublicKey); - afterBalance.ShouldBe(balance.Add(profitAmount.Value)); + afterBalance.ShouldBe(balance.Add(profitAmount.AllProfitAmount)); } // diff --git a/test/AElf.Contracts.Election.Tests/Full/MainChainMinerElection.cs b/test/AElf.Contracts.Election.Tests/Full/MainChainMinerElection.cs index 6d51422854..f7585ea3b6 100644 --- a/test/AElf.Contracts.Election.Tests/Full/MainChainMinerElection.cs +++ b/test/AElf.Contracts.Election.Tests/Full/MainChainMinerElection.cs @@ -63,7 +63,7 @@ public async Task UserVote_And_GetProfitAmount_Test() { SchemeId = ProfitItemsIds[ProfitType.CitizenWelfare], Symbol = "ELF" - })).Value; + })).AllProfitAmount; profitBalance.ShouldBeGreaterThan(24000000); } } \ No newline at end of file diff --git a/test/AElf.Contracts.Election.Tests/Full/ReleaseProfitsFromTreasury.cs b/test/AElf.Contracts.Election.Tests/Full/ReleaseProfitsFromTreasury.cs index cbe677415b..e0a2c78445 100644 --- a/test/AElf.Contracts.Election.Tests/Full/ReleaseProfitsFromTreasury.cs +++ b/test/AElf.Contracts.Election.Tests/Full/ReleaseProfitsFromTreasury.cs @@ -387,7 +387,7 @@ public async Task CheckTreasuryProfitsDistribution_Test() { SchemeId = ProfitItemsIds[ProfitType.CitizenWelfare], Symbol = EconomicContractsTestConstants.NativeTokenSymbol - })).Value; + })).OneTimeClaimableProfitAmount; profitAmount.ShouldBeGreaterThan(0); var profitResult = await profitTester.ClaimProfits.SendAsync(new ClaimProfitsInput @@ -425,7 +425,7 @@ public async Task CheckTreasuryProfitsDistribution_Test() { SchemeId = ProfitItemsIds[ProfitType.BasicMinerReward], Symbol = "ELF" - })).Value; + })).AllProfitAmount; basicMinerRewardAmount.ShouldBeGreaterThan(0); //flexible Shares - 10% @@ -433,7 +433,7 @@ public async Task CheckTreasuryProfitsDistribution_Test() { SchemeId = ProfitItemsIds[ProfitType.FlexibleReward], Symbol = "ELF" - })).Value; + })).AllProfitAmount; flexibleRewardWeight.ShouldBeGreaterThan(0); //welcome Shares - 10% @@ -441,7 +441,7 @@ public async Task CheckTreasuryProfitsDistribution_Test() { SchemeId = ProfitItemsIds[ProfitType.WelcomeReward], Symbol = "ELF" - })).Value; + })).AllProfitAmount; welcomeBalance.ShouldBe(0); //backup Shares - 20% @@ -449,7 +449,7 @@ public async Task CheckTreasuryProfitsDistribution_Test() { SchemeId = ProfitItemsIds[ProfitType.BackupSubsidy], Symbol = "ELF" - })).Value; + })).AllProfitAmount; backupBalance.ShouldBeGreaterThan(0); //Profit all @@ -505,7 +505,7 @@ public async Task CheckTreasuryProfitsDistribution_Test() { SchemeId = ProfitItemsIds[ProfitType.BasicMinerReward], Symbol = "ELF" - })).Value; + })).AllProfitAmount; basicMinerRewardAmount.ShouldBeGreaterThan(0); //flexible Shares - 75% @@ -513,7 +513,7 @@ public async Task CheckTreasuryProfitsDistribution_Test() { SchemeId = ProfitItemsIds[ProfitType.FlexibleReward], Symbol = "ELF" - })).Value; + })).AllProfitAmount; flexibleRewardWeight.ShouldBe(0); //welcome Shares - 5% @@ -521,7 +521,7 @@ public async Task CheckTreasuryProfitsDistribution_Test() { SchemeId = ProfitItemsIds[ProfitType.WelcomeReward], Symbol = "ELF" - })).Value; + })).AllProfitAmount; welcomeBalance.ShouldBe(0); //backup Shares - 5% @@ -529,7 +529,7 @@ public async Task CheckTreasuryProfitsDistribution_Test() { SchemeId = ProfitItemsIds[ProfitType.BackupSubsidy], Symbol = "ELF" - })).Value; + })).AllProfitAmount; backupBalance.ShouldBeGreaterThan(0); //Profit all @@ -640,7 +640,7 @@ private async Task GetProfitAmount(ProfitType type) { SchemeId = ProfitItemsIds[type], Symbol = EconomicContractsTestConstants.NativeTokenSymbol - })).Value; + })).OneTimeClaimableProfitAmount; } private async Task GetReleasedAmount() diff --git a/test/AElf.Contracts.TokenHolder.Tests/TokenHolderTests.cs b/test/AElf.Contracts.TokenHolder.Tests/TokenHolderTests.cs index 68698e8177..9f21a6098f 100644 --- a/test/AElf.Contracts.TokenHolder.Tests/TokenHolderTests.cs +++ b/test/AElf.Contracts.TokenHolder.Tests/TokenHolderTests.cs @@ -409,9 +409,9 @@ await TokenHolderContractStub.RegisterForProfits.SendAsync(new RegisterForProfit Beneficiary = Starter, SchemeId = schemeId }); - profitMap.Value.Count.ShouldBe(2); - profitMap.Value.ContainsKey(nativeTokenSymbol).ShouldBeTrue(); - profitMap.Value[nativeTokenSymbol].ShouldBe(amount); + profitMap.AllProfitsMap.Count.ShouldBe(2); + profitMap.AllProfitsMap.ContainsKey(nativeTokenSymbol).ShouldBeTrue(); + profitMap.AllProfitsMap[nativeTokenSymbol].ShouldBe(amount); var schemeInfoInProfit = await ProfitContractStub.GetScheme.CallAsync(schemeId); var schemeInfoInTokenHolder = await TokenHolderContractStub.GetScheme.CallAsync(Starter); schemeInfoInProfit.CurrentPeriod.ShouldBe(2); From 39d1120e8cb069affbe1d2aeaaab86b89cfe8fbf Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Tue, 23 Jan 2024 13:54:31 +0800 Subject: [PATCH 22/41] Enhance ProfitContract with methods for getting all profit amount --- contract/AElf.Contracts.Profit/ViewMethods.cs | 86 +++++++++++++++++-- .../TokenHolderContract.cs | 2 +- protobuf/profit_contract.proto | 35 +++++++- .../BVT/ElectionTests.cs | 18 ++-- .../Full/MainChainMinerElection.cs | 2 +- .../Full/ReleaseProfitsFromTreasury.cs | 20 ++--- .../ProfitTests.cs | 71 ++++++++++++--- .../TokenHolderTests.cs | 6 +- 8 files changed, 197 insertions(+), 43 deletions(-) diff --git a/contract/AElf.Contracts.Profit/ViewMethods.cs b/contract/AElf.Contracts.Profit/ViewMethods.cs index d5ba00e3a4..c6f73db5f8 100644 --- a/contract/AElf.Contracts.Profit/ViewMethods.cs +++ b/contract/AElf.Contracts.Profit/ViewMethods.cs @@ -59,14 +59,56 @@ private Hash GeneratePeriodVirtualAddressFromHash(Hash schemeId, long period) return HashHelper.XorAndCompute(schemeId, HashHelper.ComputeFrom(period)); } - public override GetProfitAmountOutput GetProfitAmount(GetProfitAmountInput input) + public override Int64Value GetProfitAmount(GetProfitAmountInput input) { var profitItem = State.SchemeInfos[input.SchemeId]; Assert(profitItem != null, "Scheme not found."); var beneficiary = input.Beneficiary ?? Context.Sender; var profitDetails = State.ProfitDetailsMap[input.SchemeId][beneficiary]; - if (profitDetails == null) return new GetProfitAmountOutput { AllProfitAmount = 0, OneTimeClaimableProfitAmount = 0 }; + if (profitDetails == null) return new Int64Value { Value = 0 }; + + // ReSharper disable once PossibleNullReferenceException + var availableDetails = profitDetails.Details.Where(d => + d.LastProfitPeriod < profitItem.CurrentPeriod && (d.LastProfitPeriod == 0 + ? d.EndPeriod >= d.StartPeriod + : d.EndPeriod >= d.LastProfitPeriod) + ).ToList(); + + var amount = 0L; + + var profitableDetailCount = + Math.Min(ProfitContractConstants.ProfitReceivingLimitForEachTime, availableDetails.Count); + var maxProfitReceivingPeriodCount = GetMaximumPeriodCountForProfitableDetail(profitableDetailCount); + + for (var i = 0;i < profitableDetailCount; i++) + { + var profitDetail = availableDetails[i]; + if (profitDetail.LastProfitPeriod == 0) profitDetail.LastProfitPeriod = profitDetail.StartPeriod; + + var profitsDictForEachProfitDetail = ProfitAllPeriods(profitItem, profitDetail, beneficiary, + maxProfitReceivingPeriodCount, true, + input.Symbol); + + amount = amount.Add(profitsDictForEachProfitDetail.TryGetValue(input.Symbol, out var value) + ? value + : 0); + } + + return new Int64Value + { + Value = amount + }; + } + + public override GetAllProfitAmountOutput GetAllProfitAmount(GetAllProfitAmountInput input) + { + var profitItem = State.SchemeInfos[input.SchemeId]; + Assert(profitItem != null, "Scheme not found."); + var beneficiary = input.Beneficiary ?? Context.Sender; + var profitDetails = State.ProfitDetailsMap[input.SchemeId][beneficiary]; + + if (profitDetails == null) return new GetAllProfitAmountOutput { AllProfitAmount = 0, OneTimeClaimableProfitAmount = 0 }; var profitVirtualAddress = Context.ConvertVirtualAddressToContractAddress(input.SchemeId); @@ -107,7 +149,7 @@ public override GetProfitAmountOutput GetProfitAmount(GetProfitAmountInput input : 0); } - return new GetProfitAmountOutput + return new GetAllProfitAmountOutput { AllProfitAmount = allProfitAmount, OneTimeClaimableProfitAmount = claimableProfitAmount @@ -123,7 +165,41 @@ public override ReceivedProfitsMap GetProfitsMap(ClaimProfitsInput input) if (profitDetails == null) return new ReceivedProfitsMap(); - var profitVirtualAddress = Context.ConvertVirtualAddressToContractAddress(input.SchemeId); + // ReSharper disable once PossibleNullReferenceException + var availableDetails = profitDetails.Details.Where(d => + d.LastProfitPeriod < scheme.CurrentPeriod && (d.LastProfitPeriod == 0 + ? d.EndPeriod >= d.StartPeriod + : d.EndPeriod >= d.LastProfitPeriod) + ).ToList(); + + var profitableDetailCount = + Math.Min(ProfitContractConstants.ProfitReceivingLimitForEachTime, availableDetails.Count); + var maxProfitReceivingPeriodCount = GetMaximumPeriodCountForProfitableDetail(profitableDetailCount); + + var profitsDict = new Dictionary(); + for (var i = 0; i < profitableDetailCount; i++) + { + var profitDetail = availableDetails[i]; + if (profitDetail.LastProfitPeriod == 0) profitDetail.LastProfitPeriod = profitDetail.StartPeriod; + + var profitsDictForEachProfitDetail = ProfitAllPeriods(scheme, profitDetail, beneficiary, maxProfitReceivingPeriodCount,true); + AddProfitToDict(profitsDict, profitsDictForEachProfitDetail); + } + + return new ReceivedProfitsMap + { + Value = { profitsDict } + }; + } + + public override GetAllProfitsMapOutput GetAllProfitsMap(GetAllProfitsMapInput input) + { + var scheme = State.SchemeInfos[input.SchemeId]; + Assert(scheme != null, "Scheme not found."); + var beneficiary = input.Beneficiary ?? Context.Sender; + var profitDetails = State.ProfitDetailsMap[input.SchemeId][beneficiary]; + + if (profitDetails == null) return new GetAllProfitsMapOutput(); // ReSharper disable once PossibleNullReferenceException var availableDetails = profitDetails.Details.Where(d => @@ -150,7 +226,7 @@ public override ReceivedProfitsMap GetProfitsMap(ClaimProfitsInput input) AddProfitToDict(claimableProfitsDict, claimableProfitsDictForEachProfitDetail); } - return new ReceivedProfitsMap + return new GetAllProfitsMapOutput { AllProfitsMap = { allProfitsDict }, OneTimeClaimableProfitsMap = { claimableProfitsDict } diff --git a/contract/AElf.Contracts.TokenHolder/TokenHolderContract.cs b/contract/AElf.Contracts.TokenHolder/TokenHolderContract.cs index 1c5a4e976a..e2a60ab703 100644 --- a/contract/AElf.Contracts.TokenHolder/TokenHolderContract.cs +++ b/contract/AElf.Contracts.TokenHolder/TokenHolderContract.cs @@ -271,7 +271,7 @@ public override ReceivedProfitsMap GetProfitsMap(ClaimProfitsInput input) }); return new ReceivedProfitsMap { - Value = { profitsMap.AllProfitsMap } + Value = { profitsMap.Value } }; } diff --git a/protobuf/profit_contract.proto b/protobuf/profit_contract.proto index 45b39a8886..aac44f2d6a 100644 --- a/protobuf/profit_contract.proto +++ b/protobuf/profit_contract.proto @@ -93,7 +93,12 @@ service ProfitContract { } // Query the amount of profit according to token symbol. (up to 10 periods). - rpc GetProfitAmount (GetProfitAmountInput) returns (GetProfitAmountOutput) { + rpc GetProfitAmount (GetProfitAmountInput) returns (google.protobuf.Int64Value) { + option (aelf.is_view) = true; + } + + // Query the amount of profit according to token symbol. + rpc GetAllProfitAmount (GetAllProfitAmountInput) returns (GetAllProfitAmountOutput) { option (aelf.is_view) = true; } @@ -102,6 +107,11 @@ service ProfitContract { option (aelf.is_view) = true; } + // Query all profit. + rpc GetAllProfitsMap (GetAllProfitsMapInput) returns (GetAllProfitsMapOutput) { + option (aelf.is_view) = true; + } + rpc GetMaximumProfitReceivingPeriodCount(google.protobuf.Empty) returns (google.protobuf.Int32Value) { option (aelf.is_view) = true; } @@ -315,12 +325,33 @@ message GetProfitAmountInput { aelf.Address beneficiary = 3; } -message GetProfitAmountOutput{ +message GetAllProfitAmountInput { + // The scheme id. + aelf.Hash scheme_id = 1; + // The token symbol. + string symbol = 2; + // The beneficiary's address. + aelf.Address beneficiary = 3; +} + +message GetAllProfitAmountOutput{ int64 all_profit_amount = 1; int64 one_time_claimable_profit_amount = 2; } message ReceivedProfitsMap { + // The collection of profits received, token symbol -> amount. + map value = 1; +} + +message GetAllProfitsMapInput { + // The scheme id. + aelf.Hash scheme_id = 1; + // The address of beneficiary. + aelf.Address beneficiary = 2; +} + +message GetAllProfitsMapOutput { // The collection of all profits received, token symbol -> amount. map all_profits_map = 1; // The collection of claimable profits received, token symbol -> amount. diff --git a/test/AElf.Contracts.Election.Tests/BVT/ElectionTests.cs b/test/AElf.Contracts.Election.Tests/BVT/ElectionTests.cs index 50127529b8..2ba363f220 100644 --- a/test/AElf.Contracts.Election.Tests/BVT/ElectionTests.cs +++ b/test/AElf.Contracts.Election.Tests/BVT/ElectionTests.cs @@ -653,7 +653,7 @@ await ElectionContractStub.GetElectorVoteWithRecords.CallAsync( SchemeId = ProfitItemsIds[ProfitType.CitizenWelfare], Symbol = EconomicContractsTestConstants.NativeTokenSymbol }); - profitAmountList.Add(profitAmount.AllProfitAmount); + profitAmountList.Add(profitAmount.Value); } weightList.First().Div(weightList.Last()).ShouldBe(profitAmountList.First().Div(profitAmountList.Last())); @@ -737,7 +737,7 @@ await ElectionContractStub.GetElectorVoteWithRecords.CallAsync( SchemeId = ProfitItemsIds[ProfitType.CitizenWelfare], Symbol = EconomicContractsTestConstants.NativeTokenSymbol }); - profitAmountList.Add(profitAmount.AllProfitAmount); + profitAmountList.Add(profitAmount.Value); } profitAmountList.First().ShouldBe(profitAmountList.Last()); @@ -851,8 +851,8 @@ await ProfitContractStub.GetProfitDetails.CallAsync(new GetProfitDetailsInput SchemeId = ProfitItemsIds[ProfitType.CitizenWelfare], Symbol = EconomicContractsTestConstants.NativeTokenSymbol }); - profitList.Add(profitAmount.AllProfitAmount); - allProfitAmount += profitAmount.AllProfitAmount; + profitList.Add(profitAmount.Value); + allProfitAmount += profitAmount.Value; } profitList.First().ShouldBe(profitList.Last()); @@ -888,7 +888,7 @@ await originVoterElectionStub.Withdraw.SendAsync( SchemeId = ProfitItemsIds[ProfitType.CitizenWelfare], Symbol = EconomicContractsTestConstants.NativeTokenSymbol }); - originProfitAmount.AllProfitAmount.ShouldBe(profitAmountList[term.Value - 1].Div(2)); + originProfitAmount.Value.ShouldBe(profitAmountList[term.Value - 1].Div(2)); var changeProfitAmount = await ProfitContractStub.GetProfitAmount.CallAsync(new GetProfitAmountInput { @@ -896,7 +896,7 @@ await originVoterElectionStub.Withdraw.SendAsync( SchemeId = ProfitItemsIds[ProfitType.CitizenWelfare], Symbol = EconomicContractsTestConstants.NativeTokenSymbol }); - changeProfitAmount.AllProfitAmount.ShouldBe(profitAmountList[term.Value - 1].Div(2) + changeProfitAmount.Value.ShouldBe(profitAmountList[term.Value - 1].Div(2) .Add(citizenAmount.AmountsMap[EconomicContractsTestConstants.NativeTokenSymbol])); } @@ -993,7 +993,7 @@ await ProfitContractStub.GetProfitDetails.CallAsync(new GetProfitDetailsInput var profitsClaimedEvents = claimResult.TransactionResult.Logs.Where(l => l.Name.Equals("ProfitsClaimed")) .Select(l => l.NonIndexed); var logEvents = profitsClaimedEvents.Select(e => ProfitsClaimed.Parser.ParseFrom(e)); - logEvents.Sum(e => e.Amount).ShouldBe(profitAmount.AllProfitAmount); + logEvents.Sum(e => e.Amount).ShouldBe(profitAmount.Value); } [Fact] @@ -1177,14 +1177,14 @@ await ElectionContractStub.GetElectorVoteWithRecords.CallAsync( SchemeId = ProfitItemsIds[ProfitType.CitizenWelfare], Symbol = EconomicContractsTestConstants.NativeTokenSymbol }); - profitAll += profitAmount.AllProfitAmount; + profitAll += profitAmount.Value; var claimResult = await voter.ClaimProfits.SendAsync(new ClaimProfitsInput { SchemeId = ProfitItemsIds[ProfitType.CitizenWelfare] }); claimResult.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); var afterBalance = await GetNativeTokenBalance(votersKeyPairs[i].PublicKey); - afterBalance.ShouldBe(balance.Add(profitAmount.AllProfitAmount)); + afterBalance.ShouldBe(balance.Add(profitAmount.Value)); } // diff --git a/test/AElf.Contracts.Election.Tests/Full/MainChainMinerElection.cs b/test/AElf.Contracts.Election.Tests/Full/MainChainMinerElection.cs index f7585ea3b6..6d51422854 100644 --- a/test/AElf.Contracts.Election.Tests/Full/MainChainMinerElection.cs +++ b/test/AElf.Contracts.Election.Tests/Full/MainChainMinerElection.cs @@ -63,7 +63,7 @@ public async Task UserVote_And_GetProfitAmount_Test() { SchemeId = ProfitItemsIds[ProfitType.CitizenWelfare], Symbol = "ELF" - })).AllProfitAmount; + })).Value; profitBalance.ShouldBeGreaterThan(24000000); } } \ No newline at end of file diff --git a/test/AElf.Contracts.Election.Tests/Full/ReleaseProfitsFromTreasury.cs b/test/AElf.Contracts.Election.Tests/Full/ReleaseProfitsFromTreasury.cs index e0a2c78445..cbe677415b 100644 --- a/test/AElf.Contracts.Election.Tests/Full/ReleaseProfitsFromTreasury.cs +++ b/test/AElf.Contracts.Election.Tests/Full/ReleaseProfitsFromTreasury.cs @@ -387,7 +387,7 @@ public async Task CheckTreasuryProfitsDistribution_Test() { SchemeId = ProfitItemsIds[ProfitType.CitizenWelfare], Symbol = EconomicContractsTestConstants.NativeTokenSymbol - })).OneTimeClaimableProfitAmount; + })).Value; profitAmount.ShouldBeGreaterThan(0); var profitResult = await profitTester.ClaimProfits.SendAsync(new ClaimProfitsInput @@ -425,7 +425,7 @@ public async Task CheckTreasuryProfitsDistribution_Test() { SchemeId = ProfitItemsIds[ProfitType.BasicMinerReward], Symbol = "ELF" - })).AllProfitAmount; + })).Value; basicMinerRewardAmount.ShouldBeGreaterThan(0); //flexible Shares - 10% @@ -433,7 +433,7 @@ public async Task CheckTreasuryProfitsDistribution_Test() { SchemeId = ProfitItemsIds[ProfitType.FlexibleReward], Symbol = "ELF" - })).AllProfitAmount; + })).Value; flexibleRewardWeight.ShouldBeGreaterThan(0); //welcome Shares - 10% @@ -441,7 +441,7 @@ public async Task CheckTreasuryProfitsDistribution_Test() { SchemeId = ProfitItemsIds[ProfitType.WelcomeReward], Symbol = "ELF" - })).AllProfitAmount; + })).Value; welcomeBalance.ShouldBe(0); //backup Shares - 20% @@ -449,7 +449,7 @@ public async Task CheckTreasuryProfitsDistribution_Test() { SchemeId = ProfitItemsIds[ProfitType.BackupSubsidy], Symbol = "ELF" - })).AllProfitAmount; + })).Value; backupBalance.ShouldBeGreaterThan(0); //Profit all @@ -505,7 +505,7 @@ public async Task CheckTreasuryProfitsDistribution_Test() { SchemeId = ProfitItemsIds[ProfitType.BasicMinerReward], Symbol = "ELF" - })).AllProfitAmount; + })).Value; basicMinerRewardAmount.ShouldBeGreaterThan(0); //flexible Shares - 75% @@ -513,7 +513,7 @@ public async Task CheckTreasuryProfitsDistribution_Test() { SchemeId = ProfitItemsIds[ProfitType.FlexibleReward], Symbol = "ELF" - })).AllProfitAmount; + })).Value; flexibleRewardWeight.ShouldBe(0); //welcome Shares - 5% @@ -521,7 +521,7 @@ public async Task CheckTreasuryProfitsDistribution_Test() { SchemeId = ProfitItemsIds[ProfitType.WelcomeReward], Symbol = "ELF" - })).AllProfitAmount; + })).Value; welcomeBalance.ShouldBe(0); //backup Shares - 5% @@ -529,7 +529,7 @@ public async Task CheckTreasuryProfitsDistribution_Test() { SchemeId = ProfitItemsIds[ProfitType.BackupSubsidy], Symbol = "ELF" - })).AllProfitAmount; + })).Value; backupBalance.ShouldBeGreaterThan(0); //Profit all @@ -640,7 +640,7 @@ private async Task GetProfitAmount(ProfitType type) { SchemeId = ProfitItemsIds[type], Symbol = EconomicContractsTestConstants.NativeTokenSymbol - })).OneTimeClaimableProfitAmount; + })).Value; } private async Task GetReleasedAmount() diff --git a/test/AElf.Contracts.Profit.Tests/ProfitTests.cs b/test/AElf.Contracts.Profit.Tests/ProfitTests.cs index 69ce56e25f..601fd7ccf6 100644 --- a/test/AElf.Contracts.Profit.Tests/ProfitTests.cs +++ b/test/AElf.Contracts.Profit.Tests/ProfitTests.cs @@ -1605,15 +1605,32 @@ await creator.DistributeProfits.SendAsync(new DistributeProfitsInput Symbol = tokenSymbol, SchemeId = schemeId }); - profitAmount.AllProfitAmount.ShouldBe(amount); - profitAmount.OneTimeClaimableProfitAmount.ShouldBe(amount); + + profitAmount.Value.ShouldBe(amount); + + var allProfitAmount = await ProfitContractStub.GetAllProfitAmount.CallAsync(new GetAllProfitAmountInput + { + Beneficiary = receiver, + Symbol = tokenSymbol, + SchemeId = schemeId + }); + allProfitAmount.AllProfitAmount.ShouldBe(amount); + allProfitAmount.OneTimeClaimableProfitAmount.ShouldBe(amount); var profitMap = await ProfitContractStub.GetProfitsMap.CallAsync(new ClaimProfitsInput { SchemeId = schemeId, Beneficiary = receiver }); - profitMap.AllProfitsMap[tokenSymbol].ShouldBe(amount); - profitMap.OneTimeClaimableProfitsMap[tokenSymbol].ShouldBe(amount); + + profitMap.Value[tokenSymbol].ShouldBe(amount); + + var allProfitMap = await ProfitContractStub.GetAllProfitsMap.CallAsync(new GetAllProfitsMapInput + { + SchemeId = schemeId, + Beneficiary = receiver + }); + allProfitMap.AllProfitsMap[tokenSymbol].ShouldBe(amount); + allProfitMap.OneTimeClaimableProfitsMap[tokenSymbol].ShouldBe(amount); } // after claim @@ -1635,15 +1652,31 @@ await ProfitContractStub.ClaimProfits.SendAsync(new ClaimProfitsInput Symbol = tokenSymbol, SchemeId = schemeId }); - profitAmount.AllProfitAmount.ShouldBe(0); - profitAmount.OneTimeClaimableProfitAmount.ShouldBe(0); + profitAmount.Value.ShouldBe(0); + + var allProfitAmount = await ProfitContractStub.GetAllProfitAmount.CallAsync(new GetAllProfitAmountInput + { + Beneficiary = receiver, + Symbol = tokenSymbol, + SchemeId = schemeId + }); + allProfitAmount.AllProfitAmount.ShouldBe(0); + allProfitAmount.OneTimeClaimableProfitAmount.ShouldBe(0); + var profitMap = await ProfitContractStub.GetProfitsMap.CallAsync(new ClaimProfitsInput { SchemeId = schemeId, Beneficiary = receiver }); - profitMap.AllProfitsMap.ShouldNotContainKey(tokenSymbol); - profitMap.OneTimeClaimableProfitsMap.ShouldNotContainKey(tokenSymbol); + profitMap.Value.ShouldNotContainKey(tokenSymbol); + + var allProfitMap = await ProfitContractStub.GetAllProfitsMap.CallAsync(new GetAllProfitsMapInput + { + SchemeId = schemeId, + Beneficiary = receiver + }); + allProfitMap.AllProfitsMap.ShouldNotContainKey(tokenSymbol); + allProfitMap.OneTimeClaimableProfitsMap.ShouldNotContainKey(tokenSymbol); } //second time @@ -1663,15 +1696,29 @@ await creator.DistributeProfits.SendAsync(new DistributeProfitsInput Symbol = tokenSymbol, SchemeId = schemeId }); - profitAmount.AllProfitAmount.ShouldBe(amount); - profitAmount.OneTimeClaimableProfitAmount.ShouldBe(amount); + profitAmount.Value.ShouldBe(amount); + var allProfitAmount = await ProfitContractStub.GetAllProfitAmount.CallAsync(new GetAllProfitAmountInput + { + Beneficiary = receiver, + Symbol = tokenSymbol, + SchemeId = schemeId + }); + allProfitAmount.AllProfitAmount.ShouldBe(amount); + allProfitAmount.OneTimeClaimableProfitAmount.ShouldBe(amount); var profitMap = await ProfitContractStub.GetProfitsMap.CallAsync(new ClaimProfitsInput { SchemeId = schemeId, Beneficiary = receiver }); - profitMap.AllProfitsMap[tokenSymbol].ShouldBe(amount); - profitMap.OneTimeClaimableProfitsMap[tokenSymbol].ShouldBe(amount); + profitMap.Value[tokenSymbol].ShouldBe(amount); + + var allProfitMap = await ProfitContractStub.GetAllProfitsMap.CallAsync(new GetAllProfitsMapInput + { + SchemeId = schemeId, + Beneficiary = receiver + }); + allProfitMap.AllProfitsMap[tokenSymbol].ShouldBe(amount); + allProfitMap.OneTimeClaimableProfitsMap[tokenSymbol].ShouldBe(amount); await ProfitContractStub.ClaimProfits.SendAsync(new ClaimProfitsInput { diff --git a/test/AElf.Contracts.TokenHolder.Tests/TokenHolderTests.cs b/test/AElf.Contracts.TokenHolder.Tests/TokenHolderTests.cs index 9f21a6098f..68698e8177 100644 --- a/test/AElf.Contracts.TokenHolder.Tests/TokenHolderTests.cs +++ b/test/AElf.Contracts.TokenHolder.Tests/TokenHolderTests.cs @@ -409,9 +409,9 @@ await TokenHolderContractStub.RegisterForProfits.SendAsync(new RegisterForProfit Beneficiary = Starter, SchemeId = schemeId }); - profitMap.AllProfitsMap.Count.ShouldBe(2); - profitMap.AllProfitsMap.ContainsKey(nativeTokenSymbol).ShouldBeTrue(); - profitMap.AllProfitsMap[nativeTokenSymbol].ShouldBe(amount); + profitMap.Value.Count.ShouldBe(2); + profitMap.Value.ContainsKey(nativeTokenSymbol).ShouldBeTrue(); + profitMap.Value[nativeTokenSymbol].ShouldBe(amount); var schemeInfoInProfit = await ProfitContractStub.GetScheme.CallAsync(schemeId); var schemeInfoInTokenHolder = await TokenHolderContractStub.GetScheme.CallAsync(Starter); schemeInfoInProfit.CurrentPeriod.ShouldBe(2); From 06d1e434371982fca963b6d89ddab00bf16bb743 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Tue, 23 Jan 2024 15:33:04 +0800 Subject: [PATCH 23/41] Remove duplicate codes --- contract/AElf.Contracts.Profit/ViewMethods.cs | 98 +++++-------------- 1 file changed, 24 insertions(+), 74 deletions(-) diff --git a/contract/AElf.Contracts.Profit/ViewMethods.cs b/contract/AElf.Contracts.Profit/ViewMethods.cs index c6f73db5f8..99a4d666d7 100644 --- a/contract/AElf.Contracts.Profit/ViewMethods.cs +++ b/contract/AElf.Contracts.Profit/ViewMethods.cs @@ -61,57 +61,28 @@ private Hash GeneratePeriodVirtualAddressFromHash(Hash schemeId, long period) public override Int64Value GetProfitAmount(GetProfitAmountInput input) { - var profitItem = State.SchemeInfos[input.SchemeId]; - Assert(profitItem != null, "Scheme not found."); - var beneficiary = input.Beneficiary ?? Context.Sender; - var profitDetails = State.ProfitDetailsMap[input.SchemeId][beneficiary]; - - if (profitDetails == null) return new Int64Value { Value = 0 }; - - // ReSharper disable once PossibleNullReferenceException - var availableDetails = profitDetails.Details.Where(d => - d.LastProfitPeriod < profitItem.CurrentPeriod && (d.LastProfitPeriod == 0 - ? d.EndPeriod >= d.StartPeriod - : d.EndPeriod >= d.LastProfitPeriod) - ).ToList(); - - var amount = 0L; - - var profitableDetailCount = - Math.Min(ProfitContractConstants.ProfitReceivingLimitForEachTime, availableDetails.Count); - var maxProfitReceivingPeriodCount = GetMaximumPeriodCountForProfitableDetail(profitableDetailCount); - - for (var i = 0;i < profitableDetailCount; i++) - { - var profitDetail = availableDetails[i]; - if (profitDetail.LastProfitPeriod == 0) profitDetail.LastProfitPeriod = profitDetail.StartPeriod; - - var profitsDictForEachProfitDetail = ProfitAllPeriods(profitItem, profitDetail, beneficiary, - maxProfitReceivingPeriodCount, true, - input.Symbol); - - amount = amount.Add(profitsDictForEachProfitDetail.TryGetValue(input.Symbol, out var value) - ? value - : 0); - } + var output = GetAllProfitAmount(input.SchemeId, input.Symbol, input.Beneficiary); return new Int64Value { - Value = amount + Value = output.OneTimeClaimableProfitAmount }; } public override GetAllProfitAmountOutput GetAllProfitAmount(GetAllProfitAmountInput input) { - var profitItem = State.SchemeInfos[input.SchemeId]; + return GetAllProfitAmount(input.SchemeId, input.Symbol, input.Beneficiary); + } + + private GetAllProfitAmountOutput GetAllProfitAmount(Hash schemaId, string symbol, Address beneficiary) + { + var profitItem = State.SchemeInfos[schemaId]; Assert(profitItem != null, "Scheme not found."); - var beneficiary = input.Beneficiary ?? Context.Sender; - var profitDetails = State.ProfitDetailsMap[input.SchemeId][beneficiary]; + beneficiary = beneficiary ?? Context.Sender; + var profitDetails = State.ProfitDetailsMap[schemaId][beneficiary]; if (profitDetails == null) return new GetAllProfitAmountOutput { AllProfitAmount = 0, OneTimeClaimableProfitAmount = 0 }; - var profitVirtualAddress = Context.ConvertVirtualAddressToContractAddress(input.SchemeId); - // ReSharper disable once PossibleNullReferenceException var availableDetails = profitDetails.Details.Where(d => d.LastProfitPeriod < profitItem.CurrentPeriod && (d.LastProfitPeriod == 0 @@ -132,19 +103,19 @@ public override GetAllProfitAmountOutput GetAllProfitAmount(GetAllProfitAmountIn if (profitDetail.LastProfitPeriod == 0) profitDetail.LastProfitPeriod = profitDetail.StartPeriod; var totalProfitsDictForEachProfitDetail = ProfitAllPeriods(profitItem, profitDetail, beneficiary, - profitDetail.EndPeriod.Sub(profitDetail.LastProfitPeriod), true, input.Symbol); + profitDetail.EndPeriod.Sub(profitDetail.LastProfitPeriod), true, symbol); allProfitAmount = - allProfitAmount.Add(totalProfitsDictForEachProfitDetail.TryGetValue(input.Symbol, out var value) + allProfitAmount.Add(totalProfitsDictForEachProfitDetail.TryGetValue(symbol, out var value) ? value : 0); if(i >= profitableDetailCount) continue; var claimableProfitsDictForEachProfitDetail = ProfitAllPeriods(profitItem, profitDetail, beneficiary, maxProfitReceivingPeriodCount, true, - input.Symbol); + symbol); claimableProfitAmount = claimableProfitAmount.Add( - claimableProfitsDictForEachProfitDetail.TryGetValue(input.Symbol, out var claimableValue) + claimableProfitsDictForEachProfitDetail.TryGetValue(symbol, out var claimableValue) ? claimableValue : 0); } @@ -158,46 +129,25 @@ public override GetAllProfitAmountOutput GetAllProfitAmount(GetAllProfitAmountIn public override ReceivedProfitsMap GetProfitsMap(ClaimProfitsInput input) { - var scheme = State.SchemeInfos[input.SchemeId]; - Assert(scheme != null, "Scheme not found."); - var beneficiary = input.Beneficiary ?? Context.Sender; - var profitDetails = State.ProfitDetailsMap[input.SchemeId][beneficiary]; - - if (profitDetails == null) return new ReceivedProfitsMap(); - - // ReSharper disable once PossibleNullReferenceException - var availableDetails = profitDetails.Details.Where(d => - d.LastProfitPeriod < scheme.CurrentPeriod && (d.LastProfitPeriod == 0 - ? d.EndPeriod >= d.StartPeriod - : d.EndPeriod >= d.LastProfitPeriod) - ).ToList(); - - var profitableDetailCount = - Math.Min(ProfitContractConstants.ProfitReceivingLimitForEachTime, availableDetails.Count); - var maxProfitReceivingPeriodCount = GetMaximumPeriodCountForProfitableDetail(profitableDetailCount); - - var profitsDict = new Dictionary(); - for (var i = 0; i < profitableDetailCount; i++) - { - var profitDetail = availableDetails[i]; - if (profitDetail.LastProfitPeriod == 0) profitDetail.LastProfitPeriod = profitDetail.StartPeriod; - - var profitsDictForEachProfitDetail = ProfitAllPeriods(scheme, profitDetail, beneficiary, maxProfitReceivingPeriodCount,true); - AddProfitToDict(profitsDict, profitsDictForEachProfitDetail); - } + var output = GetAllProfitsMap(input.SchemeId, input.Beneficiary); return new ReceivedProfitsMap { - Value = { profitsDict } + Value = { output.OneTimeClaimableProfitsMap } }; } public override GetAllProfitsMapOutput GetAllProfitsMap(GetAllProfitsMapInput input) { - var scheme = State.SchemeInfos[input.SchemeId]; + return GetAllProfitsMap(input.SchemeId, input.Beneficiary); + } + + private GetAllProfitsMapOutput GetAllProfitsMap(Hash schemeId, Address beneficiary) + { + var scheme = State.SchemeInfos[schemeId]; Assert(scheme != null, "Scheme not found."); - var beneficiary = input.Beneficiary ?? Context.Sender; - var profitDetails = State.ProfitDetailsMap[input.SchemeId][beneficiary]; + beneficiary = beneficiary ?? Context.Sender; + var profitDetails = State.ProfitDetailsMap[schemeId][beneficiary]; if (profitDetails == null) return new GetAllProfitsMapOutput(); From 07adf842369c1f721757f1f9ce8b6e35fc11e13e Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Tue, 23 Jan 2024 15:38:40 +0800 Subject: [PATCH 24/41] Change to return all profit amount --- contract/AElf.Contracts.Profit/ViewMethods.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contract/AElf.Contracts.Profit/ViewMethods.cs b/contract/AElf.Contracts.Profit/ViewMethods.cs index 99a4d666d7..20fc498b11 100644 --- a/contract/AElf.Contracts.Profit/ViewMethods.cs +++ b/contract/AElf.Contracts.Profit/ViewMethods.cs @@ -65,7 +65,7 @@ public override Int64Value GetProfitAmount(GetProfitAmountInput input) return new Int64Value { - Value = output.OneTimeClaimableProfitAmount + Value = output.AllProfitAmount }; } @@ -133,7 +133,7 @@ public override ReceivedProfitsMap GetProfitsMap(ClaimProfitsInput input) return new ReceivedProfitsMap { - Value = { output.OneTimeClaimableProfitsMap } + Value = { output.AllProfitsMap } }; } From d32538f82566d16768ecf5062f95f36836ac9207 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Tue, 23 Jan 2024 20:32:09 +0800 Subject: [PATCH 25/41] Fix unit test error --- .../Full/ReleaseProfitsFromTreasury.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/AElf.Contracts.Election.Tests/Full/ReleaseProfitsFromTreasury.cs b/test/AElf.Contracts.Election.Tests/Full/ReleaseProfitsFromTreasury.cs index cbe677415b..96657c3cd2 100644 --- a/test/AElf.Contracts.Election.Tests/Full/ReleaseProfitsFromTreasury.cs +++ b/test/AElf.Contracts.Election.Tests/Full/ReleaseProfitsFromTreasury.cs @@ -383,11 +383,11 @@ public async Task CheckTreasuryProfitsDistribution_Test() })).Balance; var profitTester = GetProfitContractTester(VoterKeyPairs[0]); - var profitAmount = (await profitTester.GetProfitAmount.CallAsync(new GetProfitAmountInput + var profitAmount = (await profitTester.GetAllProfitAmount.CallAsync(new GetAllProfitAmountInput { SchemeId = ProfitItemsIds[ProfitType.CitizenWelfare], Symbol = EconomicContractsTestConstants.NativeTokenSymbol - })).Value; + })).OneTimeClaimableProfitAmount; profitAmount.ShouldBeGreaterThan(0); var profitResult = await profitTester.ClaimProfits.SendAsync(new ClaimProfitsInput @@ -636,11 +636,11 @@ private async Task GetProfitAmount(ProfitType type) break; } - return (await stub.GetProfitAmount.CallAsync(new GetProfitAmountInput + return (await stub.GetAllProfitAmount.CallAsync(new GetAllProfitAmountInput { SchemeId = ProfitItemsIds[type], Symbol = EconomicContractsTestConstants.NativeTokenSymbol - })).Value; + })).OneTimeClaimableProfitAmount; } private async Task GetReleasedAmount() From ef9ae577bd46828fb0d5ffa020cb02f5a84d8127 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Wed, 24 Jan 2024 18:52:18 +0800 Subject: [PATCH 26/41] Remove duplicate codes --- contract/AElf.Contracts.Profit/ViewMethods.cs | 74 ++++--------------- 1 file changed, 16 insertions(+), 58 deletions(-) diff --git a/contract/AElf.Contracts.Profit/ViewMethods.cs b/contract/AElf.Contracts.Profit/ViewMethods.cs index 20fc498b11..190204c3c5 100644 --- a/contract/AElf.Contracts.Profit/ViewMethods.cs +++ b/contract/AElf.Contracts.Profit/ViewMethods.cs @@ -61,79 +61,37 @@ private Hash GeneratePeriodVirtualAddressFromHash(Hash schemeId, long period) public override Int64Value GetProfitAmount(GetProfitAmountInput input) { - var output = GetAllProfitAmount(input.SchemeId, input.Symbol, input.Beneficiary); + var allProfitsMapResult = GetAllProfitsMap(input.SchemeId, input.Beneficiary, input.Symbol); return new Int64Value { - Value = output.AllProfitAmount + Value = allProfitsMapResult.AllProfitsMap.TryGetValue(input.Symbol, out var value) ? value : 0 }; } public override GetAllProfitAmountOutput GetAllProfitAmount(GetAllProfitAmountInput input) { - return GetAllProfitAmount(input.SchemeId, input.Symbol, input.Beneficiary); - } - - private GetAllProfitAmountOutput GetAllProfitAmount(Hash schemaId, string symbol, Address beneficiary) - { - var profitItem = State.SchemeInfos[schemaId]; - Assert(profitItem != null, "Scheme not found."); - beneficiary = beneficiary ?? Context.Sender; - var profitDetails = State.ProfitDetailsMap[schemaId][beneficiary]; - - if (profitDetails == null) return new GetAllProfitAmountOutput { AllProfitAmount = 0, OneTimeClaimableProfitAmount = 0 }; - - // ReSharper disable once PossibleNullReferenceException - var availableDetails = profitDetails.Details.Where(d => - d.LastProfitPeriod < profitItem.CurrentPeriod && (d.LastProfitPeriod == 0 - ? d.EndPeriod >= d.StartPeriod - : d.EndPeriod >= d.LastProfitPeriod) - ).ToList(); - - var allProfitAmount = 0L; - var claimableProfitAmount = 0L; - - var profitableDetailCount = - Math.Min(ProfitContractConstants.ProfitReceivingLimitForEachTime, availableDetails.Count); - var maxProfitReceivingPeriodCount = GetMaximumPeriodCountForProfitableDetail(profitableDetailCount); - - for (var i = 0;i < availableDetails.Count; i++) - { - var profitDetail = availableDetails[i]; - if (profitDetail.LastProfitPeriod == 0) profitDetail.LastProfitPeriod = profitDetail.StartPeriod; - - var totalProfitsDictForEachProfitDetail = ProfitAllPeriods(profitItem, profitDetail, beneficiary, - profitDetail.EndPeriod.Sub(profitDetail.LastProfitPeriod), true, symbol); - allProfitAmount = - allProfitAmount.Add(totalProfitsDictForEachProfitDetail.TryGetValue(symbol, out var value) - ? value - : 0); - if(i >= profitableDetailCount) continue; - var claimableProfitsDictForEachProfitDetail = ProfitAllPeriods(profitItem, profitDetail, beneficiary, - maxProfitReceivingPeriodCount, true, - symbol); - - claimableProfitAmount = - claimableProfitAmount.Add( - claimableProfitsDictForEachProfitDetail.TryGetValue(symbol, out var claimableValue) - ? claimableValue - : 0); - } - + var allProfitsMapResult = GetAllProfitsMap(input.SchemeId, input.Beneficiary, input.Symbol); return new GetAllProfitAmountOutput { - AllProfitAmount = allProfitAmount, - OneTimeClaimableProfitAmount = claimableProfitAmount + AllProfitAmount = allProfitsMapResult.AllProfitsMap.TryGetValue(input.Symbol, out var allProfitAmount) + ? allProfitAmount + : 0, + OneTimeClaimableProfitAmount = + allProfitsMapResult.OneTimeClaimableProfitsMap.TryGetValue(input.Symbol, + out var oneTimeClaimableProfitAmount) + ? oneTimeClaimableProfitAmount + : 0 }; } public override ReceivedProfitsMap GetProfitsMap(ClaimProfitsInput input) { - var output = GetAllProfitsMap(input.SchemeId, input.Beneficiary); + var allProfitsMapResult = GetAllProfitsMap(input.SchemeId, input.Beneficiary); return new ReceivedProfitsMap { - Value = { output.AllProfitsMap } + Value = { allProfitsMapResult.AllProfitsMap } }; } @@ -142,7 +100,7 @@ public override GetAllProfitsMapOutput GetAllProfitsMap(GetAllProfitsMapInput in return GetAllProfitsMap(input.SchemeId, input.Beneficiary); } - private GetAllProfitsMapOutput GetAllProfitsMap(Hash schemeId, Address beneficiary) + private GetAllProfitsMapOutput GetAllProfitsMap(Hash schemeId, Address beneficiary, string symbol = null) { var scheme = State.SchemeInfos[schemeId]; Assert(scheme != null, "Scheme not found."); @@ -169,10 +127,10 @@ private GetAllProfitsMapOutput GetAllProfitsMap(Hash schemeId, Address beneficia var profitDetail = availableDetails[i]; if (profitDetail.LastProfitPeriod == 0) profitDetail.LastProfitPeriod = profitDetail.StartPeriod; - var totalProfitsDictForEachProfitDetail = ProfitAllPeriods(scheme, profitDetail, beneficiary, profitDetail.EndPeriod.Sub(profitDetail.LastProfitPeriod),true); + var totalProfitsDictForEachProfitDetail = ProfitAllPeriods(scheme, profitDetail, beneficiary, profitDetail.EndPeriod.Sub(profitDetail.LastProfitPeriod),true, symbol); AddProfitToDict(allProfitsDict, totalProfitsDictForEachProfitDetail); if(i >= profitableDetailCount) continue; - var claimableProfitsDictForEachProfitDetail = ProfitAllPeriods(scheme, profitDetail, beneficiary, maxProfitReceivingPeriodCount,true); + var claimableProfitsDictForEachProfitDetail = ProfitAllPeriods(scheme, profitDetail, beneficiary, maxProfitReceivingPeriodCount,true, symbol); AddProfitToDict(claimableProfitsDict, claimableProfitsDictForEachProfitDetail); } From 7edbdc7e8c0a3e2dc92cd05da944a4e4e1a0a651 Mon Sep 17 00:00:00 2001 From: "louis.li" Date: Thu, 22 Feb 2024 15:39:13 +0800 Subject: [PATCH 27/41] feat:remove nft decimal restriction --- .../TokenContract_NFTHelper.cs | 4 --- .../TokenContract_NFT_Actions.cs | 2 -- .../BVT/NftApplicationTests.cs | 31 ------------------- 3 files changed, 37 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_NFTHelper.cs b/contract/AElf.Contracts.MultiToken/TokenContract_NFTHelper.cs index bddfb444bf..4c3efdf6a3 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_NFTHelper.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_NFTHelper.cs @@ -13,8 +13,4 @@ private SymbolType GetCreateInputSymbolType(string symbol) return words[1] == TokenContractConstants.CollectionSymbolSuffix ? SymbolType.NftCollection : SymbolType.Nft; } - private void AssertNFTCreateInput(CreateInput input) - { - Assert(input.Decimals == 0, "NFT's decimals must be 0"); - } } \ No newline at end of file diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs index 1926727e68..ac487122cb 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs @@ -9,13 +9,11 @@ public partial class TokenContract { private Empty CreateNFTCollection(CreateInput input) { - AssertNFTCreateInput(input); return CreateToken(input, SymbolType.NftCollection); } private Empty CreateNFTInfo(CreateInput input) { - AssertNFTCreateInput(input); var nftCollectionInfo = AssertNftCollectionExist(input.Symbol); input.IssueChainId = input.IssueChainId == 0 ? nftCollectionInfo.IssueChainId : input.IssueChainId; Assert(input.IssueChainId == nftCollectionInfo.IssueChainId, diff --git a/test/AElf.Contracts.MultiToken.Tests/BVT/NftApplicationTests.cs b/test/AElf.Contracts.MultiToken.Tests/BVT/NftApplicationTests.cs index 24055b432b..8cec0aabbd 100644 --- a/test/AElf.Contracts.MultiToken.Tests/BVT/NftApplicationTests.cs +++ b/test/AElf.Contracts.MultiToken.Tests/BVT/NftApplicationTests.cs @@ -214,22 +214,6 @@ public async Task MultiTokenContract_Create_721Nft_Test() public async Task MultiTokenContract_Create_NFTCollection_Input_Check_Test() { var input = NftCollection721Info; - // Decimals check - { - var result = await CreateMutiTokenWithExceptionAsync(TokenContractStub, new CreateInput - { - Symbol = $"{input.Symbol}0", - TokenName = input.TokenName, - TotalSupply = input.TotalSupply, - Decimals = 8, - Issuer = input.Issuer, - IssueChainId = input.IssueChainId, - ExternalInfo = input.ExternalInfo, - Owner = input.Owner - }); - result.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); - result.TransactionResult.Error.ShouldContain("NFT's decimals must be 0"); - } // Symbol check { var seedInput = BuildSeedCreateInput( new CreateInput @@ -289,21 +273,6 @@ public async Task MultiTokenContract_Create_NFT_Input_Check_Test() await CreateNftCollectionAsync(NftCollection721Info); var input = Nft721Info; - // Decimals check - { - var result = await CreateMutiTokenWithExceptionAsync(TokenContractStub, new CreateInput - { - Symbol = "GHJ-0", - TokenName = input.TokenName, - TotalSupply = input.TotalSupply, - Decimals = 8, - Issuer = input.Issuer, - IssueChainId = input.IssueChainId, - ExternalInfo = input.ExternalInfo - }); - result.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); - result.TransactionResult.Error.ShouldContain("NFT's decimals must be 0"); - } // Symbol check { var result = await CreateSeedNftWithExceptionAsync(TokenContractStub, new CreateInput From 6c4f6c808d59374b64af76c877b2a25945d498d5 Mon Sep 17 00:00:00 2001 From: "louis.li" Date: Mon, 26 Feb 2024 16:36:03 +0800 Subject: [PATCH 28/41] feat: create token on specified chain --- .../TokenContract_Actions.cs | 8 +++-- .../TokenContract_NFT_Actions.cs | 5 ++-- .../BVT/NftApplicationTests.cs | 30 +++++++++++++++++++ .../BVT/TokenApplicationTests.cs | 25 +++++++++------- 4 files changed, 54 insertions(+), 14 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index 249afc33ea..15c8553e3f 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -32,8 +32,6 @@ public override Empty InitializeFromParentChain(InitializeFromParentChainInput i /// public override Empty Create(CreateInput input) { - // can not call create on side chain - Assert(State.SideChainCreator.Value == null, "Failed to create token if side chain creator already set."); var inputSymbolType = GetCreateInputSymbolType(input.Symbol); if (input.Owner == null) { @@ -62,6 +60,12 @@ private Empty CreateToken(CreateInput input, SymbolType symbolType = SymbolType. DoTransferFrom(Context.Sender, Context.Self, Context.Self, symbolSeed, balance, ""); Burn(Context.Self, symbolSeed, balance); } + else + { + // can not call create on side chain + Assert(State.SideChainCreator.Value == null, + "Failed to create token if side chain creator already set."); + } } var tokenInfo = new TokenInfo diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs index 1926727e68..61ae892907 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs @@ -18,14 +18,15 @@ private Empty CreateNFTInfo(CreateInput input) AssertNFTCreateInput(input); var nftCollectionInfo = AssertNftCollectionExist(input.Symbol); input.IssueChainId = input.IssueChainId == 0 ? nftCollectionInfo.IssueChainId : input.IssueChainId; - Assert(input.IssueChainId == nftCollectionInfo.IssueChainId, + Assert( + input.IssueChainId == nftCollectionInfo.IssueChainId && Context.ChainId == nftCollectionInfo.IssueChainId, "NFT create ChainId must be collection's issue chainId"); - var owner = nftCollectionInfo.Owner ?? nftCollectionInfo.Issuer; Assert(Context.Sender == owner && owner == input.Owner, "NFT owner must be collection's owner"); if (nftCollectionInfo.Symbol == TokenContractConstants.SeedCollectionSymbol) { + Assert(input.Decimals == 0 && input.TotalSupply == 1, "SEED must be unique."); Assert(input.ExternalInfo.Value.TryGetValue(TokenContractConstants.SeedOwnedSymbolExternalInfoKey, out var ownedSymbol), "OwnedSymbol does not exist."); Assert(input.ExternalInfo.Value.TryGetValue(TokenContractConstants.SeedExpireTimeExternalInfoKey, diff --git a/test/AElf.Contracts.MultiToken.Tests/BVT/NftApplicationTests.cs b/test/AElf.Contracts.MultiToken.Tests/BVT/NftApplicationTests.cs index 24055b432b..dcf55368a8 100644 --- a/test/AElf.Contracts.MultiToken.Tests/BVT/NftApplicationTests.cs +++ b/test/AElf.Contracts.MultiToken.Tests/BVT/NftApplicationTests.cs @@ -184,6 +184,30 @@ private async Task> CreateNftCollectionAndNft(bool reuseItemId = tr return symbols; } + private async Task CreateNftFailed() + { + var collectionInfo = NftCollection1155Info; + collectionInfo.IssueChainId = 123; + var createCollectionRes = await CreateNftCollectionAsync(collectionInfo); + createCollectionRes.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); + Nft1155Info.IssueChainId = 123; + var createNft2Res = await TokenContractStub.Create.SendWithExceptionAsync(new CreateInput + { + Symbol = $"{collectionInfo.Symbol}{Nft1155Info.Symbol}", + TokenName = Nft1155Info.TokenName, + TotalSupply = Nft1155Info.TotalSupply, + Decimals = Nft1155Info.Decimals, + Issuer = Nft1155Info.Issuer, + IsBurnable = Nft1155Info.IsBurnable, + IssueChainId = Nft1155Info.IssueChainId, + ExternalInfo = Nft1155Info.ExternalInfo, + Owner = Nft1155Info.Issuer + }); + createNft2Res.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); + createNft2Res.TransactionResult.Error.Contains("NFT create ChainId must be collection's issue chainId") + .ShouldBeTrue(); + } + private void AssertTokenEqual(TokenCreated log, TokenInfo input) { Assert.Equal(log.TokenName, input.TokenName); @@ -204,6 +228,12 @@ public async Task MultiTokenContract_Create_1155Nft_Test() await CreateNftCollectionAndNft(); } + [Fact(DisplayName = "[MultiToken_Nft] Create 1155 nfts failed.")] + public async Task MultiTokenContract_Create_1155Nft_failed_Test() + { + await CreateNftFailed(); + } + [Fact(DisplayName = "[MultiToken_Nft] Create 721 nfts.")] public async Task MultiTokenContract_Create_721Nft_Test() { diff --git a/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs b/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs index dba96e1023..0bb4ade958 100644 --- a/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs +++ b/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs @@ -999,16 +999,21 @@ public async Task Side_Chain_Creat_Token_Test() }); await ApproveWithMinersAsync(proposalId); await ParliamentContractStub.Release.SendAsync(proposalId); - var createTokenRet = await TokenContractStub.Create.SendWithExceptionAsync(new CreateInput - { - Symbol = "ALI", - TokenName = "Ali", - Decimals = 4, - TotalSupply = 100_000, - Issuer = DefaultAddress, - Owner = DefaultAddress - }); - createTokenRet.TransactionResult.Error.ShouldContain( + + proposalId = await CreateProposalAsync(TokenContractAddress, + defaultParliament, nameof(TokenContractStub.Create), + new CreateInput + { + Symbol = "ALI", + TokenName = "Ali", + Decimals = 4, + TotalSupply = 100_000, + Issuer = DefaultAddress, + Owner = DefaultAddress + }); + await ApproveWithMinersAsync(proposalId); + var createTokenRe = await ParliamentContractStub.Release.SendWithExceptionAsync(proposalId); + createTokenRe.TransactionResult.Error.ShouldContain( "Failed to create token if side chain creator already set."); } From 5f364ff1e86fe7964b84f0d07870c98a0f6da5bb Mon Sep 17 00:00:00 2001 From: "louis.li" Date: Thu, 29 Feb 2024 10:52:05 +0800 Subject: [PATCH 29/41] feat: add seed when cross chain create --- .../TokenContract_Actions.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index 15c8553e3f..3d9331120d 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -127,7 +127,7 @@ private void CheckSeedNFT(string symbolSeed, String symbol) /// - /// Set primary token symbol. + /// SetSymbolSeed primary token symbol. /// /// /// @@ -472,7 +472,7 @@ public override Empty CrossChainCreateToken(CrossChainCreateTokenInput input) Owner = validateTokenInfoExistsInput.Owner ?? validateTokenInfoExistsInput.Issuer }; RegisterTokenInfo(tokenInfo); - + SetSymbolSeed(tokenInfo); Context.Fire(new TokenCreated { Symbol = validateTokenInfoExistsInput.Symbol, @@ -489,6 +489,17 @@ public override Empty CrossChainCreateToken(CrossChainCreateTokenInput input) return new Empty(); } + private void SetSymbolSeed(TokenInfo tokenInfo) + { + var collectionSymbol = GetNftCollectionSymbol(tokenInfo.Symbol); + if (collectionSymbol == TokenContractConstants.SeedCollectionSymbol && tokenInfo.ExternalInfo != null && + tokenInfo.ExternalInfo.Value.TryGetValue(TokenContractConstants.SeedOwnedSymbolExternalInfoKey, + out var ownedSymbol)) + { + State.SymbolSeedMap[ownedSymbol] = tokenInfo.Symbol; + } + } + public override Empty RegisterCrossChainTokenContractAddress(RegisterCrossChainTokenContractAddressInput input) { CheckCrossChainTokenContractRegistrationControllerAuthority(); From 0815a2b8b3f3e2ecd2bbc98c69d7531b1e84fbd6 Mon Sep 17 00:00:00 2001 From: "louis.li" Date: Thu, 29 Feb 2024 11:21:03 +0800 Subject: [PATCH 30/41] feat: SetSymbolSeed update --- .../TokenContract_Actions.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index 3d9331120d..c099134932 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -491,13 +491,18 @@ public override Empty CrossChainCreateToken(CrossChainCreateTokenInput input) private void SetSymbolSeed(TokenInfo tokenInfo) { - var collectionSymbol = GetNftCollectionSymbol(tokenInfo.Symbol); - if (collectionSymbol == TokenContractConstants.SeedCollectionSymbol && tokenInfo.ExternalInfo != null && + if (GetNftCollectionSymbol(tokenInfo.Symbol) == TokenContractConstants.SeedCollectionSymbol && + tokenInfo.ExternalInfo != null && tokenInfo.ExternalInfo.Value.TryGetValue(TokenContractConstants.SeedOwnedSymbolExternalInfoKey, - out var ownedSymbol)) + out var ownedSymbol) && + tokenInfo.ExternalInfo.Value.TryGetValue(TokenContractConstants.SeedExpireTimeExternalInfoKey, + out var expirationTime) && + long.TryParse(expirationTime, out var expirationTimeLong) && + Context.CurrentBlockTime.Seconds <= expirationTimeLong) { State.SymbolSeedMap[ownedSymbol] = tokenInfo.Symbol; } + } public override Empty RegisterCrossChainTokenContractAddress(RegisterCrossChainTokenContractAddressInput input) From ab522bbf346745409ba7d6c78ece10d750e578ce Mon Sep 17 00:00:00 2001 From: "louis.li" Date: Thu, 29 Feb 2024 15:43:03 +0800 Subject: [PATCH 31/41] feat: add batch approve --- .../TokenContractConstants.cs | 1 + .../TokenContractState.cs | 2 + .../TokenContract_Actions.cs | 48 +++++++-- protobuf/token_contract.proto | 8 +- protobuf/token_contract_impl.proto | 9 ++ .../BVT/TokenApplicationTests.cs | 97 +++++++++++++++++++ 6 files changed, 158 insertions(+), 7 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContractConstants.cs b/contract/AElf.Contracts.MultiToken/TokenContractConstants.cs index a14a4e9496..db4923fd4e 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContractConstants.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContractConstants.cs @@ -22,4 +22,5 @@ public static class TokenContractConstants public const string SeedCollectionSymbol = "SEED-0"; public const string SeedOwnedSymbolExternalInfoKey = "__seed_owned_symbol"; public const string SeedExpireTimeExternalInfoKey = "__seed_exp_time"; + public const int DefaultMaximumBatchApproveCount = 10; } \ No newline at end of file diff --git a/contract/AElf.Contracts.MultiToken/TokenContractState.cs b/contract/AElf.Contracts.MultiToken/TokenContractState.cs index c32b0ea62f..dc386b311b 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContractState.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContractState.cs @@ -65,4 +65,6 @@ public partial class TokenContractState : ContractState public SingletonState
VoteContractAddress { get; set; } public SingletonState TokenIssuerAndOwnerModificationDisabled { get; set; } + + public SingletonState MaximumBatchApproveCount { get; set; } } \ No newline at end of file diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index 249afc33ea..ca328a6260 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -251,16 +251,28 @@ public override Empty TransferFrom(TransferFromInput input) public override Empty Approve(ApproveInput input) { - AssertValidInputAddress(input.Spender); - AssertValidToken(input.Symbol, input.Amount); - State.Allowances[Context.Sender][input.Spender][input.Symbol] = input.Amount; + Approve(input.Spender, input.Symbol, input.Amount); + return new Empty(); + } + + private void Approve(Address spender, string symbol, long amount) + { + AssertValidInputAddress(spender); + AssertValidToken(symbol, amount); + State.Allowances[Context.Sender][spender][symbol] = amount; Context.Fire(new Approved { Owner = Context.Sender, - Spender = input.Spender, - Symbol = input.Symbol, - Amount = input.Amount + Spender = spender, + Symbol = symbol, + Amount = amount }); + } + + public override Empty BatchApprove(BatchApproveInput input) + { + Assert(input.Value.Count <= GetMaximumBatchApproveCount(), "Exceeds the maximum batch approve count."); + foreach (var approve in input.Value) Approve(approve.Spender, approve.Symbol, approve.Amount); return new Empty(); } @@ -626,4 +638,28 @@ public override BoolValue GetTokenIssuerAndOwnerModificationEnabled(Empty input) Value = !State.TokenIssuerAndOwnerModificationDisabled.Value }; } + + public override Empty SetMaximumBatchApproveCount(Int32Value input) + { + AssertSenderAddressWith(GetDefaultParliamentController().OwnerAddress); + Assert(input.Value != 0, "Invalid input."); + State.MaximumBatchApproveCount.Value = input.Value; + return new Empty(); + } + + public override Int32Value GetMaximumBatchApproveCount(Empty input) + { + return new Int32Value + { + Value = GetMaximumBatchApproveCount() + }; + } + + private int GetMaximumBatchApproveCount() + { + var maximumBatchApproveCount = State.MaximumBatchApproveCount.Value == 0 + ? TokenContractConstants.DefaultMaximumBatchApproveCount + : State.MaximumBatchApproveCount.Value; + return maximumBatchApproveCount; + } } \ No newline at end of file diff --git a/protobuf/token_contract.proto b/protobuf/token_contract.proto index d858bf5f1d..e1174b8b45 100644 --- a/protobuf/token_contract.proto +++ b/protobuf/token_contract.proto @@ -40,7 +40,10 @@ service TokenContract { // enabling the Spender to call TransferFrom. rpc Approve (ApproveInput) returns (google.protobuf.Empty) { } - + + rpc BatchApprove (BatchApproveInput) returns (google.protobuf.Empty) { + } + // This is the reverse operation for Approve, it will decrease the allowance. rpc UnApprove (UnApproveInput) returns (google.protobuf.Empty) { } @@ -354,6 +357,9 @@ message ApproveInput { // The amount of token to approve. int64 amount = 3; } +message BatchApproveInput { + repeated ApproveInput value = 1; +} message UnApproveInput { // The address that allowance will be decreased. diff --git a/protobuf/token_contract_impl.proto b/protobuf/token_contract_impl.proto index 9a7779ec45..ece30da14c 100644 --- a/protobuf/token_contract_impl.proto +++ b/protobuf/token_contract_impl.proto @@ -83,6 +83,11 @@ service TokenContractImpl { rpc RemoveTransactionFeeFreeAllowancesConfig (RemoveTransactionFeeFreeAllowancesConfigInput) returns (google.protobuf.Empty) { } + rpc SetMaximumBatchApproveCount (google.protobuf.Int32Value) returns (google.protobuf.Empty) { + + } + + // Delegatee sets the delegation and related information of the delegator based on a transaction. rpc SetTransactionFeeDelegateInfos (SetTransactionFeeDelegateInfosInput) returns (google.protobuf.Empty){ } @@ -173,6 +178,10 @@ service TokenContractImpl { rpc GetTokenIssuerAndOwnerModificationEnabled(google.protobuf.Empty) returns (google.protobuf.BoolValue) { option (aelf.is_view) = true; } + + rpc GetMaximumBatchApproveCount (google.protobuf.Empty) returns (google.protobuf.Int32Value) { + + } } message AdvanceResourceTokenInput { diff --git a/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs b/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs index dba96e1023..07b737dfd0 100644 --- a/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs +++ b/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs @@ -171,6 +171,103 @@ public async Task MultiTokenContract_Approve_ContractAddress_Test() basicAllowanceOutput.Allowance.ShouldBe(2000L); } + [Fact(DisplayName = "[MultiToken] BatchApprove token to Contract")] + public async Task MultiTokenContract_BatchApprove_ContractAddress_Test() + { + await CreateTokenAndIssue(); + var approveBasisResult = (await TokenContractStub.BatchApprove.SendAsync(new BatchApproveInput + { + Value = + { + new ApproveInput + { + Symbol = SymbolForTest, + Amount = 2000L, + Spender = BasicFunctionContractAddress + }, + new ApproveInput + { + Symbol = SymbolForTest, + Amount = 1000L, + Spender = OtherBasicFunctionContractAddress + }, + new ApproveInput + { + Symbol = SymbolForTest, + Amount = 5000L, + Spender = TreasuryContractAddress + } + } + })).TransactionResult; + approveBasisResult.Status.ShouldBe(TransactionResultStatus.Mined); + + var basicAllowanceOutput = await TokenContractStub.GetAllowance.CallAsync(new GetAllowanceInput + { + Owner = DefaultAddress, + Spender = BasicFunctionContractAddress, + Symbol = SymbolForTest + }); + basicAllowanceOutput.Allowance.ShouldBe(2000L); + var otherBasicAllowanceOutput = await TokenContractStub.GetAllowance.CallAsync(new GetAllowanceInput + { + Owner = DefaultAddress, + Spender = OtherBasicFunctionContractAddress, + Symbol = SymbolForTest + }); + otherBasicAllowanceOutput.Allowance.ShouldBe(1000L); + var treasuryAllowanceOutput = await TokenContractStub.GetAllowance.CallAsync(new GetAllowanceInput + { + Owner = DefaultAddress, + Spender = TreasuryContractAddress, + Symbol = SymbolForTest + }); + treasuryAllowanceOutput.Allowance.ShouldBe(5000L); + } + + [Fact] + public async Task MultiTokenContract_SetMaximumBatchApproveCount_Test() + { + var result = await TokenContractStub.SetMaximumBatchApproveCount.SendWithExceptionAsync(new Int32Value + { + Value = 1 + }); + result.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); + result.TransactionResult.Error.ShouldContain("Unauthorized behavior"); + var maximumBatchApproveCountOutput = await TokenContractStub.GetMaximumBatchApproveCount.CallAsync(new Empty()); + maximumBatchApproveCountOutput.Value.ShouldBe(10); + var defaultParliament = await ParliamentContractStub.GetDefaultOrganizationAddress.CallAsync(new Empty()); + var proposalId = await CreateProposalAsync(TokenContractAddress, + defaultParliament, nameof(TokenContractStub.SetMaximumBatchApproveCount), + new Int32Value + { + Value = 1 + }); + await ApproveWithMinersAsync(proposalId); + await ParliamentContractStub.Release.SendAsync(proposalId); + maximumBatchApproveCountOutput = await TokenContractStub.GetMaximumBatchApproveCount.CallAsync(new Empty()); + maximumBatchApproveCountOutput.Value.ShouldBe(1); + var approveBasisResult = (await TokenContractStub.BatchApprove.SendWithExceptionAsync(new BatchApproveInput + { + Value = + { + new ApproveInput + { + Symbol = SymbolForTest, + Amount = 2000L, + Spender = BasicFunctionContractAddress + }, + new ApproveInput + { + Symbol = SymbolForTest, + Amount = 1000L, + Spender = OtherBasicFunctionContractAddress + } + } + })).TransactionResult; + approveBasisResult.Status.ShouldBe(TransactionResultStatus.Failed); + approveBasisResult.Error.ShouldContain("Exceeds the maximum batch approve count"); + } + [Fact(DisplayName = "[MultiToken] Approve token out of owner's balance")] public async Task MultiTokenContract_Approve_OutOfAmount_Test() { From 7d84b6b68d907ede1c373439f576f19d72e8f8a0 Mon Sep 17 00:00:00 2001 From: "louis.li" Date: Fri, 1 Mar 2024 09:59:48 +0800 Subject: [PATCH 32/41] feat: update MaximumBatchApproveCount check --- contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index ca328a6260..ad7605faaa 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -642,7 +642,7 @@ public override BoolValue GetTokenIssuerAndOwnerModificationEnabled(Empty input) public override Empty SetMaximumBatchApproveCount(Int32Value input) { AssertSenderAddressWith(GetDefaultParliamentController().OwnerAddress); - Assert(input.Value != 0, "Invalid input."); + Assert(input.Value > 0, "Invalid input."); State.MaximumBatchApproveCount.Value = input.Value; return new Empty(); } From 709555422e6062ca07431ff438c4271e6cceb448 Mon Sep 17 00:00:00 2001 From: "louis.li" Date: Fri, 1 Mar 2024 10:38:14 +0800 Subject: [PATCH 33/41] feat:update description --- contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index c099134932..574bed6287 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -127,7 +127,7 @@ private void CheckSeedNFT(string symbolSeed, String symbol) /// - /// SetSymbolSeed primary token symbol. + /// Set primary token symbol. /// /// /// From eb4ceaad5304e1fc9100e0faf805445a1d4e9c76 Mon Sep 17 00:00:00 2001 From: "louis.li" Date: Fri, 1 Mar 2024 11:53:47 +0800 Subject: [PATCH 34/41] feat: ApproveInputList distinct --- .../TokenContract_Actions.cs | 18 ++++++-- .../BVT/TokenApplicationTests.cs | 42 +++++++++++++++++++ 2 files changed, 56 insertions(+), 4 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index ad7605faaa..49f0556bb1 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -251,14 +251,14 @@ public override Empty TransferFrom(TransferFromInput input) public override Empty Approve(ApproveInput input) { + AssertValidInputAddress(input.Spender); + AssertValidToken(input.Symbol, input.Amount); Approve(input.Spender, input.Symbol, input.Amount); return new Empty(); } private void Approve(Address spender, string symbol, long amount) { - AssertValidInputAddress(spender); - AssertValidToken(symbol, amount); State.Allowances[Context.Sender][spender][symbol] = amount; Context.Fire(new Approved { @@ -271,8 +271,18 @@ private void Approve(Address spender, string symbol, long amount) public override Empty BatchApprove(BatchApproveInput input) { - Assert(input.Value.Count <= GetMaximumBatchApproveCount(), "Exceeds the maximum batch approve count."); - foreach (var approve in input.Value) Approve(approve.Spender, approve.Symbol, approve.Amount); + Assert(input != null && input.Value != null, "Invalid input ."); + foreach (var approve in input.Value) + { + AssertValidInputAddress(approve.Spender); + AssertValidToken(approve.Symbol, approve.Amount); + } + + var approveInputList = input.Value.GroupBy(approve => approve.Symbol + approve.Spender, approve => approve) + .Select(approve => approve.Last()).ToList(); + Assert(approveInputList.Count <= GetMaximumBatchApproveCount(), "Exceeds the maximum batch approve count."); + foreach (var approve in approveInputList) + Approve(approve.Spender, approve.Symbol, approve.Amount); return new Empty(); } diff --git a/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs b/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs index 07b737dfd0..fe80b7710f 100644 --- a/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs +++ b/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs @@ -222,6 +222,47 @@ public async Task MultiTokenContract_BatchApprove_ContractAddress_Test() Symbol = SymbolForTest }); treasuryAllowanceOutput.Allowance.ShouldBe(5000L); + + approveBasisResult = (await TokenContractStub.BatchApprove.SendAsync(new BatchApproveInput + { + Value = + { + new ApproveInput + { + Symbol = SymbolForTest, + Amount = 1000L, + Spender = BasicFunctionContractAddress + }, + new ApproveInput + { + Symbol = SymbolForTest, + Amount = 3000L, + Spender = BasicFunctionContractAddress + }, + new ApproveInput + { + Symbol = SymbolForTest, + Amount = 3000L, + Spender = TreasuryContractAddress + } + } + })).TransactionResult; + approveBasisResult.Status.ShouldBe(TransactionResultStatus.Mined); + basicAllowanceOutput = await TokenContractStub.GetAllowance.CallAsync(new GetAllowanceInput + { + Owner = DefaultAddress, + Spender = BasicFunctionContractAddress, + Symbol = SymbolForTest + }); + basicAllowanceOutput.Allowance.ShouldBe(3000L); + + treasuryAllowanceOutput = await TokenContractStub.GetAllowance.CallAsync(new GetAllowanceInput + { + Owner = DefaultAddress, + Spender = TreasuryContractAddress, + Symbol = SymbolForTest + }); + treasuryAllowanceOutput.Allowance.ShouldBe(3000L); } [Fact] @@ -246,6 +287,7 @@ public async Task MultiTokenContract_SetMaximumBatchApproveCount_Test() await ParliamentContractStub.Release.SendAsync(proposalId); maximumBatchApproveCountOutput = await TokenContractStub.GetMaximumBatchApproveCount.CallAsync(new Empty()); maximumBatchApproveCountOutput.Value.ShouldBe(1); + await CreateTokenAndIssue(); var approveBasisResult = (await TokenContractStub.BatchApprove.SendWithExceptionAsync(new BatchApproveInput { Value = From cc028e2f551de6fc9ebbbfcae38a0b6edb5b722e Mon Sep 17 00:00:00 2001 From: "louis.li" Date: Fri, 1 Mar 2024 14:00:41 +0800 Subject: [PATCH 35/41] feat: method rename --- .../TokenContractConstants.cs | 2 +- .../TokenContract_Actions.cs | 12 ++++++------ protobuf/token_contract_impl.proto | 4 ++-- .../BVT/TokenApplicationTests.cs | 10 +++++----- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContractConstants.cs b/contract/AElf.Contracts.MultiToken/TokenContractConstants.cs index db4923fd4e..59eae99a8c 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContractConstants.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContractConstants.cs @@ -22,5 +22,5 @@ public static class TokenContractConstants public const string SeedCollectionSymbol = "SEED-0"; public const string SeedOwnedSymbolExternalInfoKey = "__seed_owned_symbol"; public const string SeedExpireTimeExternalInfoKey = "__seed_exp_time"; - public const int DefaultMaximumBatchApproveCount = 10; + public const int DefaultMaxBatchApproveCount = 100; } \ No newline at end of file diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index 49f0556bb1..8ccceb13d9 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -280,7 +280,7 @@ public override Empty BatchApprove(BatchApproveInput input) var approveInputList = input.Value.GroupBy(approve => approve.Symbol + approve.Spender, approve => approve) .Select(approve => approve.Last()).ToList(); - Assert(approveInputList.Count <= GetMaximumBatchApproveCount(), "Exceeds the maximum batch approve count."); + Assert(approveInputList.Count <= GetMaxBatchApproveCount(), "Exceeds the maximum batch approve count."); foreach (var approve in approveInputList) Approve(approve.Spender, approve.Symbol, approve.Amount); return new Empty(); @@ -649,7 +649,7 @@ public override BoolValue GetTokenIssuerAndOwnerModificationEnabled(Empty input) }; } - public override Empty SetMaximumBatchApproveCount(Int32Value input) + public override Empty SetMaxBatchApproveCount(Int32Value input) { AssertSenderAddressWith(GetDefaultParliamentController().OwnerAddress); Assert(input.Value > 0, "Invalid input."); @@ -657,18 +657,18 @@ public override Empty SetMaximumBatchApproveCount(Int32Value input) return new Empty(); } - public override Int32Value GetMaximumBatchApproveCount(Empty input) + public override Int32Value GetMaxBatchApproveCount(Empty input) { return new Int32Value { - Value = GetMaximumBatchApproveCount() + Value = GetMaxBatchApproveCount() }; } - private int GetMaximumBatchApproveCount() + private int GetMaxBatchApproveCount() { var maximumBatchApproveCount = State.MaximumBatchApproveCount.Value == 0 - ? TokenContractConstants.DefaultMaximumBatchApproveCount + ? TokenContractConstants.DefaultMaxBatchApproveCount : State.MaximumBatchApproveCount.Value; return maximumBatchApproveCount; } diff --git a/protobuf/token_contract_impl.proto b/protobuf/token_contract_impl.proto index ece30da14c..5885914e80 100644 --- a/protobuf/token_contract_impl.proto +++ b/protobuf/token_contract_impl.proto @@ -83,7 +83,7 @@ service TokenContractImpl { rpc RemoveTransactionFeeFreeAllowancesConfig (RemoveTransactionFeeFreeAllowancesConfigInput) returns (google.protobuf.Empty) { } - rpc SetMaximumBatchApproveCount (google.protobuf.Int32Value) returns (google.protobuf.Empty) { + rpc SetMaxBatchApproveCount (google.protobuf.Int32Value) returns (google.protobuf.Empty) { } @@ -179,7 +179,7 @@ service TokenContractImpl { option (aelf.is_view) = true; } - rpc GetMaximumBatchApproveCount (google.protobuf.Empty) returns (google.protobuf.Int32Value) { + rpc GetMaxBatchApproveCount (google.protobuf.Empty) returns (google.protobuf.Int32Value) { } } diff --git a/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs b/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs index fe80b7710f..fec97b0026 100644 --- a/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs +++ b/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs @@ -268,24 +268,24 @@ public async Task MultiTokenContract_BatchApprove_ContractAddress_Test() [Fact] public async Task MultiTokenContract_SetMaximumBatchApproveCount_Test() { - var result = await TokenContractStub.SetMaximumBatchApproveCount.SendWithExceptionAsync(new Int32Value + var result = await TokenContractStub.SetMaxBatchApproveCount.SendWithExceptionAsync(new Int32Value { Value = 1 }); result.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); result.TransactionResult.Error.ShouldContain("Unauthorized behavior"); - var maximumBatchApproveCountOutput = await TokenContractStub.GetMaximumBatchApproveCount.CallAsync(new Empty()); - maximumBatchApproveCountOutput.Value.ShouldBe(10); + var maximumBatchApproveCountOutput = await TokenContractStub.GetMaxBatchApproveCount.CallAsync(new Empty()); + maximumBatchApproveCountOutput.Value.ShouldBe(100); var defaultParliament = await ParliamentContractStub.GetDefaultOrganizationAddress.CallAsync(new Empty()); var proposalId = await CreateProposalAsync(TokenContractAddress, - defaultParliament, nameof(TokenContractStub.SetMaximumBatchApproveCount), + defaultParliament, nameof(TokenContractStub.SetMaxBatchApproveCount), new Int32Value { Value = 1 }); await ApproveWithMinersAsync(proposalId); await ParliamentContractStub.Release.SendAsync(proposalId); - maximumBatchApproveCountOutput = await TokenContractStub.GetMaximumBatchApproveCount.CallAsync(new Empty()); + maximumBatchApproveCountOutput = await TokenContractStub.GetMaxBatchApproveCount.CallAsync(new Empty()); maximumBatchApproveCountOutput.Value.ShouldBe(1); await CreateTokenAndIssue(); var approveBasisResult = (await TokenContractStub.BatchApprove.SendWithExceptionAsync(new BatchApproveInput From b4263e3d2c5ce5930c08e842afb5b70e501794ec Mon Sep 17 00:00:00 2001 From: "louis.li" Date: Fri, 1 Mar 2024 16:25:25 +0800 Subject: [PATCH 36/41] feat: update BatchApproveInputCount check --- contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index 8ccceb13d9..5ba46b9623 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -272,15 +272,14 @@ private void Approve(Address spender, string symbol, long amount) public override Empty BatchApprove(BatchApproveInput input) { Assert(input != null && input.Value != null, "Invalid input ."); + Assert(input.Value.Count <= GetMaxBatchApproveCount(), "Exceeds the maximum batch approve count."); foreach (var approve in input.Value) { AssertValidInputAddress(approve.Spender); AssertValidToken(approve.Symbol, approve.Amount); } - var approveInputList = input.Value.GroupBy(approve => approve.Symbol + approve.Spender, approve => approve) .Select(approve => approve.Last()).ToList(); - Assert(approveInputList.Count <= GetMaxBatchApproveCount(), "Exceeds the maximum batch approve count."); foreach (var approve in approveInputList) Approve(approve.Spender, approve.Symbol, approve.Amount); return new Empty(); From db75acf5b4db8badddbf84c45d1522983f67cc80 Mon Sep 17 00:00:00 2001 From: "louis.li" Date: Sat, 2 Mar 2024 15:13:49 +0800 Subject: [PATCH 37/41] feat:seed cannot create token on side chain --- .../TokenContract_Actions.cs | 25 +++---------------- 1 file changed, 3 insertions(+), 22 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index 574bed6287..0159b7002c 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -50,6 +50,9 @@ private Empty CreateToken(CreateInput input, SymbolType symbolType = SymbolType. AssertValidCreateInput(input, symbolType); if (symbolType == SymbolType.Token || symbolType == SymbolType.NftCollection) { + // can not call create on side chain + Assert(State.SideChainCreator.Value == null, + "Failed to create token if side chain creator already set."); if (!IsAddressInCreateWhiteList(Context.Sender) && input.Symbol != TokenContractConstants.SeedCollectionSymbol) { @@ -60,12 +63,6 @@ private Empty CreateToken(CreateInput input, SymbolType symbolType = SymbolType. DoTransferFrom(Context.Sender, Context.Self, Context.Self, symbolSeed, balance, ""); Burn(Context.Self, symbolSeed, balance); } - else - { - // can not call create on side chain - Assert(State.SideChainCreator.Value == null, - "Failed to create token if side chain creator already set."); - } } var tokenInfo = new TokenInfo @@ -472,7 +469,6 @@ public override Empty CrossChainCreateToken(CrossChainCreateTokenInput input) Owner = validateTokenInfoExistsInput.Owner ?? validateTokenInfoExistsInput.Issuer }; RegisterTokenInfo(tokenInfo); - SetSymbolSeed(tokenInfo); Context.Fire(new TokenCreated { Symbol = validateTokenInfoExistsInput.Symbol, @@ -489,21 +485,6 @@ public override Empty CrossChainCreateToken(CrossChainCreateTokenInput input) return new Empty(); } - private void SetSymbolSeed(TokenInfo tokenInfo) - { - if (GetNftCollectionSymbol(tokenInfo.Symbol) == TokenContractConstants.SeedCollectionSymbol && - tokenInfo.ExternalInfo != null && - tokenInfo.ExternalInfo.Value.TryGetValue(TokenContractConstants.SeedOwnedSymbolExternalInfoKey, - out var ownedSymbol) && - tokenInfo.ExternalInfo.Value.TryGetValue(TokenContractConstants.SeedExpireTimeExternalInfoKey, - out var expirationTime) && - long.TryParse(expirationTime, out var expirationTimeLong) && - Context.CurrentBlockTime.Seconds <= expirationTimeLong) - { - State.SymbolSeedMap[ownedSymbol] = tokenInfo.Symbol; - } - - } public override Empty RegisterCrossChainTokenContractAddress(RegisterCrossChainTokenContractAddressInput input) { From aa1b5b30bf204472105826df62327a9c12bcd764 Mon Sep 17 00:00:00 2001 From: "louis.li" Date: Sun, 3 Mar 2024 14:35:05 +0800 Subject: [PATCH 38/41] feat:rename MaxBatchApproveCount --- .../AElf.Contracts.MultiToken/TokenContractState.cs | 2 +- .../AElf.Contracts.MultiToken/TokenContract_Actions.cs | 10 +++++----- .../BVT/TokenApplicationTests.cs | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContractState.cs b/contract/AElf.Contracts.MultiToken/TokenContractState.cs index dc386b311b..b687cf1598 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContractState.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContractState.cs @@ -66,5 +66,5 @@ public partial class TokenContractState : ContractState public SingletonState TokenIssuerAndOwnerModificationDisabled { get; set; } - public SingletonState MaximumBatchApproveCount { get; set; } + public SingletonState MaxBatchApproveCount { get; set; } } \ No newline at end of file diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index 5ba46b9623..08ff42a689 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -272,7 +272,7 @@ private void Approve(Address spender, string symbol, long amount) public override Empty BatchApprove(BatchApproveInput input) { Assert(input != null && input.Value != null, "Invalid input ."); - Assert(input.Value.Count <= GetMaxBatchApproveCount(), "Exceeds the maximum batch approve count."); + Assert(input.Value.Count <= GetMaxBatchApproveCount(), "Exceeds the max batch approve count."); foreach (var approve in input.Value) { AssertValidInputAddress(approve.Spender); @@ -652,7 +652,7 @@ public override Empty SetMaxBatchApproveCount(Int32Value input) { AssertSenderAddressWith(GetDefaultParliamentController().OwnerAddress); Assert(input.Value > 0, "Invalid input."); - State.MaximumBatchApproveCount.Value = input.Value; + State.MaxBatchApproveCount.Value = input.Value; return new Empty(); } @@ -666,9 +666,9 @@ public override Int32Value GetMaxBatchApproveCount(Empty input) private int GetMaxBatchApproveCount() { - var maximumBatchApproveCount = State.MaximumBatchApproveCount.Value == 0 + var maxBatchApproveCount = State.MaxBatchApproveCount.Value == 0 ? TokenContractConstants.DefaultMaxBatchApproveCount - : State.MaximumBatchApproveCount.Value; - return maximumBatchApproveCount; + : State.MaxBatchApproveCount.Value; + return maxBatchApproveCount; } } \ No newline at end of file diff --git a/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs b/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs index fec97b0026..a63eba113c 100644 --- a/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs +++ b/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs @@ -307,7 +307,7 @@ public async Task MultiTokenContract_SetMaximumBatchApproveCount_Test() } })).TransactionResult; approveBasisResult.Status.ShouldBe(TransactionResultStatus.Failed); - approveBasisResult.Error.ShouldContain("Exceeds the maximum batch approve count"); + approveBasisResult.Error.ShouldContain("Exceeds the max batch approve count"); } [Fact(DisplayName = "[MultiToken] Approve token out of owner's balance")] From 4f7f5639c31e6518620935f5a5af413555d67408 Mon Sep 17 00:00:00 2001 From: "louis.li" Date: Sun, 3 Mar 2024 17:11:41 +0800 Subject: [PATCH 39/41] feat:update BatchApprove check --- .../AElf.Contracts.MultiToken/TokenContract_Actions.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index 162ba78c01..2a477cafe7 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -272,7 +272,7 @@ private void Approve(Address spender, string symbol, long amount) public override Empty BatchApprove(BatchApproveInput input) { - Assert(input != null && input.Value != null, "Invalid input ."); + Assert(input != null && input.Value != null && input.Value.Count > 0, "Invalid input ."); Assert(input.Value.Count <= GetMaxBatchApproveCount(), "Exceeds the max batch approve count."); foreach (var approve in input.Value) { @@ -651,8 +651,8 @@ public override BoolValue GetTokenIssuerAndOwnerModificationEnabled(Empty input) public override Empty SetMaxBatchApproveCount(Int32Value input) { - AssertSenderAddressWith(GetDefaultParliamentController().OwnerAddress); Assert(input.Value > 0, "Invalid input."); + AssertSenderAddressWith(GetDefaultParliamentController().OwnerAddress); State.MaxBatchApproveCount.Value = input.Value; return new Empty(); } @@ -667,9 +667,8 @@ public override Int32Value GetMaxBatchApproveCount(Empty input) private int GetMaxBatchApproveCount() { - var maxBatchApproveCount = State.MaxBatchApproveCount.Value == 0 + return State.MaxBatchApproveCount.Value == 0 ? TokenContractConstants.DefaultMaxBatchApproveCount : State.MaxBatchApproveCount.Value; - return maxBatchApproveCount; } } \ No newline at end of file From 2753cb991d18085777539b27435f97804b38337b Mon Sep 17 00:00:00 2001 From: "louis.li" Date: Wed, 6 Mar 2024 20:35:17 +0800 Subject: [PATCH 40/41] feat:Compatibility with historical NFT creations --- .../TokenContractConstants.cs | 1 + .../TokenContract_NFT_Actions.cs | 17 +++++++++-- .../BVT/NftApplicationTests.cs | 28 +++++++++++++++---- 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContractConstants.cs b/contract/AElf.Contracts.MultiToken/TokenContractConstants.cs index a14a4e9496..95bb390726 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContractConstants.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContractConstants.cs @@ -22,4 +22,5 @@ public static class TokenContractConstants public const string SeedCollectionSymbol = "SEED-0"; public const string SeedOwnedSymbolExternalInfoKey = "__seed_owned_symbol"; public const string SeedExpireTimeExternalInfoKey = "__seed_exp_time"; + public const string NftCreateChainIdExternalInfoKey = "__nft_create_chain_id"; } \ No newline at end of file diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs index 2e242cf589..2e8fa8bd29 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs @@ -17,10 +17,23 @@ private Empty CreateNFTInfo(CreateInput input) var nftCollectionInfo = AssertNftCollectionExist(input.Symbol); input.IssueChainId = input.IssueChainId == 0 ? nftCollectionInfo.IssueChainId : input.IssueChainId; Assert( - input.IssueChainId == nftCollectionInfo.IssueChainId && Context.ChainId == nftCollectionInfo.IssueChainId, - "NFT create ChainId must be collection's issue chainId"); + input.IssueChainId == nftCollectionInfo.IssueChainId, + "NFT issue ChainId must be collection's issue chainId"); + var owner = nftCollectionInfo.Owner ?? nftCollectionInfo.Issuer; Assert(Context.Sender == owner && owner == input.Owner, "NFT owner must be collection's owner"); + if (nftCollectionInfo.ExternalInfo != null && nftCollectionInfo.ExternalInfo.Value.TryGetValue( + TokenContractConstants.NftCreateChainIdExternalInfoKey, + out var nftCreateChainId) && long.TryParse(nftCreateChainId, out var nftCreateChainIdLong)) + { + Assert(nftCreateChainIdLong == Context.ChainId, + "NFT create ChainId must be collection's NFT create chainId"); + } + else + { + Assert(State.SideChainCreator.Value == null, + "Failed to create token if side chain creator already set."); + } if (nftCollectionInfo.Symbol == TokenContractConstants.SeedCollectionSymbol) { diff --git a/test/AElf.Contracts.MultiToken.Tests/BVT/NftApplicationTests.cs b/test/AElf.Contracts.MultiToken.Tests/BVT/NftApplicationTests.cs index 6c92193b21..d6a6945ef2 100644 --- a/test/AElf.Contracts.MultiToken.Tests/BVT/NftApplicationTests.cs +++ b/test/AElf.Contracts.MultiToken.Tests/BVT/NftApplicationTests.cs @@ -17,6 +17,7 @@ public static class NftCollectionMetaFields public static string BaseUriKey = "__nft_base_uri"; public static string NftType = "__nft_type"; public const string IsItemIdReuseKey = "__nft_is_item_id_reuse"; + public const string NftCreateChainIdExternalInfoKey = "__nft_create_chain_id"; } public static class NftInfoMetaFields @@ -186,11 +187,28 @@ private async Task> CreateNftCollectionAndNft(bool reuseItemId = tr private async Task CreateNftFailed() { - var collectionInfo = NftCollection1155Info; - collectionInfo.IssueChainId = 123; + var collectionInfo = new TokenInfo + { + Symbol = NftCollection1155Info.Symbol, + TokenName = NftCollection1155Info.TokenName, + TotalSupply = NftCollection1155Info.TotalSupply, + Decimals = NftCollection1155Info.Decimals, + Issuer = NftCollection1155Info.Issuer, + IssueChainId = NftCollection1155Info.IssueChainId, + ExternalInfo = new ExternalInfo() + { + Value = + { + { + NftCollectionMetaFields.NftCreateChainIdExternalInfoKey, + "1234" + } + } + }, + Owner = NftCollection1155Info.Issuer + }; var createCollectionRes = await CreateNftCollectionAsync(collectionInfo); createCollectionRes.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); - Nft1155Info.IssueChainId = 123; var createNft2Res = await TokenContractStub.Create.SendWithExceptionAsync(new CreateInput { Symbol = $"{collectionInfo.Symbol}{Nft1155Info.Symbol}", @@ -204,7 +222,7 @@ private async Task CreateNftFailed() Owner = Nft1155Info.Issuer }); createNft2Res.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); - createNft2Res.TransactionResult.Error.Contains("NFT create ChainId must be collection's issue chainId") + createNft2Res.TransactionResult.Error.Contains("NFT create ChainId must be collection's NFT create chainId") .ShouldBeTrue(); } @@ -377,7 +395,7 @@ public async Task MultiTokenContract_Create_NFT_Input_Check_Test() Owner = input.Owner }); result.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); - result.TransactionResult.Error.ShouldContain("NFT create ChainId must be collection's issue chainId"); + result.TransactionResult.Error.ShouldContain("NFT issue ChainId must be collection's issue chainId"); } } From 777e040dc98c98b7b35b447cb5487c76021ec68c Mon Sep 17 00:00:00 2001 From: "louis.li" Date: Thu, 7 Mar 2024 10:27:05 +0800 Subject: [PATCH 41/41] feat:Modify the verification order --- .../AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs index 2e8fa8bd29..124d1dac73 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs @@ -19,9 +19,6 @@ private Empty CreateNFTInfo(CreateInput input) Assert( input.IssueChainId == nftCollectionInfo.IssueChainId, "NFT issue ChainId must be collection's issue chainId"); - - var owner = nftCollectionInfo.Owner ?? nftCollectionInfo.Issuer; - Assert(Context.Sender == owner && owner == input.Owner, "NFT owner must be collection's owner"); if (nftCollectionInfo.ExternalInfo != null && nftCollectionInfo.ExternalInfo.Value.TryGetValue( TokenContractConstants.NftCreateChainIdExternalInfoKey, out var nftCreateChainId) && long.TryParse(nftCreateChainId, out var nftCreateChainIdLong)) @@ -34,7 +31,9 @@ private Empty CreateNFTInfo(CreateInput input) Assert(State.SideChainCreator.Value == null, "Failed to create token if side chain creator already set."); } - + + var owner = nftCollectionInfo.Owner ?? nftCollectionInfo.Issuer; + Assert(Context.Sender == owner && owner == input.Owner, "NFT owner must be collection's owner"); if (nftCollectionInfo.Symbol == TokenContractConstants.SeedCollectionSymbol) { Assert(input.Decimals == 0 && input.TotalSupply == 1, "SEED must be unique.");