diff --git a/contract/Forest/ForestContract.cs b/contract/Forest/ForestContract.cs
index cd8346bc..4ce475de 100755
--- a/contract/Forest/ForestContract.cs
+++ b/contract/Forest/ForestContract.cs
@@ -239,5 +239,23 @@ public override Empty CreateArt(CreateArtInput input)
});
return new Empty();
}
+
+ public override Empty SetMaxBatchCancelOfferCount(Int32Value input)
+ {
+ AssertSenderIsAdmin();
+ Assert(input is { Value: > 0 }, "Invalid input.");
+
+ State.MaxBatchCancelOfferCount.Value = input.Value;
+ return new Empty();
+ }
+
+ public override Empty SetMaxBatchCancelListCount(Int32Value input)
+ {
+ AssertSenderIsAdmin();
+ Assert(input is { Value: > 0 }, "Invalid input.");
+
+ State.MaxBatchCancelListCount.Value = input.Value;
+ return new Empty();
+ }
}
}
\ No newline at end of file
diff --git a/contract/Forest/ForestContractConstants.cs b/contract/Forest/ForestContractConstants.cs
index 9c495d0d..561026f1 100644
--- a/contract/Forest/ForestContractConstants.cs
+++ b/contract/Forest/ForestContractConstants.cs
@@ -22,5 +22,7 @@ public partial class ForestContract
public const string DefaultAIImageSize1024 = "1024x1024";
public const string DefaultAIImageSize512 = "512x512";
public const string DefaultAIImageSize256 = "256x256";
+ public const int DefaultMaxBatchCancelOfferCount= 20;
+ public const int DefaultMaxBatchCancelListCount= 20;
}
\ No newline at end of file
diff --git a/contract/Forest/ForestContractState.cs b/contract/Forest/ForestContractState.cs
index 1bd12a4a..c21a6ff0 100755
--- a/contract/Forest/ForestContractState.cs
+++ b/contract/Forest/ForestContractState.cs
@@ -61,6 +61,9 @@ public partial class ForestContractState : ContractState
/// Symbol -> Address TxId -> CreateArtInfo
///
public MappedState
CreateArtInfoMap { get; set; }
+ public SingletonState MaxBatchCancelOfferCount { get; set; }
+
+ public SingletonState MaxBatchCancelListCount { get; set; }
}
}
\ No newline at end of file
diff --git a/contract/Forest/ForestContract_Buyers.cs b/contract/Forest/ForestContract_Buyers.cs
index d9a976df..dd167c35 100644
--- a/contract/Forest/ForestContract_Buyers.cs
+++ b/contract/Forest/ForestContract_Buyers.cs
@@ -534,6 +534,12 @@ public override Empty CancelOffer(CancelOfferInput input)
public override Empty CancelOfferListByExpireTime(CancelOfferListByExpireTimeInput input)
{
AssertContractInitialized();
+ CancelOfferList(input);
+ return new Empty();
+ }
+
+ private Empty CancelOfferList(CancelOfferListByExpireTimeInput input)
+ {
Assert(Context.Sender != null, "Invalid input data : Context.Sender");
Assert(input != null, "Invalid input data");
Assert(input.Symbol != null, "Invalid input data : Symbol");
@@ -548,15 +554,26 @@ public override Empty CancelOfferListByExpireTime(CancelOfferListByExpireTimeInp
Assert(offerList?.Value?.Count > 0, "Offer not exists");
- var cancelOfferList = offerList?.Value.Where(existOffer => input.CancelOfferList.Any(cannelOffer =>
- AreOffersEqual(existOffer, cannelOffer)
- )).ToList();
+ var cancelOfferList = new List();
+ var remainOfferList = new List();
+ foreach (var existOffer in offerList?.Value)
+ {
+ foreach (var cannelOffer in input.CancelOfferList)
+ {
+ if (AreOffersEqual(existOffer, cannelOffer))
+ {
+ cancelOfferList.Add(existOffer);
+ }
+ if (!AreOffersEqual(existOffer, cannelOffer))
+ {
+ remainOfferList.Add(existOffer);
+ }
+ }
+ }
Assert(cancelOfferList?.Count > 0, "Cannel Offer not exists");
var newOfferList = new OfferList();
- var remainOfferList = offerList?.Value.Where(existOffer => !input.CancelOfferList.Any(cannelOffer =>
- AreOffersEqual(existOffer, cannelOffer)
- )).ToList();
+
newOfferList.Value.Add(remainOfferList);
var cancelOfferMap = new Dictionary();
@@ -599,7 +616,7 @@ public override Empty CancelOfferListByExpireTime(CancelOfferListByExpireTimeInp
return new Empty();
}
-
+
private bool AreOffersEqual(Offer existOffer, CancelOffer cancelOffer)
{
return existOffer.To == cancelOffer.OfferTo &&
@@ -757,4 +774,35 @@ private void PerformMakeOffer(MakeOfferInput input, GetBalanceOutput originGetBa
Quantity = input.Quantity
});
}
+
+ public override Empty BatchCancelOfferList(BatchCancelOfferListInput input)
+ {
+ AssertContractInitialized();
+ RequireMaxBatchCancelOfferCountSet();
+ Assert(Context.Sender != null, "Invalid input data : Context.Sender");
+ Assert(input != null, "Invalid input data");
+ Assert(input.BatchCancelOfferInfo != null, "Invalid input data : BatchCancelOfferInfo");
+ Assert(input.BatchCancelOfferInfo.CancelOfferList != null, "Invalid input data : CancelOfferList");
+ var maxBatchCancelOfferCount = State.MaxBatchCancelOfferCount.Value;
+ Assert(input.BatchCancelOfferInfo.CancelOfferList.Count <= maxBatchCancelOfferCount, "Invalid cancel list count.");
+ foreach (var cancelOffer in input.BatchCancelOfferInfo.CancelOfferList)
+ {
+ var cancelInput = new CancelOfferListByExpireTimeInput()
+ {
+ Symbol = cancelOffer.Symbol,
+ CancelOfferList = {new CancelOffer()
+ {
+ ExpireTime = cancelOffer.ExpireTime,
+ OfferTo = cancelOffer.OfferTo,
+ Price = new Price()
+ {
+ Symbol = cancelOffer.Price.Symbol,
+ Amount = cancelOffer.Price.Amount
+ }
+ }}
+ };
+ CancelOfferList(cancelInput);
+ }
+ return new Empty();
+ }
}
\ No newline at end of file
diff --git a/contract/Forest/ForestContract_Helpers.cs b/contract/Forest/ForestContract_Helpers.cs
index 26661f7b..a6955b1f 100644
--- a/contract/Forest/ForestContract_Helpers.cs
+++ b/contract/Forest/ForestContract_Helpers.cs
@@ -342,4 +342,22 @@ private void CheckCreateArtParams(CreateArtInput input)
var size = State.AIImageSizeList.Value.Value;
Assert(State.AIImageSizeList.Value.Value.Contains(input.Size), $"Invalid input size");
}
+
+ private void RequireMaxBatchCancelOfferCountSet()
+ {
+ var maxCount = State.MaxBatchCancelOfferCount.Value;
+ if (maxCount <= 0)
+ {
+ State.MaxBatchCancelOfferCount.Value = DefaultMaxBatchCancelOfferCount;
+ }
+ }
+
+ private void RequireMaxBatchCancelListCountSet()
+ {
+ var maxCount = State.MaxBatchCancelListCount.Value;
+ if (maxCount <= 0)
+ {
+ State.MaxBatchCancelListCount.Value = DefaultMaxBatchCancelListCount;
+ }
+ }
}
\ No newline at end of file
diff --git a/contract/Forest/ForestContract_Sellers.cs b/contract/Forest/ForestContract_Sellers.cs
index da12a289..d816d548 100644
--- a/contract/Forest/ForestContract_Sellers.cs
+++ b/contract/Forest/ForestContract_Sellers.cs
@@ -138,6 +138,12 @@ public override Empty ListWithFixedPrice(ListWithFixedPriceInput input)
}
public override Empty Delist(DelistInput input)
+ {
+ SingleDelist(input);
+ return new Empty();
+ }
+
+ private Empty SingleDelist(DelistInput input)
{
Assert(input.Quantity > 0, "Quantity must be a positive integer.");
var listedNftInfoList = State.ListedNFTInfoListMap[input.Symbol][Context.Sender];
@@ -147,10 +153,18 @@ public override Empty Delist(DelistInput input)
}
Assert(input.Price != null, "Need to specific list record.");
- var listedNftInfo = listedNftInfoList.Value.FirstOrDefault(i =>
- i.Price.Amount == input.Price.Amount && i.Price.Symbol == input.Price.Symbol &&
- i.Owner == Context.Sender &&
- (input.StartTime == null ? true : input.StartTime.Seconds == i.Duration.StartTime.Seconds));
+ ListedNFTInfo listedNftInfo = null;
+
+ foreach (var i in listedNftInfoList.Value)
+ {
+ if (i.Price.Amount == input.Price.Amount && i.Price.Symbol == input.Price.Symbol &&
+ i.Owner == Context.Sender &&
+ (input.StartTime == null ? true : input.StartTime.Seconds == i.Duration.StartTime.Seconds))
+ {
+ listedNftInfo = i;
+ break;
+ }
+ }
if (listedNftInfo == null)
{
@@ -370,4 +384,33 @@ public override Empty Deal(DealInput input)
});
return new Empty();
}
+
+ public override Empty BatchCancelList(BatchCancelListInput input)
+ {
+ AssertContractInitialized();
+ RequireMaxBatchCancelListCountSet();
+ Assert(Context.Sender != null, "Invalid input data : Context.Sender");
+ Assert(input != null, "Invalid input data");
+ Assert(input.BatchCancelListInfo != null, "Invalid input data : Symbol");
+ Assert(input.BatchCancelListInfo.CancelList != null, "Invalid input data : CancelList");
+ var maxBatchCancelCount = State.MaxBatchCancelListCount.Value;
+ Assert(input.BatchCancelListInfo.CancelList.Count <= maxBatchCancelCount, "Invalid cancel list count.");
+ foreach (var cancelInfo in input.BatchCancelListInfo.CancelList)
+ {
+ var cancelInput = new DelistInput()
+ {
+ Symbol = cancelInfo.Symbol,
+ Quantity = cancelInfo.Quantity,
+ Price = new Price()
+ {
+ Symbol = cancelInfo.Price.Symbol,
+ Amount = cancelInfo.Price.Amount
+ },
+ StartTime = cancelInfo.StartTime
+ };
+ SingleDelist(cancelInput);
+ }
+ return new Empty();
+
+ }
}
\ No newline at end of file
diff --git a/contract/Forest/ForestContract_Views.cs b/contract/Forest/ForestContract_Views.cs
index 0edbe95e..e947dfc9 100644
--- a/contract/Forest/ForestContract_Views.cs
+++ b/contract/Forest/ForestContract_Views.cs
@@ -147,4 +147,14 @@ public override CreateArtInfo GetCreateArtInfo(GetCreateArtInfoInput input)
Assert(input != null, "Invalid TransactionId");
return State.CreateArtInfoMap[input.Address ?? Context.Sender][input.TransactionId];
}
+
+ public override Int32Value GetMaxBatchCancelOfferCount(Empty input)
+ {
+ return new Int32Value { Value = State.MaxBatchCancelOfferCount.Value };
+ }
+
+ public override Int32Value GetMaxBatchCancelListCount(Empty input)
+ {
+ return new Int32Value { Value = State.MaxBatchCancelListCount.Value };
+ }
}
\ No newline at end of file
diff --git a/protobuf/forest_contract.proto b/protobuf/forest_contract.proto
index 05cf01ea..9aa64e87 100755
--- a/protobuf/forest_contract.proto
+++ b/protobuf/forest_contract.proto
@@ -39,6 +39,11 @@ service ForestContract {
}
rpc CancelOfferListByExpireTime (CancelOfferListByExpireTimeInput) returns (google.protobuf.Empty) {
}
+
+ rpc BatchCancelOfferList (BatchCancelOfferListInput) returns (google.protobuf.Empty) {
+ }
+ rpc BatchCancelList (BatchCancelListInput) returns (google.protobuf.Empty){
+ }
rpc BatchBuyNow (BatchBuyNowInput) returns (google.protobuf.Empty){
}
@@ -74,7 +79,13 @@ service ForestContract {
rpc RemoveAIImageSize (google.protobuf.StringValue) returns (google.protobuf.Empty){
}
+
+ rpc SetMaxBatchCancelOfferCount(google.protobuf.Int32Value) returns (google.protobuf.Empty){
+ }
+ rpc SetMaxBatchCancelListCount(google.protobuf.Int32Value) returns (google.protobuf.Empty){
+ }
+
// Views.
rpc GetListedNFTInfoList (GetListedNFTInfoListInput) returns (ListedNFTInfoList) {
option (aelf.is_view) = true;
@@ -124,6 +135,14 @@ service ForestContract {
rpc GetCreateArtInfo (GetCreateArtInfoInput) returns (CreateArtInfo){
option (aelf.is_view) = true;
}
+
+ rpc GetMaxBatchCancelOfferCount(google.protobuf.Empty) returns (google.protobuf.Int32Value){
+ option (aelf.is_view) = true;
+ }
+
+ rpc GetMaxBatchCancelListCount(google.protobuf.Empty) returns (google.protobuf.Int32Value){
+ option (aelf.is_view) = true;
+ }
}
// Structs.
@@ -141,7 +160,6 @@ message ListedNFTInfoList {
repeated ListedNFTInfo value = 1;
}
-
message StringList {
repeated string value = 1;
}
@@ -269,6 +287,30 @@ message CancelOfferListByExpireTimeInput {
repeated CancelOffer cancel_offer_list =2;
}
+message BatchCancelOfferListInput {
+ BatchCancelOfferInfo batch_cancel_offer_info = 1;
+}
+
+message BatchCancelOfferInfo{
+ repeated CancelOfferList cancel_offer_list =1;
+}
+
+message CancelOfferList{
+ string symbol = 1;
+ google.protobuf.Timestamp expire_time = 2;
+ aelf.Address offer_to = 3;
+ Price price = 4;
+}
+
+message BatchCancelListInput{
+ BatchCancelListInfo batch_cancel_list_info = 1;
+}
+
+message BatchCancelListInfo{
+ repeated DelistInput cancel_list =1;
+}
+
+
message CancelOffer{
google.protobuf.Timestamp expire_time = 1;
aelf.Address offer_to = 2;
diff --git a/src/AElf.Boilerplate.MainChain/AElf.Boilerplate.MainChain.csproj b/src/AElf.Boilerplate.MainChain/AElf.Boilerplate.MainChain.csproj
index 34f47b89..f04a49e6 100755
--- a/src/AElf.Boilerplate.MainChain/AElf.Boilerplate.MainChain.csproj
+++ b/src/AElf.Boilerplate.MainChain/AElf.Boilerplate.MainChain.csproj
@@ -43,7 +43,7 @@
Contract
PreserveNewest
-
+
false
Contract
PreserveNewest
diff --git a/src/AElf.Boilerplate.TestBase/AElf.Boilerplate.TestBase.csproj b/src/AElf.Boilerplate.TestBase/AElf.Boilerplate.TestBase.csproj
index 22e32d0d..5e0b734c 100755
--- a/src/AElf.Boilerplate.TestBase/AElf.Boilerplate.TestBase.csproj
+++ b/src/AElf.Boilerplate.TestBase/AElf.Boilerplate.TestBase.csproj
@@ -56,7 +56,7 @@
Contract
PreserveNewest
-
+
false
Contract
PreserveNewest
diff --git a/test/Forest.Contracts.Auction.Tests/AuctionContractTests.cs b/test/Forest.Contracts.Auction.Tests/AuctionContractTests.cs
index 720e481e..06efefaa 100644
--- a/test/Forest.Contracts.Auction.Tests/AuctionContractTests.cs
+++ b/test/Forest.Contracts.Auction.Tests/AuctionContractTests.cs
@@ -347,7 +347,7 @@ public async Task CreateAuctionTests_Fail()
{
Symbol = "SEED-2"
});
- result.TransactionResult.Error.ShouldContain("Only support 721 type NFT.");
+ result.TransactionResult.Error.ShouldContain("Invalid input auction type.");
result = await AuctionContractStub.CreateAuction.SendWithExceptionAsync(new CreateAuctionInput
{
@@ -904,7 +904,7 @@ await TokenContractStub.Create.SendAsync(new CreateInput
Issuer = DefaultAddress,
Symbol = "SEED-2",
TokenName = "SEED-TEST",
- TotalSupply = 2,
+ TotalSupply = 1,
Decimals = 0,
IsBurnable = true,
LockWhiteList = { TokenContractAddress },
diff --git a/test/Forest.Tests/Forest.Tests.csproj b/test/Forest.Tests/Forest.Tests.csproj
index 93f107f6..6b4ba21b 100755
--- a/test/Forest.Tests/Forest.Tests.csproj
+++ b/test/Forest.Tests/Forest.Tests.csproj
@@ -97,8 +97,4 @@
-
-
-
-
\ No newline at end of file
diff --git a/test/Forest.Tests/ForestContractTests_CancelOffer.cs b/test/Forest.Tests/ForestContractTests_CancelOffer.cs
index a65edd9b..65f0e08d 100644
--- a/test/Forest.Tests/ForestContractTests_CancelOffer.cs
+++ b/test/Forest.Tests/ForestContractTests_CancelOffer.cs
@@ -1251,4 +1251,114 @@ await BuyerForestContractStub.MakeOffer.SendAsync(new MakeOfferInput()
#endregion
}
+ [Fact]
+ public async void BatchCancelOffer_Test()
+ {
+ await InitializeForestContract();
+ await PrepareNftData();
+
+ var sellPrice = Elf(5_0000_0000);
+ var offerPrice = Elf(5_0000_0000);
+ var offerQuantity = 1;
+ var dealQuantity = 1;
+ var serviceFee = dealQuantity * sellPrice.Amount * ServiceFeeRate / 10000;
+
+ var expireTime = Timestamp.FromDateTime(DateTime.UtcNow.AddMinutes(5));
+ #region user buy
+ {
+ // user2 make offer to user1
+ await BuyerForestContractStub.MakeOffer.SendAsync(new MakeOfferInput()
+ {
+ Symbol = NftSymbol,
+ OfferTo = User1Address,
+ Quantity = offerQuantity,
+ Price = offerPrice,
+ ExpireTime = expireTime
+ });
+
+ // user2 make offer to user1
+ await BuyerForestContractStub.MakeOffer.SendAsync(new MakeOfferInput()
+ {
+ Symbol = NftSymbol2,
+ OfferTo = User1Address,
+ Quantity = offerQuantity,
+ Price = offerPrice,
+ ExpireTime = expireTime
+ });
+ }
+ #endregion
+
+ #region check offer list
+ {
+ // list offers just sent
+ var offerList1 = await BuyerForestContractStub.GetOfferList.SendAsync(new GetOfferListInput()
+ {
+ Symbol = NftSymbol,
+ Address = User2Address,
+ });
+ offerList1.Output.Value.Count.ShouldBeGreaterThan(0);
+ offerList1.Output.Value[0].To.ShouldBe(User1Address);
+ offerList1.Output.Value[0].From.ShouldBe(User2Address);
+ offerList1.Output.Value[0].Quantity.ShouldBe(offerQuantity);
+
+ var offerList2 = await BuyerForestContractStub.GetOfferList.SendAsync(new GetOfferListInput()
+ {
+ Symbol = NftSymbol2,
+ Address = User2Address,
+ });
+ offerList2.Output.Value.Count.ShouldBeGreaterThan(0);
+ offerList2.Output.Value[0].To.ShouldBe(User1Address);
+ offerList2.Output.Value[0].From.ShouldBe(User2Address);
+ offerList2.Output.Value[0].Quantity.ShouldBe(offerQuantity);
+ }
+ #endregion
+
+ #region Admin Cancel order
+ {
+ var batchCancelOfferInput = new BatchCancelOfferListInput()
+ {
+ BatchCancelOfferInfo = new BatchCancelOfferInfo()
+ {
+ CancelOfferList = { new CancelOfferList()
+ {
+ Symbol = NftSymbol,
+ ExpireTime = expireTime,
+ OfferTo = User1Address,
+ Price = offerPrice
+ },
+ new CancelOfferList()
+ {
+ Symbol = NftSymbol2,
+ ExpireTime = expireTime,
+ OfferTo = User1Address,
+ Price = offerPrice
+ }
+ }
+ }
+ };
+ await BuyerForestContractStub.BatchCancelOfferList.SendAsync(batchCancelOfferInput);
+ }
+ #endregion
+
+ #region check offer list
+ {
+ var offerList1 = await BuyerForestContractStub.GetOfferList.SendAsync(new GetOfferListInput()
+ {
+ Symbol = NftSymbol,
+ Address = User2Address,
+ });
+ offerList1.Output.Value.Count.ShouldBe(0);
+
+
+ var offerList2 = await BuyerForestContractStub.GetOfferList.SendAsync(new GetOfferListInput()
+ {
+ Symbol = NftSymbol2,
+ Address = User2Address,
+ });
+ offerList2.Output.Value.Count.ShouldBe(0);
+ }
+ #endregion
+ }
+
+
}
\ No newline at end of file
diff --git a/test/Forest.Tests/ForestContractTests_List.cs b/test/Forest.Tests/ForestContractTests_List.cs
index 79d2684c..93cdfc3e 100644
--- a/test/Forest.Tests/ForestContractTests_List.cs
+++ b/test/Forest.Tests/ForestContractTests_List.cs
@@ -13,6 +13,8 @@ namespace Forest;
public class ForestContractListTests : ForestContractTestBase
{
private const string NftSymbol = "TESTNFT-1";
+ private const string NftSymbol2 = "TESTNFT-2";
+
private const string ElfSymbol = "ELF";
private const int ServiceFeeRate = 1000; // 10%
private const long InitializeElfAmount = 10000_0000_0000;
@@ -106,6 +108,28 @@ private async Task PrepareNftData()
log2.Amount.ShouldBe(10);
log2.Memo.ShouldBe("");
log2.To.ShouldBe(User1Address);
+
+ //create and issue nft2
+ {
+ // create NFT via MULTI-TOKEN-CONTRACT
+ await UserTokenContractStub.Create.SendAsync(new CreateInput
+ {
+ Symbol = NftSymbol2,
+ TokenName = NftSymbol2,
+ TotalSupply = 100,
+ Decimals = 0,
+ Issuer = User1Address,
+ IsBurnable = false,
+ IssueChainId = 0,
+ ExternalInfo = new ExternalInfo()
+ });
+ await UserTokenContractStub.Issue.SendAsync(new IssueInput()
+ {
+ Symbol = NftSymbol2,
+ Amount = 10,
+ To = User1Address
+ });
+ }
// got 100-totalSupply and 10-supply
var tokenInfo = await UserTokenContractStub.GetTokenInfo.SendAsync(new GetTokenInfoInput()
{
@@ -1221,22 +1245,22 @@ await Seller1ForestContractStub.Delist.SendAsync(new DelistInput
}
- private async Task InitListInfo(int listQuantity, int inputSellPrice, int approveQuantity)
+ private async Task InitListInfo(int listQuantity, int inputSellPrice, int approveQuantity, string symbol = null, Timestamp startTime = null)
{
var sellPrice = Elf(inputSellPrice);
{
await UserTokenContractStub.Approve.SendAsync(new ApproveInput()
- { Spender = ForestContractAddress, Symbol = NftSymbol, Amount = approveQuantity });
+ { Spender = ForestContractAddress, Symbol = symbol ?? NftSymbol, Amount = approveQuantity });
var executionResult = await Seller1ForestContractStub.ListWithFixedPrice.SendAsync(
new ListWithFixedPriceInput
{
- Symbol = NftSymbol,
+ Symbol = symbol ?? NftSymbol,
Quantity = listQuantity,
IsWhitelistAvailable = true,
Price = sellPrice,
Duration = new ListWithFixedPriceDuration()
{
- StartTime = Timestamp.FromDateTimeOffset(DateTimeOffset.UtcNow.AddSeconds(approveQuantity)),
+ StartTime = startTime?? Timestamp.FromDateTimeOffset(DateTimeOffset.UtcNow.AddSeconds(approveQuantity)),
PublicTime = Timestamp.FromDateTimeOffset(DateTimeOffset.UtcNow.AddSeconds(approveQuantity)),
}
});
@@ -1245,7 +1269,7 @@ await UserTokenContractStub.Approve.SendAsync(new ApproveInput()
.NonIndexed);
log.Owner.ShouldBe(User1Address);
log.Quantity.ShouldBe(listQuantity);
- log.Symbol.ShouldBe(NftSymbol);
+ log.Symbol.ShouldBe( symbol ?? NftSymbol);
log.Price.Symbol.ShouldBe(ElfSymbol);
log.Price.Amount.ShouldBe(inputSellPrice);
log.Duration.StartTime.ShouldNotBeNull();
@@ -1255,7 +1279,7 @@ await UserTokenContractStub.Approve.SendAsync(new ApproveInput()
var listedNftInfo = (await Seller1ForestContractStub.GetListedNFTInfoList.CallAsync(
new GetListedNFTInfoListInput
{
- Symbol = NftSymbol,
+ Symbol = symbol ?? NftSymbol,
Owner = User1Address
})).Value.Last();
listedNftInfo.Price.Symbol.ShouldBe("ELF");
@@ -1268,13 +1292,13 @@ await UserTokenContractStub.Approve.SendAsync(new ApproveInput()
}
}
- private async Task QueryLastByStartAscListInfo(int intpuListQuantity, int inputSellPrice)
+ private async Task QueryLastByStartAscListInfo(int intpuListQuantity, int inputSellPrice, string symbol = null)
{
{
var listedNftInfo = (await Seller1ForestContractStub.GetListedNFTInfoList.CallAsync(
new GetListedNFTInfoListInput
{
- Symbol = NftSymbol,
+ Symbol = symbol ?? NftSymbol,
Owner = User1Address
})).Value.Last();
listedNftInfo.Price.Symbol.ShouldBe("ELF");
@@ -3138,4 +3162,102 @@ await UserTokenContractStub.Approve.SendAsync(new ApproveInput()
}
}
+
+ [Fact]
+ public async void BatchCancelListTest()
+ {
+ //basic begin
+ int approveQuantity = 0;
+ await InitializeForestContract();
+ await PrepareNftData();
+
+ int inputListQuantity1 = 1;
+ int inputSellPrice1 = 2;
+ var startTime1 = Timestamp.FromDateTimeOffset(DateTimeOffset.UtcNow.AddSeconds(approveQuantity));
+
+ approveQuantity += inputListQuantity1;
+ await InitListInfo(inputListQuantity1, inputSellPrice1, approveQuantity, NftSymbol, startTime1);
+ await QueryLastByStartAscListInfo(inputListQuantity1, inputSellPrice1, NftSymbol);
+ await QueryFirstByStartAscListInfo(inputListQuantity1, inputSellPrice1);
+
+ int inputListQuantity2 = 2;
+ int inputSellPrice2 = 2;
+ var startTime2 = Timestamp.FromDateTimeOffset(DateTimeOffset.UtcNow.AddSeconds(approveQuantity));
+
+ approveQuantity += inputListQuantity2;
+ await InitListInfo(inputListQuantity2, inputSellPrice2, approveQuantity, NftSymbol2, startTime2);
+ await QueryLastByStartAscListInfo(inputListQuantity2, inputSellPrice2, NftSymbol2);
+ await QueryFirstByStartAscListInfo(inputListQuantity1, inputSellPrice1);
+
+ var batchInput = new BatchCancelListInput()
+ {
+ BatchCancelListInfo = new BatchCancelListInfo()
+ {
+ CancelList = { new DelistInput()
+ {
+ Symbol = NftSymbol,
+ Quantity = inputListQuantity1,
+ Price = new Price()
+ {
+ Symbol = ElfSymbol,
+ Amount = inputSellPrice1,
+ },
+ StartTime = startTime1
+ },new DelistInput()
+ {
+ Symbol = NftSymbol2,
+ Quantity = inputListQuantity2,
+ Price = new Price()
+ {
+ Symbol = ElfSymbol,
+ Amount = inputSellPrice2,
+ },
+ StartTime = startTime2
+ } }
+ }
+ };
+
+ {
+ var listedNftInfo1 = (await Seller1ForestContractStub.GetListedNFTInfoList.CallAsync(
+ new GetListedNFTInfoListInput
+ {
+ Symbol = NftSymbol,
+ Owner = User1Address
+ })).Value.First();
+ listedNftInfo1.ShouldNotBeNull();
+ listedNftInfo1.Symbol.ShouldBe(NftSymbol);
+
+ var listedNftInfo2 = (await Seller1ForestContractStub.GetListedNFTInfoList.CallAsync(
+ new GetListedNFTInfoListInput
+ {
+ Symbol = NftSymbol2,
+ Owner = User1Address
+ })).Value.First();
+ listedNftInfo2.ShouldNotBeNull();
+ listedNftInfo2.Symbol.ShouldBe(NftSymbol2);
+
+ }
+
+ {
+ //batch cancel list
+ await Seller1ForestContractStub.BatchCancelList.SendAsync(batchInput);
+
+ var listedNftInfo1 = (await Seller1ForestContractStub.GetListedNFTInfoList.CallAsync(
+ new GetListedNFTInfoListInput
+ {
+ Symbol = NftSymbol,
+ Owner = User1Address
+ })).Value;
+ listedNftInfo1.ShouldBeEmpty();
+
+ var listedNftInfo2 = (await Seller1ForestContractStub.GetListedNFTInfoList.CallAsync(
+ new GetListedNFTInfoListInput
+ {
+ Symbol = NftSymbol2,
+ Owner = User1Address
+ })).Value;
+ listedNftInfo1.ShouldBeEmpty();
+
+ }
+ }
}
\ No newline at end of file
diff --git a/test/Forest.Tests/ForestContractTests_MakeOffer_whiteUser.cs b/test/Forest.Tests/ForestContractTests_MakeOffer_whiteUser.cs
index e18ef6c7..963b2c95 100644
--- a/test/Forest.Tests/ForestContractTests_MakeOffer_whiteUser.cs
+++ b/test/Forest.Tests/ForestContractTests_MakeOffer_whiteUser.cs
@@ -1936,7 +1936,7 @@ await Seller1ForestContractStub.ListWithFixedPrice.SendAsync(new ListWithFixedPr
Duration = new ListWithFixedPriceDuration()
{
// start 5min ago
- StartTime = Timestamp.FromDateTime(DateTime.UtcNow).AddSeconds(-2),
+ StartTime = Timestamp.FromDateTime(DateTime.UtcNow).AddSeconds(-20),
// public 10min after
PublicTime = Timestamp.FromDateTime(DateTime.UtcNow).AddSeconds(-2),
DurationMinutes = 1 * 60,
@@ -2192,7 +2192,7 @@ await Seller1ForestContractStub.ListWithFixedPrice.SendAsync(new ListWithFixedPr
try
{
await UserTokenContractStub.Approve.SendAsync(new ApproveInput() { Spender = ForestContractAddress, Symbol = NftSymbol, Amount = 1 });
- await User2TokenContractStub.Approve.SendAsync(new ApproveInput() { Spender = ForestContractAddress, Symbol = whitePrice.Symbol, Amount = sellPrice.Amount*2 });
+ await User2TokenContractStub.Approve.SendAsync(new ApproveInput() { Spender = ForestContractAddress, Symbol = whitePrice.Symbol, Amount = sellPrice.Amount-1 });
var executionResult = await BuyerForestContractStub.MakeOffer.SendAsync(new MakeOfferInput()
{
Symbol = NftSymbol,
@@ -2206,7 +2206,7 @@ await Seller1ForestContractStub.ListWithFixedPrice.SendAsync(new ListWithFixedPr
{
errorMessage = e.Message;
}
- errorMessage.ShouldContain("[TransferFrom]Insufficient allowance");
+ errorMessage.ShouldContain("The allowance you set is less than required");
}
#endregion
diff --git a/test/Forest.Tests/ForestContractTests_Security.cs b/test/Forest.Tests/ForestContractTests_Security.cs
index e5c3b2b5..1e0bb344 100644
--- a/test/Forest.Tests/ForestContractTests_Security.cs
+++ b/test/Forest.Tests/ForestContractTests_Security.cs
@@ -1259,6 +1259,14 @@ await UserTokenContractStub.Approve.SendAsync(new ApproveInput()
Amount = issueAmount,
Spender = ForestContractAddress
});
+ await AdminForestContractStub.SetBizConfig.SendAsync(new BizConfig()
+ {
+ MaxListCount = 3,
+ MaxOfferCount = 3,
+ MaxOfferDealCount = 3,
+ MaxTokenWhitelistCount = 3
+
+ });
var bizConfig = await Seller1ForestContractStub.GetBizConfig.SendAsync(new Empty());
@@ -1336,6 +1344,13 @@ await UserTokenContractStub.Issue.SendAsync(new IssueInput()
#region create offer reach maxCount
+ await AdminForestContractStub.SetBizConfig.SendAsync(new BizConfig()
+ {
+ MaxListCount = 3,
+ MaxOfferCount = 3,
+ MaxOfferDealCount = 3,
+ MaxTokenWhitelistCount = 3
+ });
var bizConfig = await Seller1ForestContractStub.GetBizConfig.SendAsync(new Empty());
await UserTokenContractStub.Approve.SendAsync(new ApproveInput()
diff --git a/test/Forest.Tests/ForestContractTests_Views.cs b/test/Forest.Tests/ForestContractTests_Views.cs
index 87d9de34..6417a2d0 100644
--- a/test/Forest.Tests/ForestContractTests_Views.cs
+++ b/test/Forest.Tests/ForestContractTests_Views.cs
@@ -1191,4 +1191,28 @@ await BuyerForestContractStub.MakeOffer.SendAsync(new MakeOfferInput()
}
+ [Fact]
+ public async void GetMaxBatchCancelOfferListCount_Test()
+ {
+ await InitializeForestContract();
+ var count = await AdminForestContractStub.GetMaxBatchCancelOfferCount.CallAsync(new Empty());
+ count.Value.ShouldBe(0);
+
+ await AdminForestContractStub.SetMaxBatchCancelOfferCount.SendAsync(new Int32Value(){Value = 10});
+ count = await AdminForestContractStub.GetMaxBatchCancelOfferCount.CallAsync(new Empty());
+ count.Value.ShouldBe(10);
+ }
+
+ [Fact]
+ public async void GetMaxBatchCancelListCount_Test()
+ {
+ await InitializeForestContract();
+ var count = await AdminForestContractStub.GetMaxBatchCancelListCount.CallAsync(new Empty());
+ count.Value.ShouldBe(0);
+
+ await AdminForestContractStub.SetMaxBatchCancelListCount.SendAsync(new Int32Value(){Value = 10});
+ count = await AdminForestContractStub.GetMaxBatchCancelListCount.CallAsync(new Empty());
+ count.Value.ShouldBe(10);
+ }
+
}
\ No newline at end of file