diff --git a/e2e/common/index.ts b/e2e/common/index.ts index 3fd027a71..bb4c671fe 100644 --- a/e2e/common/index.ts +++ b/e2e/common/index.ts @@ -400,28 +400,34 @@ export const FUTUREPASS_PRECOMPILE_ABI = [ export const MARKET_PLACE_ABI = [ "event MarketplaceRegister(address indexed sender, uint256 indexed marketplaceId, address marketplace_account)", - "event FixedPriceSaleList(address indexed seller, uint256 indexed listingId, uint256 indexed fixedPrice, uint256[] serialNumbers, address collectionAddress)", - "event FixedPriceSaleUpdate(uint256 indexed collectionId, uint256 indexed listingId, uint256 indexed newPrice, address sender, uint256[] serialNumbers)", - "event FixedPriceSaleComplete(uint256 indexed collectionId, uint256 indexed listingId, uint256 indexed fixedPrice, address sender, uint256[] serialNumbers)", - "event AuctionOpen(uint256 indexed collectionId, uint256 indexed listingId, uint256 indexed reservePrice, address sender, uint256[] serialNumbers)", - "event Bid(address indexed bidder, uint256 indexed listingId, uint256 indexed amount)", - "event FixedPriceSaleClose(uint256 indexed collectionId, uint256 indexed listingId, address sender, uint256[] serialNumbers)", //uint256,uint256,address,uint256[] - "event AuctionClose(uint256 indexed collectionId, uint256 indexed listingId, address sender, uint256[] serialNumbers)", - "event Offer(uint256 indexed offerId, address indexed sender, uint256 collectionId, uint256 seriesId)", - "event OfferCancel(uint256 indexed offerId, address indexed sender, uint256 collectionId, uint256 seriesId)", - "event OfferAccept(uint256 indexed offerId, uint256 indexed amount, address indexed sender, uint256 collectionId, uint256 seriesId)", // uint256,uint256,address,uint256 + "event FixedPriceSaleListNFT(address indexed seller, uint256 indexed listingId, uint256 indexed fixedPrice, uint256[] serialNumbers, address collectionAddress, uint128 marketplace_id)", + "event FixedPriceSaleListSFT(address indexed seller, uint256 indexed listingId, uint256 indexed fixedPrice, uint256[] serialNumbers, address collectionAddress, uint128 marketplace_id, uint256[] quantities)", + "event FixedPriceSaleUpdate(uint256 indexed collectionId, uint256 indexed listingId, uint256 indexed newPrice, address sender, uint256[] serialNumbers, uint128 marketplace_id)", + "event FixedPriceSaleComplete(uint256 indexed collectionId, uint256 indexed listingId, uint256 indexed fixedPrice, address sender, uint256[] serialNumbers, uint128 marketplace_id)", + "event AuctionOpenNFT(uint256 indexed collectionId, uint256 indexed listingId, uint256 indexed reservePrice, address sender, uint256[] serialNumbers, uint128 marketplace_id)", + "event AuctionOpenSFT(uint256 indexed collectionId, uint256 indexed listingId, uint256 indexed reservePrice, address sender, uint256[] serialNumbers, uint128 marketplace_id)", + "event Bid(address indexed bidder, uint256 indexed listingId, uint256 indexed amount, uint128 marketplace_id)", + "event FixedPriceSaleClose(uint256 indexed collectionId, uint256 indexed listingId, address sender, uint256[] serialNumbers, uint128 marketplace_id)", //uint256,uint256,address,uint256[] + "event AuctionClose(uint256 indexed collectionId, uint256 indexed listingId, address sender, uint256[] serialNumbers, uint128 marketplace_id)", + "event Offer(uint256 indexed offerId, address indexed sender, uint256 collectionId, uint256 seriesId, uint128 marketplace_id)", + "event OfferCancel(uint256 indexed offerId, address indexed sender, uint256 collectionId, uint256 seriesId, uint128 marketplace_id)", + "event OfferAccept(uint256 indexed offerId, uint256 indexed amount, address indexed sender, uint256 collectionId, uint256 seriesId, uint128 marketplace_id)", // uint256,uint256,address,uint256 "function registerMarketplace(address marketplaceAccount, uint256 entitlement) external returns (uint marketplaceId)", "function sellNftWithMarketplaceId(address collectionAddress, uint256[] calldata serialNumberIds, address buyer, address paymentAsset, uint256 fixedPrice, uint256 duration, uint32 marketplaceId) external returns (uint listingId)", - "function sellNftWithoutMarketplace(address collectionAddress, uint256[] calldata serialNumberIds, address buyer, address paymentAsset, uint256 fixedPrice, uint256 duration) external returns (uint listingId)", + "function sellNft(address collectionAddress, uint256[] calldata serialNumberIds, address buyer, address paymentAsset, uint256 fixedPrice, uint256 duration, uint32 marketplace_id) external returns (uint listingId)", + "function sellSftWithMarketplaceId(address collectionAddress, uint256[] calldata serialNumberIds, uint256[] calldata quantities, address buyer, address paymentAsset, uint256 fixedPrice, uint256 duration, uint32 marketplaceId) external returns (uint listingId)", + "function sellSft(address collectionAddress, uint256[] calldata serialNumberIds, uint256[] calldata quantities, address buyer, address paymentAsset, uint256 fixedPrice, uint256 duration, uint32 marketplace_id) external returns (uint listingId)", "function updateFixedPrice(uint128 listingId, uint256 newPrice) external", "function buy(uint128 listingId) external payable", "function auctionNftWithMarketplaceId(address collectionAddress, uint256[] calldata serialNumberIds, address paymentAsset, uint256 reservePrice, uint256 duration, uint256 marketplaceId)", - "function auctionNftWithoutMarketplace(address collectionAddress, uint256[] calldata serialNumberIds, address paymentAsset, uint256 reservePrice, uint256 duration)", + "function auctionNft(address collectionAddress, uint256[] calldata serialNumberIds, address paymentAsset, uint256 reservePrice, uint256 duration, uint32 marketplaceId)", + "function auctionSftWithMarketplaceId(address collectionAddress, uint256[] calldata serialNumberIds, uint256[] calldata quantities, address paymentAsset, uint256 reservePrice, uint256 duration, uint32 marketplaceId)", + "function auctionSft(address collectionAddress, uint256[] calldata serialNumberIds, uint256[] calldata quantities, address paymentAsset, uint256 reservePrice, uint256 duration, uint32 marketplaceId)", "function bid(uint128 listingId, uint256 amount) external", "function cancelSale(uint128 listingId) external", "function makeSimpleOfferWithMarketplaceId(address collectionAddress, uint32 serialNumber, uint256 amount, address assetId, uint32 marketplaceId) external returns (uint offerId)", - "function makeSimpleOfferWithoutMarketplace(address collectionAddress, uint32 serialNumber, uint256 amount, address assetId) external returns (uint offerId)", + "function makeSimpleOffer(address collectionAddress, uint32 serialNumber, uint256 amount, address assetId, uint32 marketplaceId) external returns (uint offerId)", "function cancelOffer(uint64 offerId) external", "function acceptOffer(uint64 offerId) external", diff --git a/e2e/test/MarketPlace/MarketPlacePrecompile.test.ts b/e2e/test/MarketPlace/MarketPlacePrecompile.test.ts index c3c3aa814..be473a7a8 100644 --- a/e2e/test/MarketPlace/MarketPlacePrecompile.test.ts +++ b/e2e/test/MarketPlace/MarketPlacePrecompile.test.ts @@ -112,7 +112,7 @@ describe("Marketplace Precompile", function () { const sellNftTx = await marketPlacePrecompile .connect(bobSigner) - .sellNftWithMarketplaceId( + .sellNft( erc721Precompile.address, sellNFTSeries, alithSigner.address, @@ -122,41 +122,17 @@ describe("Marketplace Precompile", function () { marketplaceId, ); const receipt = await sellNftTx.wait(); - const [seller, listingId, fixedPriceFromCall, serialNumbers, collectionAddress] = (receipt?.events as any)[0].args; - expect((receipt?.events as any)[0].event).to.equal("FixedPriceSaleList"); - expect(collectionAddress).to.equal(erc721Precompile.address); - expect(listingId.toNumber()).to.gte(0); - expect(fixedPriceFromCall.toNumber()).to.equal(fixedPrice); - expect(seller).to.equal(bobSigner.address); - const s = serialNumbers.map((s: BigNumber) => s.toNumber()); - expect(JSON.stringify(s)).to.equal(JSON.stringify(sellNFTSeries)); - }); - - it("sell nft without marketplace", async () => { - const sellNFTSeries = [34, 35, 36]; - const paymentAsset = web3.utils.toChecksumAddress("0xCCCCCCCC00000002000000000000000000000000"); //xrp token address - const fixedPrice = 1000000; - const duration = 1000; //blocks - - const sellNftTx = await marketPlacePrecompile - .connect(bobSigner) - .sellNftWithoutMarketplace( - erc721Precompile.address, - sellNFTSeries, - alithSigner.address, - paymentAsset, - fixedPrice, - duration, - ); - const receipt = await sellNftTx.wait(); - const [seller, listingId, fixedPriceFromCall, serialNumbers, collectionAddress] = (receipt?.events as any)[0].args; - expect((receipt?.events as any)[0].event).to.equal("FixedPriceSaleList"); + const [seller, listingId, fixedPriceFromCall, serialNumbers, collectionAddress, marketplaceIdArgs] = ( + receipt?.events as any + )[0].args; + expect((receipt?.events as any)[0].event).to.equal("FixedPriceSaleListNFT"); expect(collectionAddress).to.equal(erc721Precompile.address); expect(listingId.toNumber()).to.gte(0); expect(fixedPriceFromCall.toNumber()).to.equal(fixedPrice); expect(seller).to.equal(bobSigner.address); const s = serialNumbers.map((s: BigNumber) => s.toNumber()); expect(JSON.stringify(s)).to.equal(JSON.stringify(sellNFTSeries)); + expect(marketplaceIdArgs.toNumber()).to.gte(0); }); it("auction nft with marketplace", async () => { @@ -168,37 +144,10 @@ describe("Marketplace Precompile", function () { const auctionNftTx = await marketPlacePrecompile .connect(bobSigner) - .auctionNftWithMarketplaceId( - erc721Precompile.address, - auctionNFTSeries, - paymentAsset, - reservePrice, - duration, - marketplaceId, - ); + .auctionNft(erc721Precompile.address, auctionNFTSeries, paymentAsset, reservePrice, duration, marketplaceId); const receipt = await auctionNftTx.wait(); const [collectionId, listingId, reservePriceFromChain, seller, serialNumbers] = (receipt?.events as any)[0].args; - expect((receipt?.events as any)[0].event).to.equal("AuctionOpen"); - expect(collectionId.toNumber()).to.gte(0); - expect(listingId.toNumber()).to.gte(0); - expect(reservePriceFromChain.toNumber()).to.equal(reservePrice); - expect(seller).to.equal(bobSigner.address); - const s = serialNumbers.map((s: BigNumber) => s.toNumber()); - expect(JSON.stringify(s)).to.equal(JSON.stringify(auctionNFTSeries)); - }); - - it("auction nft without marketplace", async () => { - const auctionNFTSeries = [7, 8]; - const paymentAsset = web3.utils.toChecksumAddress("0xCCCCCCCC00000002000000000000000000000000"); //xrp token address - const reservePrice = 1000000; - const duration = 10000; //blocks - - const auctionNftTx = await marketPlacePrecompile - .connect(bobSigner) - .auctionNftWithoutMarketplace(erc721Precompile.address, auctionNFTSeries, paymentAsset, reservePrice, duration); - const receipt = await auctionNftTx.wait(); - const [collectionId, listingId, reservePriceFromChain, seller, serialNumbers] = (receipt?.events as any)[0].args; - expect((receipt?.events as any)[0].event).to.equal("AuctionOpen"); + expect((receipt?.events as any)[0].event).to.equal("AuctionOpenNFT"); expect(collectionId.toNumber()).to.gte(0); expect(listingId.toNumber()).to.gte(0); expect(reservePriceFromChain.toNumber()).to.equal(reservePrice); @@ -215,31 +164,15 @@ describe("Marketplace Precompile", function () { const offerNftTx = await marketPlacePrecompile .connect(alithSigner) - .makeSimpleOfferWithMarketplaceId(erc721Precompile.address, offerSeries, amount, paymentAsset, marketplaceId); - const receipt = await offerNftTx.wait(); - const [offerId, buyer, collectionId, serialId] = (receipt?.events as any)[0].args; - expect((receipt?.events as any)[0].event).to.equal("Offer"); - expect(offerId.toNumber()).to.gte(0); - expect(collectionId.toNumber()).to.gte(0); - expect(serialId.toNumber()).to.equal(offerSeries); - expect(buyer).to.equal(alithSigner.address); - }); - - it("make simple offer without marketplace id", async () => { - const offerSeries = 11; - const paymentAsset = web3.utils.toChecksumAddress("0xCCCCCCCC00000002000000000000000000000000"); //xrp token address - const amount = 1000000; - - const offerNftTx = await marketPlacePrecompile - .connect(alithSigner) - .makeSimpleOfferWithoutMarketplace(erc721Precompile.address, offerSeries, amount, paymentAsset); + .makeSimpleOffer(erc721Precompile.address, offerSeries, amount, paymentAsset, marketplaceId); const receipt = await offerNftTx.wait(); - const [offerId, buyer, collectionId, serialId] = (receipt?.events as any)[0].args; + const [offerId, buyer, collectionId, serialId, marketplaceIdArgs] = (receipt?.events as any)[0].args; expect((receipt?.events as any)[0].event).to.equal("Offer"); expect(offerId.toNumber()).to.gte(0); expect(collectionId.toNumber()).to.gte(0); expect(serialId.toNumber()).to.equal(offerSeries); expect(buyer).to.equal(alithSigner.address); + expect(marketplaceIdArgs.toNumber()).to.gte(0); }); it("buy", async () => { @@ -251,7 +184,7 @@ describe("Marketplace Precompile", function () { const sellNftTx = await marketPlacePrecompile .connect(bobSigner) - .sellNftWithMarketplaceId( + .sellNft( erc721Precompile.address, sellNFTSeries, alithSigner.address, @@ -289,14 +222,7 @@ describe("Marketplace Precompile", function () { // precompile const auctionTx = await marketPlacePrecompile .connect(bobSigner) - .auctionNftWithMarketplaceId( - erc721Precompile.address, - auctionNFTSeries, - paymentAsset, - reservePrice, - duration, - marketplaceId, - ); + .auctionNft(erc721Precompile.address, auctionNFTSeries, paymentAsset, reservePrice, duration, marketplaceId); const auctionReceipt = await auctionTx.wait(); const [, listingIdForAuction, , ,] = (auctionReceipt?.events as any)[0].args; @@ -321,22 +247,14 @@ describe("Marketplace Precompile", function () { const sellNftTx = await marketPlacePrecompile .connect(bobSigner) - .sellNftWithMarketplaceId( - erc721Precompile.address, - sellNFTSeries, - buyer, - paymentAsset, - fixedPrice, - duration, - marketplaceId, - ); + .sellNft(erc721Precompile.address, sellNFTSeries, buyer, paymentAsset, fixedPrice, duration, marketplaceId); const sellReceipt = await sellNftTx.wait(); const [, listingId, , ,] = (sellReceipt?.events as any)[0].args; const cancelSaleTx = await marketPlacePrecompile.connect(bobSigner).cancelSale(listingId); const receipt = await cancelSaleTx.wait(); - const [collectionId, listingIdCanceled, caller, seriesIds] = (receipt?.events as any)[0].args; + const [collectionId, listingIdCanceled, caller, seriesIds, marketplaceIdArgs] = (receipt?.events as any)[0].args; expect((receipt?.events as any)[0].event).to.equal("FixedPriceSaleClose"); expect(collectionId.toNumber()).to.gte(0); @@ -344,27 +262,23 @@ describe("Marketplace Precompile", function () { expect(caller).to.equal(bobSigner.address); const s = seriesIds.map((s: BigNumber) => s.toNumber()); expect(JSON.stringify(s)).to.equal(JSON.stringify(sellNFTSeries)); + expect(marketplaceIdArgs.toNumber()).to.gte(0); const auctionNFTSeries = [24, 25, 26]; const reservePrice = 70000; const auctionNftTx = await marketPlacePrecompile .connect(bobSigner) - .auctionNftWithMarketplaceId( - erc721Precompile.address, - auctionNFTSeries, - paymentAsset, - reservePrice, - duration, - marketplaceId, - ); + .auctionNft(erc721Precompile.address, auctionNFTSeries, paymentAsset, reservePrice, duration, marketplaceId); const auctionReceipt = await auctionNftTx.wait(); const [, listingIdForAuction, , ,] = (auctionReceipt?.events as any)[0].args; const cancelAuctionSaleTx = await marketPlacePrecompile.connect(bobSigner).cancelSale(listingIdForAuction); const auctionCancelReceipt = await cancelAuctionSaleTx.wait(); - const [collectionId1, listingIdCanceled1, caller1, seriesIds1] = (auctionCancelReceipt?.events as any)[0].args; + const [collectionId1, listingIdCanceled1, caller1, seriesIds1, marketplaceIdArgs1] = ( + auctionCancelReceipt?.events as any + )[0].args; expect((receipt?.events as any)[0].event).to.equal("FixedPriceSaleClose"); expect(collectionId1.toNumber()).to.gte(0); @@ -372,6 +286,7 @@ describe("Marketplace Precompile", function () { expect(caller1).to.equal(bobSigner.address); const s1 = seriesIds1.map((s: BigNumber) => s.toNumber()); expect(JSON.stringify(s1)).to.equal(JSON.stringify(auctionNFTSeries)); + expect(marketplaceIdArgs1.toNumber()).to.gte(0); }); it("update fixed price", async () => { @@ -384,15 +299,7 @@ describe("Marketplace Precompile", function () { const sellNftTx = await marketPlacePrecompile .connect(bobSigner) - .sellNftWithMarketplaceId( - erc721Precompile.address, - sellNFTSeries, - buyer, - paymentAsset, - fixedPrice, - duration, - marketplaceId, - ); + .sellNft(erc721Precompile.address, sellNFTSeries, buyer, paymentAsset, fixedPrice, duration, marketplaceId); const sellReceipt = await sellNftTx.wait(); const [, listingId, , ,] = (sellReceipt?.events as any)[0].args; @@ -400,13 +307,16 @@ describe("Marketplace Precompile", function () { const updatedPriceTx = await marketPlacePrecompile.connect(bobSigner).updateFixedPrice(listingId, updatedPrice); const updatedPriceTxReceipt = await updatedPriceTx.wait(); expect((updatedPriceTxReceipt?.events as any)[0].event).to.equal("FixedPriceSaleUpdate"); - const [collectionId, listingId1, newPrice, caller, seriesIds] = (updatedPriceTxReceipt?.events as any)[0].args; + const [collectionId, listingId1, newPrice, caller, seriesIds, marketplaceIdArgs] = ( + updatedPriceTxReceipt?.events as any + )[0].args; expect(collectionId.toNumber()).to.gte(0); expect(listingId1.toNumber()).to.gte(0); expect(newPrice.toNumber()).to.equal(updatedPrice); expect(caller).to.equal(bobSigner.address); const s = seriesIds.map((s: BigNumber) => s.toNumber()); expect(JSON.stringify(s)).to.equal(JSON.stringify(sellNFTSeries)); + expect(marketplaceIdArgs.toNumber()).to.gte(0); }); it("make simple offer and owner accepts it ", async () => { @@ -417,21 +327,25 @@ describe("Marketplace Precompile", function () { const offerNftTx = await marketPlacePrecompile .connect(alithSigner) - .makeSimpleOfferWithMarketplaceId(erc721Precompile.address, offerSeries, amount, paymentAsset, marketplaceId); + .makeSimpleOffer(erc721Precompile.address, offerSeries, amount, paymentAsset, marketplaceId); const receipt = await offerNftTx.wait(); - const [offerId] = (receipt?.events as any)[0].args; + const [offerId, , , , marketplaceIdArgs] = (receipt?.events as any)[0].args; expect((receipt?.events as any)[0].event).to.equal("Offer"); expect(offerId.toNumber()).to.gte(0); + expect(marketplaceIdArgs.toNumber()).to.gte(0); const acceptOfferTx = await marketPlacePrecompile.connect(bobSigner).acceptOffer(offerId); const acceptOfferReceipt = await acceptOfferTx.wait(); - const [offerId1, amount1, sender, collectionId, seriesId] = (acceptOfferReceipt?.events as any)[0].args; + const [offerId1, amount1, sender, collectionId, seriesId, marketplaceIdArgs1] = ( + acceptOfferReceipt?.events as any + )[0].args; expect((acceptOfferReceipt?.events as any)[0].event).to.equal("OfferAccept"); expect(offerId1.toNumber()).to.equal(offerId.toNumber()); expect(amount1.toNumber()).to.equal(amount); expect(sender).to.equal(bobSigner.address); expect(seriesId.toNumber()).to.equal(offerSeries); expect(collectionId.toNumber()).to.gte(0); + expect(marketplaceIdArgs1.toNumber()).to.gte(0); }); it("make simple offer and cancel it ", async () => { @@ -442,21 +356,23 @@ describe("Marketplace Precompile", function () { const offerNftTx = await marketPlacePrecompile .connect(alithSigner) - .makeSimpleOfferWithMarketplaceId(erc721Precompile.address, offerSeries, amount, paymentAsset, marketplaceId); + .makeSimpleOffer(erc721Precompile.address, offerSeries, amount, paymentAsset, marketplaceId); const receipt = await offerNftTx.wait(); - const [offerId] = (receipt?.events as any)[0].args; + const [offerId, , , , marketplaceIdArgs] = (receipt?.events as any)[0].args; expect((receipt?.events as any)[0].event).to.equal("Offer"); expect(offerId.toNumber()).to.gte(0); + expect(marketplaceIdArgs.toNumber()).to.gte(0); const cancelOfferTx = await marketPlacePrecompile.connect(alithSigner).cancelOffer(offerId); const cancelOfferReceipt = await cancelOfferTx.wait(); - const [offerId1, caller, collectionId, seriesId] = (cancelOfferReceipt?.events as any)[0].args; + const [offerId1, caller, collectionId, seriesId, marketplaceIdArgs1] = (cancelOfferReceipt?.events as any)[0].args; expect((cancelOfferReceipt?.events as any)[0].event).to.equal("OfferCancel"); expect(offerId1.toNumber()).to.equal(offerId.toNumber()); expect(collectionId.toNumber()).to.gte(0); expect(seriesId.toNumber()).to.equal(offerSeries); expect(caller).to.equal(alithSigner.address); + expect(marketplaceIdArgs1.toNumber()).to.gte(0); }); it("get offer from offer id", async () => { @@ -467,11 +383,12 @@ describe("Marketplace Precompile", function () { const offerNftTx = await marketPlacePrecompile .connect(alithSigner) - .makeSimpleOfferWithMarketplaceId(erc721Precompile.address, offerSeries, amount, paymentAsset, marketplaceId); + .makeSimpleOffer(erc721Precompile.address, offerSeries, amount, paymentAsset, marketplaceId); const receipt = await offerNftTx.wait(); - const [offerId] = (receipt?.events as any)[0].args; + const [offerId, , , , marketplaceIdArgs] = (receipt?.events as any)[0].args; expect((receipt?.events as any)[0].event).to.equal("Offer"); expect(offerId.toNumber()).to.gte(0); + expect(marketplaceIdArgs.toNumber()).to.gte(0); const offerDetails = await marketPlacePrecompile.getOfferFromId(offerId.toNumber()); const [collectionId, seriesId, amountFromCall, caller] = offerDetails; @@ -491,15 +408,7 @@ describe("Marketplace Precompile", function () { const sellNftTx = await marketPlacePrecompile .connect(bobSigner) - .sellNftWithMarketplaceId( - erc721Precompile.address, - sellNFTSeries, - buyer, - paymentAsset, - fixedPrice, - duration, - marketplaceId, - ); + .sellNft(erc721Precompile.address, sellNFTSeries, buyer, paymentAsset, fixedPrice, duration, marketplaceId); const sellReceipt = await sellNftTx.wait(); const [, listingId, , ,] = (sellReceipt?.events as any)[0].args; @@ -522,7 +431,7 @@ describe("Marketplace Precompile", function () { await marketPlacePrecompile .connect(bobSigner) - .sellNftWithMarketplaceId( + .sellNft( erc721Precompile.address, sellNFTSeries, alithSigner.address, @@ -555,15 +464,7 @@ describe("Marketplace Precompile", function () { const sellNftTx = await marketPlacePrecompile .connect(bobSigner) - .sellNftWithMarketplaceId( - erc721Precompile.address, - sellNFTSeries, - buyer, - paymentAsset, - fixedPrice, - duration, - marketplaceId, - ); + .sellNft(erc721Precompile.address, sellNFTSeries, buyer, paymentAsset, fixedPrice, duration, marketplaceId); const sellReceipt = await sellNftTx.wait(); const [, listingId, , ,] = (sellReceipt?.events as any)[0].args; @@ -586,15 +487,7 @@ describe("Marketplace Precompile", function () { const sellNftTx = await marketPlacePrecompile .connect(bobSigner) - .sellNftWithMarketplaceId( - erc721Precompile.address, - sellNFTSeries, - buyer, - paymentAsset, - fixedPrice, - duration, - marketplaceId, - ); + .sellNft(erc721Precompile.address, sellNFTSeries, buyer, paymentAsset, fixedPrice, duration, marketplaceId); const sellReceipt = await sellNftTx.wait(); const [, listingId, , ,] = (sellReceipt?.events as any)[0].args; @@ -616,14 +509,7 @@ describe("Marketplace Precompile", function () { const auctionNftTx = await marketPlacePrecompile .connect(bobSigner) - .auctionNftWithMarketplaceId( - erc721Precompile.address, - auctionNFTSeries, - paymentAsset, - reservePrice, - duration, - marketplaceId, - ); + .auctionNft(erc721Precompile.address, auctionNFTSeries, paymentAsset, reservePrice, duration, marketplaceId); const receipt = await auctionNftTx.wait(); const [, listingId, , ,] = (receipt?.events as any)[0].args; @@ -644,7 +530,7 @@ describe("Marketplace Precompile", function () { await marketPlacePrecompile .connect(alithSigner) - .makeSimpleOfferWithMarketplaceId(erc721Precompile.address, offerSeries, amount, paymentAsset, marketplaceId) + .makeSimpleOffer(erc721Precompile.address, offerSeries, amount, paymentAsset, marketplaceId) .catch((err: any) => { expect(err.message).contains("ZeroOffer"); }); @@ -658,7 +544,7 @@ describe("Marketplace Precompile", function () { await marketPlacePrecompile .connect(bobSigner) - .makeSimpleOfferWithMarketplaceId(erc721Precompile.address, offerSeries, amount, paymentAsset, marketplaceId) + .makeSimpleOffer(erc721Precompile.address, offerSeries, amount, paymentAsset, marketplaceId) .catch((err: any) => { expect(err.message).contains("IsTokenOwner"); }); diff --git a/e2e/test/MarketPlace/Marketplace.TxCosts.test.ts b/e2e/test/MarketPlace/Marketplace.TxCosts.test.ts index 249afe758..32b1f4f69 100644 --- a/e2e/test/MarketPlace/Marketplace.TxCosts.test.ts +++ b/e2e/test/MarketPlace/Marketplace.TxCosts.test.ts @@ -154,7 +154,7 @@ describe("Marketplace Precompile Gas Estimates", function () { const marketplaceId = 1; // precompile - const precompileGasCost = await marketPlacePrecompile.estimateGas.sellNftWithMarketplaceId( + const precompileGasCost = await marketPlacePrecompile.estimateGas.sellNft( erc721Precompile.address, sellNFTSeries, alithSigner.address, @@ -166,7 +166,7 @@ describe("Marketplace Precompile Gas Estimates", function () { let balanceBefore = await bobSigner.getBalance(); const tx = await marketPlacePrecompile .connect(bobSigner) - .sellNftWithMarketplaceId( + .sellNft( erc721Precompile.address, sellNFTSeries, alithSigner.address, @@ -216,7 +216,7 @@ describe("Marketplace Precompile Gas Estimates", function () { const marketplaceId = 1; // precompile - const precompileGasCost = await marketPlacePrecompile.estimateGas.auctionNftWithMarketplaceId( + const precompileGasCost = await marketPlacePrecompile.estimateGas.auctionNft( erc721Precompile.address, auctionNFTSeries, paymentAsset, @@ -227,14 +227,7 @@ describe("Marketplace Precompile Gas Estimates", function () { let balanceBefore = await bobSigner.getBalance(); const tx = await marketPlacePrecompile .connect(bobSigner) - .auctionNftWithMarketplaceId( - erc721Precompile.address, - auctionNFTSeries, - paymentAsset, - reservePrice, - duration, - marketplaceId, - ); + .auctionNft(erc721Precompile.address, auctionNFTSeries, paymentAsset, reservePrice, duration, marketplaceId); await tx.wait(); let balanceAfter = await bobSigner.getBalance(); const precompileFeeCost = balanceBefore.sub(balanceAfter); @@ -277,17 +270,11 @@ describe("Marketplace Precompile Gas Estimates", function () { // precompile const precompileGasCost = await marketPlacePrecompile .connect(alithSigner) - .estimateGas.makeSimpleOfferWithMarketplaceId( - erc721Precompile.address, - offerSeries, - amount, - paymentAsset, - marketplaceId, - ); + .estimateGas.makeSimpleOffer(erc721Precompile.address, offerSeries, amount, paymentAsset, marketplaceId); let balanceBefore = await alithSigner.getBalance(); const tx = await marketPlacePrecompile .connect(alithSigner) - .makeSimpleOfferWithMarketplaceId(erc721Precompile.address, offerSeries, amount, paymentAsset, marketplaceId); + .makeSimpleOffer(erc721Precompile.address, offerSeries, amount, paymentAsset, marketplaceId); await tx.wait(); let balanceAfter = await alithSigner.getBalance(); const precompileFeeCost = balanceBefore.sub(balanceAfter); @@ -330,7 +317,7 @@ describe("Marketplace Precompile Gas Estimates", function () { let sellNftTx = await marketPlacePrecompile .connect(bobSigner) - .sellNftWithMarketplaceId( + .sellNft( erc721Precompile.address, sellNFTSeries, alithSigner.address, @@ -355,7 +342,7 @@ describe("Marketplace Precompile Gas Estimates", function () { sellNftTx = await marketPlacePrecompile .connect(bobSigner) - .sellNftWithMarketplaceId( + .sellNft( erc721Precompile.address, sellNFTSeries2, alithSigner.address, @@ -404,14 +391,7 @@ describe("Marketplace Precompile Gas Estimates", function () { // precompile let auctionTx = await marketPlacePrecompile .connect(bobSigner) - .auctionNftWithMarketplaceId( - erc721Precompile.address, - auctionNFTSeries, - paymentAsset, - reservePrice, - duration, - marketplaceId, - ); + .auctionNft(erc721Precompile.address, auctionNFTSeries, paymentAsset, reservePrice, duration, marketplaceId); let auctionReceipt = await auctionTx.wait(); let [, listingIdForAuction, , ,] = (auctionReceipt?.events as any)[0].args; @@ -430,14 +410,7 @@ describe("Marketplace Precompile Gas Estimates", function () { // precompile auctionTx = await marketPlacePrecompile .connect(bobSigner) - .auctionNftWithMarketplaceId( - erc721Precompile.address, - auctionNFTSeries, - paymentAsset, - reservePrice, - duration, - marketplaceId, - ); + .auctionNft(erc721Precompile.address, auctionNFTSeries, paymentAsset, reservePrice, duration, marketplaceId); auctionReceipt = await auctionTx.wait(); [, listingIdForAuction, , ,] = (auctionReceipt?.events as any)[0].args; listingId = listingIdForAuction.toNumber(); @@ -477,15 +450,7 @@ describe("Marketplace Precompile Gas Estimates", function () { let sellNftTx = await marketPlacePrecompile .connect(bobSigner) - .sellNftWithMarketplaceId( - erc721Precompile.address, - sellNFTSeries, - buyer, - paymentAsset, - fixedPrice, - duration, - marketplaceId, - ); + .sellNft(erc721Precompile.address, sellNFTSeries, buyer, paymentAsset, fixedPrice, duration, marketplaceId); let sellReceipt = await sellNftTx.wait(); let [, listingIdForSale, , ,] = (sellReceipt?.events as any)[0].args; @@ -502,15 +467,7 @@ describe("Marketplace Precompile Gas Estimates", function () { sellNFTSeries = [182, 183]; sellNftTx = await marketPlacePrecompile .connect(bobSigner) - .sellNftWithMarketplaceId( - erc721Precompile.address, - sellNFTSeries, - buyer, - paymentAsset, - fixedPrice, - duration, - marketplaceId, - ); + .sellNft(erc721Precompile.address, sellNFTSeries, buyer, paymentAsset, fixedPrice, duration, marketplaceId); sellReceipt = await sellNftTx.wait(); [, listingIdForSale, , ,] = (sellReceipt?.events as any)[0].args; @@ -550,15 +507,7 @@ describe("Marketplace Precompile Gas Estimates", function () { const sellNftTx = await marketPlacePrecompile .connect(bobSigner) - .sellNftWithMarketplaceId( - erc721Precompile.address, - sellNFTSeries, - buyer, - paymentAsset, - fixedPrice, - duration, - marketplaceId, - ); + .sellNft(erc721Precompile.address, sellNFTSeries, buyer, paymentAsset, fixedPrice, duration, marketplaceId); const sellReceipt = await sellNftTx.wait(); const [, listingId, , ,] = (sellReceipt?.events as any)[0].args; @@ -607,7 +556,7 @@ describe("Marketplace Precompile Gas Estimates", function () { let offerNftTx = await marketPlacePrecompile .connect(alithSigner) - .makeSimpleOfferWithMarketplaceId(erc721Precompile.address, offerSeries, amount, paymentAsset, marketplaceId); + .makeSimpleOffer(erc721Precompile.address, offerSeries, amount, paymentAsset, marketplaceId); let receipt = await offerNftTx.wait(); let [offerId] = (receipt?.events as any)[0].args; @@ -622,7 +571,7 @@ describe("Marketplace Precompile Gas Estimates", function () { offerNftTx = await marketPlacePrecompile .connect(alithSigner) - .makeSimpleOfferWithMarketplaceId(erc721Precompile.address, 161, amount, paymentAsset, marketplaceId); + .makeSimpleOffer(erc721Precompile.address, 161, amount, paymentAsset, marketplaceId); receipt = await offerNftTx.wait(); [offerId] = (receipt?.events as any)[0].args; @@ -659,7 +608,7 @@ describe("Marketplace Precompile Gas Estimates", function () { let offerNftTx = await marketPlacePrecompile .connect(alithSigner) - .makeSimpleOfferWithMarketplaceId(erc721Precompile.address, offerSeries, amount, paymentAsset, marketplaceId); + .makeSimpleOffer(erc721Precompile.address, offerSeries, amount, paymentAsset, marketplaceId); let receipt = await offerNftTx.wait(); let [offerId] = (receipt?.events as any)[0].args; @@ -674,7 +623,7 @@ describe("Marketplace Precompile Gas Estimates", function () { offerNftTx = await marketPlacePrecompile .connect(alithSigner) - .makeSimpleOfferWithMarketplaceId(erc721Precompile.address, 399, amount, paymentAsset, marketplaceId); + .makeSimpleOffer(erc721Precompile.address, 399, amount, paymentAsset, marketplaceId); receipt = await offerNftTx.wait(); [offerId] = (receipt?.events as any)[0].args; diff --git a/e2e/test/MarketPlace/MarketplaceSFT.TxCosts.test.ts b/e2e/test/MarketPlace/MarketplaceSFT.TxCosts.test.ts new file mode 100644 index 000000000..d5666b1ba --- /dev/null +++ b/e2e/test/MarketPlace/MarketplaceSFT.TxCosts.test.ts @@ -0,0 +1,335 @@ +import { JsonRpcProvider } from "@ethersproject/providers"; +import { ApiPromise, Keyring, WsProvider } from "@polkadot/api"; +import { KeyringPair } from "@polkadot/keyring/types"; +import { hexToU8a } from "@polkadot/util"; +import { expect } from "chai"; +import { BigNumber, Contract, Wallet } from "ethers"; +import { ethers } from "hardhat"; +import Web3 from "web3"; +import web3 from "web3"; + +import { + ALITH_PRIVATE_KEY, + BOB_PRIVATE_KEY, + ERC1155_PRECOMPILE_ABI, + MARKETPLACE_PRECOMPILE_ADDRESS, + MARKET_PLACE_ABI, + NodeProcess, + TxCosts, + getScaledGasForExtrinsicFee, + saveTxFees, + saveTxGas, + startNode, + typedefs, +} from "../../common"; + +// SFT Collection information +const name = "test-sft-collection"; +const metadataPath = "https://example.com/sft/metadata/"; + +describe("Marketplace SFT Precompile Gas Estimates", function () { + let node: NodeProcess; + + let api: ApiPromise; + let bobSigner: Wallet; + let alithSigner: Wallet; + let erc1155Precompile: Contract; + let marketPlacePrecompile: Contract; + let erc1155PrecompileAddress: string; + let collectionId: string; + let bobKeyring: KeyringPair; + let provider: JsonRpcProvider; + + const allTxGasCosts: { [key: string]: TxCosts } = {}; + const allTxFeeCosts: { [key: string]: TxCosts } = {}; + + // Setup api instance + before(async () => { + node = await startNode(); + + const wsProvider = new WsProvider(`ws://localhost:${node.wsPort}`); + // Setup Root api instance and keyring + api = await ApiPromise.create({ provider: wsProvider, types: typedefs }); + const keyring = new Keyring({ type: "ethereum" }); + bobKeyring = keyring.addFromSeed(hexToU8a(BOB_PRIVATE_KEY)); + + provider = new JsonRpcProvider(`http://127.0.0.1:${node.httpPort}`); + alithSigner = new Wallet(ALITH_PRIVATE_KEY).connect(provider); // 'development' seed + bobSigner = new Wallet(BOB_PRIVATE_KEY).connect(provider); + + // Create SFT collection using runtime, bob is collection owner + await new Promise((resolve, reject) => { + api.tx.sft + .createCollection(name, null, metadataPath, null) + .signAndSend(bobKeyring, async ({ status, events }) => { + if (status.isInBlock) { + events.forEach(({ event: { data, method } }) => { + if (method == "CollectionCreate") { + const collection_uuid = (data.toJSON() as any)[0]; + collectionId = collection_uuid; + + console.log(`Collection UUID: ${collection_uuid}`); + const collection_id_hex = (+collection_uuid).toString(16).padStart(8, "0"); + erc1155PrecompileAddress = Web3.utils.toChecksumAddress( + `0xBBBBBBBB${collection_id_hex}000000000000000000000000`, + ); + console.log(`SFT Collection Address: ${erc1155PrecompileAddress}`); + erc1155Precompile = new Contract(erc1155PrecompileAddress, ERC1155_PRECOMPILE_ABI, bobSigner); + + resolve(); + } + }); + } + }) + .catch((err) => reject(err)); + }); + + let quantity = 499; + // create token 1 + const tokenName1 = ethers.utils.hexlify(ethers.utils.toUtf8Bytes("MyToken1")); + const tx1 = await erc1155Precompile.connect(bobSigner).createToken(tokenName1, quantity, 0, bobSigner.address); + await tx1.wait(); + + // create token 2 + quantity = 899; + const tokenName2 = ethers.utils.hexlify(ethers.utils.toUtf8Bytes("MyToken2")); + const tx2 = await erc1155Precompile.connect(bobSigner).createToken(tokenName2, quantity, 0, bobSigner.address); + await tx2.wait(); + + // create token 3 + quantity = 1005; + const tokenName = ethers.utils.hexlify(ethers.utils.toUtf8Bytes("MyToken3")); + const tx3 = await erc1155Precompile.connect(bobSigner).createToken(tokenName, quantity, 0, bobSigner.address); + await tx3.wait(); + + // Deploy marketplace contract + marketPlacePrecompile = new Contract(MARKETPLACE_PRECOMPILE_ADDRESS, MARKET_PLACE_ABI, bobSigner); + // Register 0th marketplace id which would be used for other tests + const entitlements = 1000; + const marketplaceRegisterTx = await marketPlacePrecompile + .connect(bobSigner) + .registerMarketplace(bobSigner.address, entitlements); + await marketplaceRegisterTx.wait(); + }); + + after(async () => { + await node.stop(); + saveTxGas(allTxGasCosts, "MarketPlace/SFTTxCosts.md", "Marketplace Precompiles SFT"); + saveTxFees(allTxFeeCosts, "MarketPlace/SFTTxCosts.md", "Marketplace Precompiles SFT"); + }); + + it("register marketplace tx costs", async () => { + // precompile + const entitlements = 1000; + const precompileGasCost = await marketPlacePrecompile.estimateGas.registerMarketplace( + bobSigner.address, + entitlements, + ); + let balanceBefore = await bobSigner.getBalance(); + const tx = await marketPlacePrecompile.connect(bobSigner).registerMarketplace(bobSigner.address, entitlements); + await tx.wait(); + let balanceAfter = await bobSigner.getBalance(); + const precompileFeeCost = balanceBefore.sub(balanceAfter); + + // extrinsic + const owner2 = Wallet.createRandom().connect(provider); + balanceBefore = await bobSigner.getBalance(); + await new Promise((resolve) => { + api.tx.marketplace.registerMarketplace(owner2.address, entitlements).signAndSend(bobKeyring, ({ status }) => { + if (status.isInBlock) resolve(); + }); + }); + balanceAfter = await bobSigner.getBalance(); + const extrinsicFeeCost = balanceBefore.sub(balanceAfter); + const extrinsicGasCost = await getScaledGasForExtrinsicFee(provider, extrinsicFeeCost); + expect(extrinsicFeeCost).to.be.lessThan(precompileFeeCost); + + // Update all costs + allTxGasCosts["registerMarketplace"] = { + Contract: BigNumber.from(0), // no contract + Precompile: precompileGasCost, // convert to XRP Drops(6) + Extrinsic: extrinsicGasCost, // convert to XRP Drops(6) + }; + allTxFeeCosts["registerMarketplace"] = { + Contract: BigNumber.from(0), // no contract + Precompile: precompileFeeCost.div(1000000000000n), // convert to XRP Drops(6) + Extrinsic: extrinsicFeeCost.div(1000000000000n), // convert to XRP Drops(6) + }; + }); + + it("sell sft with marketplaceId", async () => { + const sellSFTSeries = [0, 1, 2]; + const quantities = [10, 15, 12]; + let paymentAsset: string | number = web3.utils.toChecksumAddress("0xCCCCCCCC00000002000000000000000000000000"); //xrp token address + const fixedPrice = 1000000; + const duration = 1000; //blocks + const marketplaceId = 1; + + // precompile + const precompileGasCost = await marketPlacePrecompile.estimateGas.sellSft( + erc1155Precompile.address, + sellSFTSeries, + quantities, + alithSigner.address, + paymentAsset, + fixedPrice, + duration, + marketplaceId, + ); + let balanceBefore = await bobSigner.getBalance(); + const tx = await marketPlacePrecompile + .connect(bobSigner) + .sellSft( + erc1155Precompile.address, + sellSFTSeries, + quantities, + alithSigner.address, + paymentAsset, + fixedPrice, + duration, + marketplaceId, + ); + const receipt = await tx.wait(); + let balanceAfter = await bobSigner.getBalance(); + const precompileFeeCost = balanceBefore.sub(balanceAfter); + const [seller, listingId, fixedPriceFromCall, serialNumbers, collectionAddress, marketplaceIdArgs, _quantities] = ( + receipt?.events as any + )[0].args; + expect((receipt?.events as any)[0].event).to.equal("FixedPriceSaleListSFT"); + expect(collectionAddress).to.equal(erc1155Precompile.address); + expect(listingId.toNumber()).to.gte(0); + expect(fixedPriceFromCall.toNumber()).to.equal(fixedPrice); + expect(seller).to.equal(bobSigner.address); + const s = serialNumbers.map((s: BigNumber) => s.toNumber()); + expect(JSON.stringify(s)).to.equal(JSON.stringify(sellSFTSeries)); + const q = _quantities.map((s: BigNumber) => s.toNumber()); + expect(JSON.stringify(q)).to.equal(JSON.stringify(quantities)); + expect(marketplaceIdArgs.toNumber()).to.gte(0); + + // extrinsic + paymentAsset = 2; + const tokens = api.createType( + "PalletMarketplaceListingTokens", + { + collectionId: collectionId, + serialNumbers: [ + [0, 10], + [1, 21], + [2, 5], + ], + }, + 1, + ); + balanceBefore = await bobSigner.getBalance(); + await new Promise((resolve) => { + api.tx.marketplace + .sell(tokens, alithSigner.address, paymentAsset, fixedPrice, duration, marketplaceId) + .signAndSend(bobKeyring, ({ status }) => { + if (status.isInBlock) resolve(); + }); + }); + balanceAfter = await bobSigner.getBalance(); + const extrinsicFeeCost = balanceBefore.sub(balanceAfter); + const extrinsicGasCost = await getScaledGasForExtrinsicFee(provider, extrinsicFeeCost); + expect(extrinsicFeeCost).to.be.lessThan(precompileFeeCost); + + // Update all costs + allTxGasCosts["sellSft"] = { + Contract: BigNumber.from(0), // no contract + Precompile: precompileGasCost, // convert to XRP Drops(6) + Extrinsic: extrinsicGasCost, // convert to XRP Drops(6) + }; + allTxFeeCosts["sellSft"] = { + Contract: BigNumber.from(0), // no contract + Precompile: precompileFeeCost.div(1000000000000n), // convert to XRP Drops(6) + Extrinsic: extrinsicFeeCost.div(1000000000000n), // convert to XRP Drops(6) + }; + }); + + it("auction sft with marketplaceId", async () => { + const auctionSFTSeries = [0, 1, 2]; + let paymentAsset: string | number = web3.utils.toChecksumAddress("0xCCCCCCCC00000002000000000000000000000000"); //xrp token address + const reservePrice = 1000000; + const duration = 10000; //blocks + const marketplaceId = 1; + const quantities = [5, 10, 15]; + + // precompile + const precompileGasCost = await marketPlacePrecompile.estimateGas.auctionSft( + erc1155Precompile.address, + auctionSFTSeries, + quantities, + paymentAsset, + reservePrice, + duration, + marketplaceId, + ); + let balanceBefore = await bobSigner.getBalance(); + const tx = await marketPlacePrecompile + .connect(bobSigner) + .auctionSft( + erc1155Precompile.address, + auctionSFTSeries, + quantities, + paymentAsset, + reservePrice, + duration, + marketplaceId, + ); + const receipt = await tx.wait(); + const [collectionIdArgs, listingId, reservePriceFromChain, seller, serialNumbers, marketplaceIdArgs] = ( + receipt?.events as any + )[0].args; + expect((receipt?.events as any)[0].event).to.equal("AuctionOpenSFT"); + expect(collectionIdArgs.toNumber()).to.gte(0); + expect(listingId.toNumber()).to.gte(0); + expect(reservePriceFromChain.toNumber()).to.equal(reservePrice); + expect(seller).to.equal(bobSigner.address); + const s = serialNumbers.map((s: BigNumber) => s.toNumber()); + expect(JSON.stringify(s)).to.equal(JSON.stringify(auctionSFTSeries)); + expect(marketplaceIdArgs.toNumber()).to.gte(0); + + let balanceAfter = await bobSigner.getBalance(); + const precompileFeeCost = balanceBefore.sub(balanceAfter); + + // extrinsic + paymentAsset = 2; + balanceBefore = await bobSigner.getBalance(); + const tokens = api.createType( + "PalletMarketplaceListingTokens", + { + collectionId: collectionId, + serialNumbers: [ + [0, 10], + [1, 21], + [2, 5], + ], + }, + 1, + ); + + await new Promise((resolve) => { + api.tx.marketplace + .auction(tokens, paymentAsset, reservePrice, duration, marketplaceId) + .signAndSend(bobKeyring, ({ status }) => { + if (status.isInBlock) resolve(); + }); + }); + balanceAfter = await bobSigner.getBalance(); + const extrinsicFeeCost = balanceBefore.sub(balanceAfter); + const extrinsicGasCost = await getScaledGasForExtrinsicFee(provider, extrinsicFeeCost); + expect(extrinsicFeeCost).to.be.lessThan(precompileFeeCost); + + // Update all costs + allTxGasCosts["auctionSft"] = { + Contract: BigNumber.from(0), // no contract + Precompile: precompileGasCost, // convert to XRP Drops(6) + Extrinsic: extrinsicGasCost, // convert to XRP Drops(6) + }; + allTxFeeCosts["auctionSft"] = { + Contract: BigNumber.from(0), // no contract + Precompile: precompileFeeCost.div(1000000000000n), // convert to XRP Drops(6) + Extrinsic: extrinsicFeeCost.div(1000000000000n), // convert to XRP Drops(6) + }; + }); +}); diff --git a/e2e/test/MarketPlace/SFTTxCosts.md b/e2e/test/MarketPlace/SFTTxCosts.md new file mode 100644 index 000000000..6a3eb4b93 --- /dev/null +++ b/e2e/test/MarketPlace/SFTTxCosts.md @@ -0,0 +1,16 @@ +## Generated tx costs(Gas) for Marketplace Precompiles SFT + +| Function Call | Contract gas | Precompile gas | (Extrinsic fee/Gas price) | +|:--------------------|:------------:|:--------------:|:-------------------------:| +| registerMarketplace | 0 | 32412 | 19836 | +| sellSft | 0 | 59048 | 35176 | +| auctionSft | 0 | 59298 | 31676 | + + +## Generated tx costs(fees) for Marketplace Precompiles SFT + +| Function Call | Contract cost (Drops) | Precompile cost (Drops) | Extrinsic cost (Drops) | +|:--------------------|:---------------------:|:-----------------------:|:----------------------:| +| registerMarketplace | 0 | 470282 | 297545 | +| sellSft | 0 | 878142 | 527648 | +| auctionSft | 0 | 883213 | 475148 | diff --git a/e2e/test/MarketPlace/TxCosts.md b/e2e/test/MarketPlace/TxCosts.md index 6b2ea5ff0..c935a75e6 100644 --- a/e2e/test/MarketPlace/TxCosts.md +++ b/e2e/test/MarketPlace/TxCosts.md @@ -2,29 +2,29 @@ | Function Call | Contract gas | Precompile gas | (Extrinsic fee/Gas price) | |:--------------------|:------------:|:--------------:|:-------------------------:| -| registerMarketplace | 0 | 28523 | 7212 | -| sellNft | 0 | 45619 | 9218 | -| auctionNft | 0 | 45698 | 8238 | -| makeSimpleOffer | 0 | 49994 | 20965 | -| buy | 0 | 42799 | 140172 | -| bid | 0 | 50373 | 1340918 | -| cancelSale | 0 | 33131 | 6792 | -| updateFixedPrice | 0 | 27601 | 7538 | -| acceptOffer | 0 | 55842 | 6405 | -| cancelOffer | 0 | 45669 | 6452 | +| registerMarketplace | 0 | 32412 | 19836 | +| sellNft | 0 | 58972 | 27010 | +| auctionNft | 0 | 59257 | 23510 | +| makeSimpleOffer | 0 | 59994 | 27844 | +| buy | 0 | 60238 | 85011 | +| bid | 0 | 70000 | 687679 | +| cancelSale | 0 | 38482 | 18338 | +| updateFixedPrice | 0 | 31563 | 21002 | +| acceptOffer | 0 | 74045 | 17006 | +| cancelOffer | 0 | 55776 | 17002 | ## Generated tx costs(fees) for Marketplace Precompiles | Function Call | Contract cost (Drops) | Precompile cost (Drops) | Extrinsic cost (Drops) | |:--------------------|:---------------------:|:-----------------------:|:----------------------:| -| registerMarketplace | 0 | 213050 | 54091 | -| sellNft | 0 | 333944 | 69141 | -| auctionNft | 0 | 334836 | 61791 | -| makeSimpleOffer | 0 | 461842 | 157241 | -| buy | 0 | 1301702 | 1051291 | -| bid | 0 | 10365893 | 10056891 | -| cancelSale | 0 | 246664 | 50941 | -| updateFixedPrice | 0 | 203425 | 56541 | -| acceptOffer | 0 | 412144 | 48041 | -| cancelOffer | 0 | 334406 | 48391 | +| registerMarketplace | 0 | 470282 | 297545 | +| sellNft | 0 | 876597 | 405150 | +| auctionNft | 0 | 882388 | 352650 | +| makeSimpleOffer | 0 | 997359 | 417662 | +| buy | 0 | 1902310 | 1275165 | +| bid | 0 | 10976222 | 10315185 | +| cancelSale | 0 | 576207 | 275073 | +| updateFixedPrice | 0 | 443084 | 315034 | +| acceptOffer | 0 | 1048039 | 255102 | +| cancelOffer | 0 | 822957 | 255041 | diff --git a/evm-precompiles/marketplace/README.md b/evm-precompiles/marketplace/README.md index 292e0b0ee..4d5a04cf1 100644 --- a/evm-precompiles/marketplace/README.md +++ b/evm-precompiles/marketplace/README.md @@ -7,34 +7,41 @@ Precompile address: `0x00000000000000000000000000000000000006CD` ```solidity interface Marketplace { event MarketplaceRegister(address indexed sender, uint256 indexed marketplaceId, address marketplace_account); - event FixedPriceSaleList(address indexed seller, uint256 indexed listingId, uint256 indexed fixedPrice, uint256[] serialNumbers, address collectionAddress); - event FixedPriceSaleUpdate(uint256 indexed collectionId, uint256 indexed listingId, uint256 indexed newPrice, address sender, uint256[] serialNumbers); - event FixedPriceSaleComplete(uint256 indexed collectionId, uint256 indexed listingId, uint256 indexed fixedPrice, address sender, uint256[] serialNumbers); - event AuctionOpen(uint256 indexed collectionId, uint256 indexed listingId, uint256 indexed reservePrice, address sender, uint256[] serialNumbers), - event Bid(address indexed bidder, uint256 indexed listingId, uint256 indexed amount); - event FixedPriceSaleClose(uint256 indexed collectionId, uint256 indexed listingId, address sender, uint256[] serialNumbers); //uint256,uint256,address,uint256[] - event AuctionClose(uint256 indexed collectionId, uint256 indexed listingId, address sender, uint256[] serialNumbers); - event Offer(uint256 indexed offerId, address indexed sender, uint256 collectionId, uint256 seriesId); - event OfferCancel(uint256 indexed offerId, address indexed sender, uint256 collectionId, uint256 seriesId); - event OfferAccept(uint256 indexed offerId, uint256 indexed amount, address indexed sender, uint256 collectionId, uint256 seriesId); // uint256,uint256,address,uint256 - + event FixedPriceSaleListNFT(address indexed seller, uint256 indexed listingId, uint256 indexed fixedPrice, uint256[] serialNumbers, address collectionAddress, uint128 marketplace_id); + event FixedPriceSaleListSFT(address indexed seller, uint256 indexed listingId, uint256 indexed fixedPrice, uint256[] serialNumbers, address collectionAddress, uint128 marketplace_id, uint256[] serialNumbers); + event FixedPriceSaleUpdate(uint256 indexed collectionId, uint256 indexed listingId, uint256 indexed newPrice, address sender, uint256[] serialNumbers, uint128 marketplace_id); + event FixedPriceSaleComplete(uint256 indexed collectionId, uint256 indexed listingId, uint256 indexed fixedPrice, address sender, uint256[] serialNumbers, uint128 marketplace_id); + event AuctionOpenNFT(uint256 indexed collectionId, uint256 indexed listingId, uint256 indexed reservePrice, address sender, uint256[] serialNumbers, uint128 marketplace_id); + event AuctionOpenSFT(uint256 indexed collectionId, uint256 indexed listingId, uint256 indexed reservePrice, address sender, uint256[] serialNumbers, uint128 marketplace_id); + event Bid(address indexed bidder, uint256 indexed listingId, uint256 indexed amount, uint128 marketplace_id); + event FixedPriceSaleClose(uint256 indexed collectionId, uint256 indexed listingId, address sender, uint256[] serialNumbers, uint128 marketplace_id); //uint256;uint256;address;uint256[] + event AuctionClose(uint256 indexed collectionId, uint256 indexed listingId, address sender, uint256[] serialNumbers, uint128 marketplace_id); + event Offer(uint256 indexed offerId, address indexed sender, uint256 collectionId, uint256 seriesId, uint128 marketplace_id); + event OfferCancel(uint256 indexed offerId, address indexed sender, uint256 collectionId, uint256 seriesId, uint128 marketplace_id); + event OfferAccept(uint256 indexed offerId, uint256 indexed amount, address indexed sender, uint256 collectionId, uint256 seriesId, uint128 marketplace_id); // uint256;uint256;address;uint256 + function registerMarketplace(address marketplaceAccount, uint256 entitlement) external returns (uint marketplaceId); + // sellNftWithMarketplaceId is deprecated for sellNFT function sellNftWithMarketplaceId(address collectionAddress, uint256[] calldata serialNumberIds, address buyer, address paymentAsset, uint256 fixedPrice, uint256 duration, uint32 marketplaceId) external returns (uint listingId); - function sellNftWithoutMarketplace(address collectionAddress, uint256[] calldata serialNumberIds, address buyer, address paymentAsset, uint256 fixedPrice, uint256 duration) external returns (uint listingId); + function sellNft(address collectionAddress, uint256[] calldata serialNumberIds, address buyer, address paymentAsset, uint256 fixedPrice, uint256 duration, uint128 marketplace_id) external returns (uint listingId); + function sellSft(address collectionAddress, uint256[] calldata serialNumberIds, uint256[] calldata quantities, address buyer, address paymentAsset, uint256 fixedPrice, uint256 duration, uint128 marketplace_id) external returns (uint listingId); function updateFixedPrice(uint128 listingId, uint256 newPrice) external; - function buy(uint128 listingId) external payable; - function auctionNftWithMarketplaceId(address collectionAddress, uint256[] calldata serialNumberIds, address paymentAsset, uint256 reservePrice, uint256 duration, uint256 marketplaceId) external payable; - function auctionNftWithoutMarketplace(address collectionAddress, uint256[] calldata serialNumberIds, address paymentAsset, uint256 reservePrice, uint256 duration) external payable; + function buy(uint128 listingId) external payable"; + // auctionNftWithMarketplaceId is deprecated for auctionNft + function auctionNftWithMarketplaceId(address collectionAddress, uint256[] calldata serialNumberIds, address paymentAsset, uint256 reservePrice, uint256 duration, uint256 marketplaceId); + function auctionNft(address collectionAddress, uint256[] calldata serialNumberIds, address paymentAsset, uint256 reservePrice, uint256 duration, uint256 marketplaceId); + function auctionSft(address collectionAddress, uint256[] calldata serialNumberIds, uint256[] calldata quantities, address paymentAsset, uint256 reservePrice, uint256 duration, uint256 marketplaceId); function bid(uint128 listingId, uint256 amount) external; function cancelSale(uint128 listingId) external; + // makeSimpleOfferWithMarketplaceId is deprecated for makeSimpleOffer function makeSimpleOfferWithMarketplaceId(address collectionAddress, uint32 serialNumber, uint256 amount, address assetId, uint32 marketplaceId) external returns (uint offerId); - function makeSimpleOfferWithoutMarketplace(address collectionAddress, uint32 serialNumber, uint256 amount, address assetId) external returns (uint offerId); + function makeSimpleOffer(address collectionAddress, uint32 serialNumber, uint256 amount, address assetId, uint32 marketplaceId) external returns (uint offerId); function cancelOffer(uint64 offerId) external; function acceptOffer(uint64 offerId) external; - // read - function getMarketplaceAccount(uint32 marketplaceId) external view returns(address marketplaceAccount); - function getListingFromId(uint128 listingId) external view returns (uint32 collectionId, uint32[] calldata serialNumbers, uint128 price, uint32 paymentAsset); - function getOfferFromId(uint64 offerId) external view returns (uint32 collectionId, uint32 serialNumber, uint128 amount, address buyer); + // read + function getMarketplaceAccount(uint32 marketplaceId) external view returns(address marketplaceAccount); + function getListingFromId(uint128 listingId) external view returns (bytes type, uint32 collectionId, uint32[] calldata serial_numbers, uint128 price, uint32 paymentAsset); + function getOfferFromId(uint64 offerId) external view returns (uint32 collectionId, uint32 serial_number, uint128 amount, address buyer); } ``` diff --git a/evm-precompiles/marketplace/src/lib.rs b/evm-precompiles/marketplace/src/lib.rs index 179ce4474..927c1e41f 100644 --- a/evm-precompiles/marketplace/src/lib.rs +++ b/evm-precompiles/marketplace/src/lib.rs @@ -23,48 +23,70 @@ use frame_support::{ }; use pallet_evm::{GasWeightMapping, Precompile}; use pallet_marketplace::{ - types::{Listing, ListingTokens, MarketplaceId, NftListing, OfferId}, + types::{Listing, ListingTokens, MarketplaceId, NftListing, OfferId, SftListing}, weights::WeightInfo, }; use precompile_utils::{ - constants::{ERC20_PRECOMPILE_ADDRESS_PREFIX, ERC721_PRECOMPILE_ADDRESS_PREFIX}, + constants::{ + ERC1155_PRECOMPILE_ADDRESS_PREFIX, ERC20_PRECOMPILE_ADDRESS_PREFIX, + ERC721_PRECOMPILE_ADDRESS_PREFIX, + }, prelude::*, }; use seed_primitives::{AssetId, Balance, BlockNumber, CollectionUuid, SerialNumber, TokenId}; use sp_core::{H160, H256, U256}; use sp_runtime::{traits::SaturatedConversion, BoundedVec, Permill}; -use sp_std::{marker::PhantomData, vec, vec::Vec}; +use sp_std::{marker::PhantomData, vec::Vec}; /// Solidity selector of the Marketplace register log, which is the Keccak of the Log signature. pub const SELECTOR_LOG_MARKETPLACE_REGISTER: [u8; 32] = keccak256!("MarketplaceRegister(address,uint256,address)"); // caller_id, marketplace_id -pub const SELECTOR_LOG_FIXED_PRICE_SALE_LIST: [u8; 32] = - keccak256!("FixedPriceSaleList(address,uint256,uint256,uint256[],address)"); // seller_id, listing_id, fixed_price, serial_number_ids, collection_address +pub const SELECTOR_LOG_FIXED_PRICE_SALE_LIST_NFT: [u8; 32] = + keccak256!("FixedPriceSaleListNFT(address,uint256,uint256,uint256[],address,uint128)"); +// seller_id, listing_id, fixed_price, serial_number_ids, collection_address, marketplace_id + +pub const SELECTOR_LOG_FIXED_PRICE_SALE_LIST_SFT: [u8; 32] = keccak256!( + "FixedPriceSaleListSFT(address,uint256,uint256,uint256[],address,uint128,uint256[])" +); +// seller_id, listing_id, fixed_price, serial_number_ids, collection_address, marketplace_id pub const SELECTOR_LOG_FIXED_PRICE_SALE_UPDATE: [u8; 32] = - keccak256!("FixedPriceSaleUpdate(uint256,uint256,uint256,address,uint256[])"); // collection_id, listing_id, new_price, sender, serial_number_ids + keccak256!("FixedPriceSaleUpdate(uint256,uint256,uint256,address,uint256[],uint128)"); +// collection_id, listing_id, new_price, sender, serial_number_ids, marketplace_id pub const SELECTOR_LOG_FIXED_PRICE_SALE_COMPLETE: [u8; 32] = - keccak256!("FixedPriceSaleComplete(uint256,uint256,uint256,address,uint256[])"); // collection_id, listing_id, fixed_price, sender, serial_number_ids + keccak256!("FixedPriceSaleComplete(uint256,uint256,uint256,address,uint256[],uint128)"); +// collection_id, listing_id, fixed_price, sender, serial_number_ids, marketplace_id + +pub const SELECTOR_LOG_AUCTION_OPEN_NFT: [u8; 32] = + keccak256!("AuctionOpenNFT(uint256,uint256,uint256,address,uint256[],uint128)"); +// collection_id, listing_id, reserve_price, sender, serial_number_ids, marketplace_id + +pub const SELECTOR_LOG_AUCTION_OPEN_SFT: [u8; 32] = + keccak256!("AuctionOpenSFT(uint256,uint256,uint256,address,uint256[],uint128)"); +// collection_id, listing_id, reserve_price, sender, serial_number_ids, marketplace_id -pub const SELECTOR_LOG_AUCTION_OPEN: [u8; 32] = - keccak256!("AuctionOpen(uint256,uint256,uint256,address,uint256[])"); // collection_id, listing_id, reserve_price, sender, serial_number_ids +pub const SELECTOR_LOG_BID: [u8; 32] = keccak256!("Bid(address,uint256,uint256,uint128)"); +// bidder, listing_id, amount, marketplace_id -pub const SELECTOR_LOG_BID: [u8; 32] = keccak256!("Bid(address,uint256,uint256)"); // bidder, listing_id, amount pub const SELECTOR_LOG_FIXED_PRICE_SALE_CLOSE: [u8; 32] = - keccak256!("FixedPriceSaleClose(uint256,uint256,address,uint256[])"); // collectionId, listing_id, caller, series_ids + keccak256!("FixedPriceSaleClose(uint256,uint256,address,uint256[],uint128)"); +// collectionId, listing_id, caller, series_ids, marketplace_id pub const SELECTOR_LOG_AUCTION_CLOSE: [u8; 32] = - keccak256!("AuctionClose(uint256,uint256,address,uint256[])"); // collectionId, listing_id, caller, series_ids + keccak256!("AuctionClose(uint256,uint256,address,uint256[],uint128)"); +// collectionId, listing_id, caller, series_ids, marketplace_id -pub const SELECTOR_LOG_OFFER: [u8; 32] = keccak256!("Offer(uint256,address,uint256,uint256)"); // offer_id, caller, collection_id, series_id +pub const SELECTOR_LOG_OFFER: [u8; 32] = + keccak256!("Offer(uint256,address,uint256,uint256,uint128)"); // offer_id, caller, collection_id, series_id, marketplace_id pub const SELECTOR_LOG_OFFER_CANCEL: [u8; 32] = - keccak256!("OfferCancel(uint256,address,uint256,uint256)"); // offer_id, caller, token_id + keccak256!("OfferCancel(uint256,address,uint256,uint256,uint128)"); // offer_id, caller, token_id, marketplace_id pub const SELECTOR_LOG_OFFER_ACCEPT: [u8; 32] = - keccak256!("OfferAccept(uint256,uint256,address,uint256,uint256)"); // offer_id, amount, caller, collection_id, series_id + keccak256!("OfferAccept(uint256,uint256,address,uint256,uint256,uint128)"); +// offer_id, amount, caller, collection_id, series_id, marketplace_id /// Saturated conversion from EVM uint256 to Blocknumber fn saturated_convert_blocknumber(input: U256) -> Result { @@ -81,22 +103,32 @@ fn saturated_convert_blocknumber(input: U256) -> Result FunctionModifier::NonPayable, @@ -152,24 +186,22 @@ where match selector { Action::RegisterMarketplace => Self::register_marketplace(handle), - Action::SellNftWithMarketplaceId => Self::sell_nft_with_marketplace_id(handle), - Action::SellNftWithoutMarketplace => Self::sell_nft_without_marketplace(handle), + Action::SellNftWithMarketplaceId => Self::sell_nft(handle), + Action::SellNft => Self::sell_nft(handle), + Action::SellSft => Self::sell_sft(handle), Action::UpdateFixedPrice => Self::update_fixed_price(handle), Action::Buy => Self::buy(handle), Action::AuctionNftWithMarketplaceId => { Self::auction_nft_with_marketplace_id(handle) }, - Action::AuctionNftWithoutMarketplace => { - Self::auction_nft_without_marketplace(handle) - }, + Action::AuctionNft => Self::auction_nft_with_marketplace_id(handle), + Action::AuctionSft => Self::auction_sft_with_marketplace_id(handle), Action::Bid => Self::bid(handle), Action::CancelSale => Self::cancel_sale(handle), Action::MakeSimpleOfferWithMarketplaceId => { Self::make_simple_offer_with_marketplace_id(handle) }, - Action::MakeSimpleOfferWithoutMarketplace => { - Self::make_simple_offer_without_marketplace(handle) - }, + Action::MakeSimpleOffer => Self::make_simple_offer_with_marketplace_id(handle), Action::CancelOffer => Self::cancel_offer(handle), Action::AcceptOffer => Self::accept_offer(handle), Action::GetMarketplaceAccount => Self::get_marketplace_account(handle), @@ -258,9 +290,7 @@ where Ok(succeed(EvmDataWriter::new().write(marketplace_id).build())) } - fn sell_nft_with_marketplace_id( - handle: &mut impl PrecompileHandle, - ) -> EvmResult { + fn sell_nft(handle: &mut impl PrecompileHandle) -> EvmResult { handle.record_log_costs_manual(3, 32)?; read_args!( handle, @@ -280,94 +310,150 @@ where ); let marketplace_id: u32 = marketplace_id.saturated_into(); - Self::sell_nft_internal( - handle, - collection_address, - serial_number_ids, + let payment_asset: AssetId = >::evm_id_to_runtime_id( + payment_asset, + ERC20_PRECOMPILE_ADDRESS_PREFIX, + ) + .ok_or_else(|| revert("Marketplace NFT: Invalid payment asset address"))?; + + let duration = Some(saturated_convert_blocknumber(duration)?.into()); + ensure!( + fixed_price <= u128::MAX.into(), + revert("Marketplace NFT: Expected fixed price <= 2^128") + ); + let fixed_price: Balance = fixed_price.saturated_into(); + + let collection_id: CollectionUuid = + >::evm_id_to_runtime_id( + collection_address, + ERC721_PRECOMPILE_ADDRESS_PREFIX, + ) + .ok_or_else(|| revert("Marketplace NFT: Invalid collection address"))?; + + let serials_unbounded = serial_number_ids + .clone() + .into_iter() + .map(|serial_number| { + if serial_number > SerialNumber::MAX.into() { + return Err(revert("Marketplace NFT: Expected serial_number <= 2^32").into()); + } + let serial_number: SerialNumber = serial_number.saturated_into(); + Ok(serial_number) + }) + .collect::, PrecompileFailure>>()?; + + let serial_numbers: BoundedVec = + BoundedVec::try_from(serials_unbounded) + .or_else(|_| Err(revert("Marketplace NFT: Too many serial numbers")))?; + let tokens = ListingTokens::Nft(NftListing { collection_id, serial_numbers }); + + let buyer: H160 = buyer.into(); + let buyer: Option = + if buyer == H160::default() { None } else { Some(buyer.into()) }; + + let caller: Runtime::AccountId = handle.context().caller.into(); + // Manually record gas + handle.record_cost(Runtime::GasWeightMapping::weight_to_gas( + ::WeightInfo::sell_nft( + serial_number_ids.len() as u32 + ), + ))?; + let listing_id = pallet_marketplace::Pallet::::do_sell( + caller, + tokens, buyer, payment_asset, fixed_price, duration, Some(marketplace_id), ) + .map_err(|e| { + revert(alloc::format!("Marketplace NFT: Dispatched call failed with error: {:?}", e)) + })?; + log4( + handle.code_address(), + SELECTOR_LOG_FIXED_PRICE_SALE_LIST_NFT, + handle.context().caller, //seller + H256::from_slice(&EvmDataWriter::new().write(listing_id).build()), + H256::from_slice(&EvmDataWriter::new().write(fixed_price).build()), + EvmDataWriter::new() + .write(serial_number_ids) + .write(collection_address) + .write(marketplace_id) + .build(), + ) + .record(handle)?; + + // Build output. + Ok(succeed(EvmDataWriter::new().write(listing_id).build())) } - fn sell_nft_without_marketplace( - handle: &mut impl PrecompileHandle, - ) -> EvmResult { + fn sell_sft(handle: &mut impl PrecompileHandle) -> EvmResult { handle.record_log_costs_manual(3, 32)?; - read_args!( handle, { collection_address: Address, serial_number_ids: Vec, + quantities: Vec, buyer: Address, payment_asset: Address, fixed_price: U256, - duration: U256 + duration: U256, + marketplace_id: U256 } ); - let marketplace_id: Option = None; - - Self::sell_nft_internal( - handle, - collection_address, - serial_number_ids, - buyer, - payment_asset, - fixed_price, - duration, - marketplace_id, - ) - } + ensure!( + marketplace_id <= u32::MAX.into(), + revert("Marketplace: Expected marketplace id <= 2^32") + ); + let marketplace_id: u32 = marketplace_id.saturated_into(); - fn sell_nft_internal( - handle: &mut impl PrecompileHandle, - collection_address: Address, - serial_number_ids: Vec, - buyer: Address, - payment_asset: Address, - fixed_price: U256, - duration: U256, - marketplace_id: Option, - ) -> EvmResult { // Parse asset_id let payment_asset: AssetId = >::evm_id_to_runtime_id( payment_asset, ERC20_PRECOMPILE_ADDRESS_PREFIX, ) - .ok_or_else(|| revert("Marketplace: Invalid payment asset address"))?; + .ok_or_else(|| revert("Marketplace SFT: Invalid payment asset address"))?; let duration = Some(saturated_convert_blocknumber(duration)?.into()); ensure!( fixed_price <= u128::MAX.into(), - revert("Marketplace: Expected fixed price <= 2^128") + revert("Marketplace SFT: Expected fixed price <= 2^128") ); let fixed_price: Balance = fixed_price.saturated_into(); let collection_id: CollectionUuid = >::evm_id_to_runtime_id( collection_address, - ERC721_PRECOMPILE_ADDRESS_PREFIX, + ERC1155_PRECOMPILE_ADDRESS_PREFIX, ) - .ok_or_else(|| revert("Marketplace: Invalid collection address"))?; + .ok_or_else(|| revert("Marketplace SFT: Invalid collection address"))?; + ensure!( + serial_number_ids.len() == quantities.len(), + revert("Marketplace SFT: Expected serial number ids and quantities array to be equal") + ); let serials_unbounded = serial_number_ids .clone() .into_iter() - .map(|serial_number| { + .zip(quantities.clone()) + .map(|(serial_number, quantity)| { if serial_number > SerialNumber::MAX.into() { - return Err(revert("Marketplace: Expected serial_number <= 2^32").into()); + return Err(revert("Marketplace SFT: Expected serial_number <= 2^32").into()); + } + if quantity > Balance::MAX.into() { + return Err(revert("Marketplace SFT: Expected quantity <= 2^128").into()); } let serial_number: SerialNumber = serial_number.saturated_into(); - Ok(serial_number) + let quantity: Balance = quantity.saturated_into(); + Ok((serial_number, quantity)) }) - .collect::, PrecompileFailure>>()?; + .collect::, PrecompileFailure>>()?; - let serial_numbers: BoundedVec = + let serial_numbers: BoundedVec<(SerialNumber, Balance), Runtime::MaxTokensPerListing> = BoundedVec::try_from(serials_unbounded) .or_else(|_| Err(revert("Marketplace: Too many serial numbers")))?; - let tokens = ListingTokens::Nft(NftListing { collection_id, serial_numbers }); + let tokens = ListingTokens::Sft(SftListing { collection_id, serial_numbers }); let buyer: H160 = buyer.into(); let buyer: Option = if buyer == H160::default() { None } else { Some(buyer.into()) }; @@ -375,7 +461,7 @@ where let caller: Runtime::AccountId = handle.context().caller.into(); // Manually record gas handle.record_cost(Runtime::GasWeightMapping::weight_to_gas( - ::WeightInfo::sell_nft( + ::WeightInfo::sell_sft( serial_number_ids.len() as u32 ), ))?; @@ -386,19 +472,23 @@ where payment_asset, fixed_price, duration, - marketplace_id, + Some(marketplace_id), ) .map_err(|e| { - revert(alloc::format!("Marketplace: Dispatched call failed with error: {:?}", e)) + revert(alloc::format!("Marketplace SFT: Dispatched call failed with error: {:?}", e)) })?; - log4( handle.code_address(), - SELECTOR_LOG_FIXED_PRICE_SALE_LIST, + SELECTOR_LOG_FIXED_PRICE_SALE_LIST_SFT, handle.context().caller, //seller H256::from_slice(&EvmDataWriter::new().write(listing_id).build()), H256::from_slice(&EvmDataWriter::new().write(fixed_price).build()), - EvmDataWriter::new().write(serial_number_ids).write(collection_address).build(), + EvmDataWriter::new() + .write(serial_number_ids) + .write(collection_address) + .write(marketplace_id) + .write::>(quantities) + .build(), ) .record(handle)?; @@ -440,15 +530,20 @@ where let (collection_id, serial_numbers) = Self::split_listing_tokens(listing.tokens)?; let collection_id = H256::from_low_u64_be(collection_id as u64); - + let marketplace_id = listing.marketplace_id; let caller: H160 = caller.into(); + log4( handle.code_address(), SELECTOR_LOG_FIXED_PRICE_SALE_UPDATE, collection_id, H256::from_slice(&EvmDataWriter::new().write(listing_id).build()), H256::from_slice(&EvmDataWriter::new().write(new_price).build()), - EvmDataWriter::new().write(Address::from(caller)).write(serial_numbers).build(), + EvmDataWriter::new() + .write(Address::from(caller)) + .write(serial_numbers) + .write(marketplace_id.unwrap_or_default()) + .build(), ) .record(handle)?; @@ -480,6 +575,7 @@ where Ok(listing) => { let (collection_id, serial_numbers) = Self::split_listing_tokens(listing.tokens)?; let collection_id = H256::from_low_u64_be(collection_id as u64); + let marketplace_id = listing.marketplace_id.unwrap_or_default(); let seller = listing.seller; let seller: H160 = seller.into(); @@ -489,7 +585,11 @@ where collection_id, H256::from_slice(&EvmDataWriter::new().write(listing_id).build()), H256::from_slice(&EvmDataWriter::new().write(listing.fixed_price).build()), - EvmDataWriter::new().write(Address::from(seller)).write(serial_numbers).build(), + EvmDataWriter::new() + .write(Address::from(seller)) + .write(serial_numbers) + .write(marketplace_id) + .build(), ) .record(handle)?; @@ -504,7 +604,7 @@ where } } - fn auction_nft_without_marketplace( + fn auction_nft_with_marketplace_id( handle: &mut impl PrecompileHandle, ) -> EvmResult { handle.record_log_costs_manual(3, 32)?; @@ -517,24 +617,95 @@ where serial_number_ids: Vec, payment_asset: Address, reserve_price: U256, - duration: U256 + duration: U256, + marketplace_id: U256 } ); - let marketplace_id: Option = None; + ensure!( + marketplace_id <= u32::MAX.into(), + revert("Marketplace: Expected marketplace id <= 2^32") + ); + let marketplace_id: u32 = marketplace_id.saturated_into(); - Self::auction_nft_internal( - handle, - collection_address, - serial_number_ids, + let duration = Some(saturated_convert_blocknumber(duration)?.into()); + ensure!( + reserve_price <= Balance::MAX.into(), + revert("Marketplace NFT: Expected reserve_price <= 2^128") + ); + let reserve_price: Balance = reserve_price.saturated_into(); + + let collection_id: CollectionUuid = + >::evm_id_to_runtime_id( + collection_address, + ERC721_PRECOMPILE_ADDRESS_PREFIX, + ) + .ok_or_else(|| revert("Marketplace NFT: Invalid collection address"))?; + + let serials_unbounded = serial_number_ids + .clone() + .into_iter() + .map(|serial_number| { + if serial_number > SerialNumber::MAX.into() { + return Err(revert("Marketplace NFT: Expected serial_number <= 2^32").into()); + } + let serial_number: SerialNumber = serial_number.saturated_into(); + Ok(serial_number) + }) + .collect::, PrecompileFailure>>()?; + + let serial_numbers: BoundedVec = + BoundedVec::try_from(serials_unbounded) + .or_else(|_| Err(revert("Marketplace NFT: Too many serial numbers")))?; + let tokens = ListingTokens::Nft(NftListing { collection_id, serial_numbers }); + + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; + + // Parse asset_id + let payment_asset: AssetId = >::evm_id_to_runtime_id( + payment_asset, + ERC20_PRECOMPILE_ADDRESS_PREFIX, + ) + .ok_or_else(|| revert("Marketplace NFT: Invalid payment asset address"))?; + + handle.record_cost(Runtime::GasWeightMapping::weight_to_gas( + ::WeightInfo::auction_nft( + serial_number_ids.len() as u32, + ), + ))?; + + let caller: Runtime::AccountId = handle.context().caller.into(); + let listing_id = pallet_marketplace::Pallet::::do_auction( + caller, + tokens, payment_asset, reserve_price, duration, - marketplace_id, + Some(marketplace_id), ) + .map_err(|e| { + revert(alloc::format!("Marketplace: Dispatched call failed with error: {:?}", e)) + })?; + let collection_id = H256::from_low_u64_be(collection_id as u64); + log4( + handle.code_address(), + SELECTOR_LOG_AUCTION_OPEN_NFT, + collection_id, + H256::from_slice(&EvmDataWriter::new().write(listing_id).build()), + H256::from_slice(&EvmDataWriter::new().write(reserve_price).build()), + EvmDataWriter::new() + .write(Address::from(handle.context().caller)) + .write(serial_number_ids) + .write(marketplace_id) + .build(), + ) + .record(handle)?; + + // Build output. + Ok(succeed([])) } - fn auction_nft_with_marketplace_id( + fn auction_sft_with_marketplace_id( handle: &mut impl PrecompileHandle, ) -> EvmResult { handle.record_log_costs_manual(3, 32)?; @@ -545,6 +716,7 @@ where { collection_address: Address, serial_number_ids: Vec, + quantities: Vec, payment_asset: Address, reserve_price: U256, duration: U256, @@ -558,54 +730,46 @@ where ); let marketplace_id: u32 = marketplace_id.saturated_into(); - Self::auction_nft_internal( - handle, - collection_address, - serial_number_ids, - payment_asset, - reserve_price, - duration, - Some(marketplace_id), - ) - } - - fn auction_nft_internal( - handle: &mut impl PrecompileHandle, - collection_address: Address, - serial_number_ids: Vec, - payment_asset: Address, - reserve_price: U256, - duration: U256, - marketplace_id: Option, - ) -> EvmResult { let duration = Some(saturated_convert_blocknumber(duration)?.into()); ensure!( reserve_price <= Balance::MAX.into(), - revert("Marketplace: Expected reserve_price <= 2^128") + revert("Marketplace SFT: Expected reserve_price <= 2^128") ); let reserve_price: Balance = reserve_price.saturated_into(); + let collection_id: CollectionUuid = >::evm_id_to_runtime_id( collection_address, - ERC721_PRECOMPILE_ADDRESS_PREFIX, + ERC1155_PRECOMPILE_ADDRESS_PREFIX, ) .ok_or_else(|| revert("Marketplace: Invalid collection address"))?; + + ensure!( + serial_number_ids.len() == quantities.len(), + revert("Marketplace: Expected serial number ids and quantities array to be equal") + ); + let serials_unbounded = serial_number_ids .clone() .into_iter() - .map(|serial_number| { + .zip(quantities.clone()) + .map(|(serial_number, quantity)| { if serial_number > SerialNumber::MAX.into() { return Err(revert("Marketplace: Expected serial_number <= 2^32").into()); } + if quantity > Balance::MAX.into() { + return Err(revert("Marketplace: Expected quantity <= 2^128").into()); + } let serial_number: SerialNumber = serial_number.saturated_into(); - Ok(serial_number) + let quantity: Balance = quantity.saturated_into(); + Ok((serial_number, quantity)) }) - .collect::, PrecompileFailure>>()?; + .collect::, PrecompileFailure>>()?; - let serial_numbers: BoundedVec = + let serial_numbers: BoundedVec<(SerialNumber, Balance), Runtime::MaxTokensPerListing> = BoundedVec::try_from(serials_unbounded) .or_else(|_| Err(revert("Marketplace: Too many serial numbers")))?; - let tokens = ListingTokens::Nft(NftListing { collection_id, serial_numbers }); + let tokens = ListingTokens::Sft(SftListing { collection_id, serial_numbers }); handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; @@ -614,10 +778,10 @@ where payment_asset, ERC20_PRECOMPILE_ADDRESS_PREFIX, ) - .ok_or_else(|| revert("Marketplace: Invalid payment asset address"))?; + .ok_or_else(|| revert("Marketplace SFT: Invalid payment asset address"))?; handle.record_cost(Runtime::GasWeightMapping::weight_to_gas( - ::WeightInfo::auction_nft( + ::WeightInfo::auction_sft( serial_number_ids.len() as u32, ), ))?; @@ -629,21 +793,22 @@ where payment_asset, reserve_price, duration, - marketplace_id, + Some(marketplace_id), ) .map_err(|e| { - revert(alloc::format!("Marketplace: Dispatched call failed with error: {:?}", e)) + revert(alloc::format!("Marketplace SFT: Dispatched call failed with error: {:?}", e)) })?; let collection_id = H256::from_low_u64_be(collection_id as u64); log4( handle.code_address(), - SELECTOR_LOG_AUCTION_OPEN, + SELECTOR_LOG_AUCTION_OPEN_SFT, collection_id, H256::from_slice(&EvmDataWriter::new().write(listing_id).build()), H256::from_slice(&EvmDataWriter::new().write(reserve_price).build()), EvmDataWriter::new() .write(Address::from(handle.context().caller)) .write(serial_number_ids) + .write(marketplace_id) .build(), ) .record(handle)?; @@ -667,6 +832,10 @@ where revert("Marketplace: Expected listing_id <= 2^128") ); let listing_id: u128 = listing_id.saturated_into(); + let listing = match pallet_marketplace::Pallet::::get_listing_detail(listing_id) { + Ok(Listing::Auction(listing)) => listing, + _ => return Err(revert("NotForAuction")), + }; ensure!(amount <= u128::MAX.into(), revert("Marketplace: Expected amount <= 2^128")); let amount: Balance = amount.saturated_into(); let origin = handle.context().caller; @@ -676,13 +845,14 @@ where pallet_marketplace::Call::::bid { listing_id, amount }, )?; + let marketplace_id = listing.marketplace_id.unwrap_or_default(); log4( handle.code_address(), SELECTOR_LOG_BID, handle.context().caller, //bidder H256::from_slice(&EvmDataWriter::new().write(listing_id).build()), H256::from_slice(&EvmDataWriter::new().write(amount).build()), - vec![], + EvmDataWriter::new().write(marketplace_id).build(), ) .record(handle)?; @@ -719,7 +889,8 @@ where )?; let collection_id = H256::from_low_u64_be(collection_id as u64); match listing { - Listing::FixedPrice(_sale) => { + Listing::FixedPrice(sale) => { + let marketplace_id = sale.marketplace_id.unwrap_or_default(); log3( handle.code_address(), SELECTOR_LOG_FIXED_PRICE_SALE_CLOSE, @@ -728,11 +899,13 @@ where EvmDataWriter::new() .write(Address::from(handle.context().caller)) .write(serial_numbers) + .write(marketplace_id) .build(), ) .record(handle)?; }, - Listing::Auction(_auction) => { + Listing::Auction(auction) => { + let marketplace_id = auction.marketplace_id.unwrap_or_default(); log3( handle.code_address(), SELECTOR_LOG_AUCTION_CLOSE, @@ -741,6 +914,7 @@ where EvmDataWriter::new() .write(Address::from(handle.context().caller)) .write(serial_numbers) + .write(marketplace_id) .build(), ) .record(handle)?; @@ -750,32 +924,6 @@ where // Build output. Ok(succeed([])) } - fn make_simple_offer_without_marketplace( - handle: &mut impl PrecompileHandle, - ) -> EvmResult { - handle.record_log_costs_manual(2, 32)?; - - // Parse input. - read_args!( - handle, - { - collection_address: Address, - serial_number: U256, - amount: U256, - asset_id: Address - } - ); - let marketplace_id: Option = None; - - Self::make_simple_offer_internal( - handle, - collection_address, - serial_number, - amount, - asset_id, - marketplace_id, - ) - } fn make_simple_offer_with_marketplace_id( handle: &mut impl PrecompileHandle, @@ -794,24 +942,6 @@ where } ); let marketplace_id: u32 = marketplace_id.saturated_into(); - Self::make_simple_offer_internal( - handle, - collection_address, - serial_number, - amount, - asset_id, - Some(marketplace_id), - ) - } - - fn make_simple_offer_internal( - handle: &mut impl PrecompileHandle, - collection_address: Address, - serial_number: U256, - amount: U256, - asset_id: Address, - marketplace_id: Option, - ) -> EvmResult { ensure!(amount <= u128::MAX.into(), revert("Marketplace: Expected amount <= 2^128")); let amount: Balance = amount.saturated_into(); let collection_id: CollectionUuid = @@ -843,7 +973,7 @@ where token_id, amount, asset_id, - marketplace_id, + Some(marketplace_id), ) .map_err(|e| { revert(alloc::format!("Marketplace: Dispatched call failed with error: {:?}", e)) @@ -854,7 +984,11 @@ where SELECTOR_LOG_OFFER, H256::from_slice(&EvmDataWriter::new().write(offer_id).build()), handle.context().caller, - EvmDataWriter::new().write(collection_id).write(serial_number).build(), + EvmDataWriter::new() + .write(collection_id) + .write(serial_number) + .write(marketplace_id) + .build(), ) .record(handle)?; @@ -882,12 +1016,17 @@ where )?; let (collection_id, serial_number) = offer.token_id; let offer_id = H256::from_low_u64_be(offer_id); + let marketplace_id = offer.marketplace_id.unwrap_or_default(); log3( handle.code_address(), SELECTOR_LOG_OFFER_CANCEL, offer_id, handle.context().caller, - EvmDataWriter::new().write(collection_id).write(serial_number).build(), + EvmDataWriter::new() + .write(collection_id) + .write(serial_number) + .write(marketplace_id) + .build(), ) .record(handle)?; Ok(succeed([])) @@ -914,15 +1053,21 @@ where )?; let offer_id = H256::from_low_u64_be(offer_id); let (collection_id, serial_number) = offer.token_id; + let marketplace_id = offer.marketplace_id.unwrap_or_default(); log4( handle.code_address(), SELECTOR_LOG_OFFER_ACCEPT, offer_id, H256::from_slice(&EvmDataWriter::new().write(offer.amount).build()), handle.context().caller, - EvmDataWriter::new().write(collection_id).write(serial_number).build(), + EvmDataWriter::new() + .write(collection_id) + .write(serial_number) + .write(marketplace_id) + .build(), ) .record(handle)?; + Ok(succeed([])) } @@ -1022,7 +1167,17 @@ where let serial_numbers = tokens.serial_numbers.into_inner(); Ok((collection_id, serial_numbers)) }, - _ => Err(revert("Marketplace: Expected NFT tokens")), + ListingTokens::Sft(tokens) => { + let collection_id = tokens.collection_id; + // let serial_numbers = tokens.serial_numbers.into_inner(); + let serial_numbers = tokens + .serial_numbers + .clone() + .into_iter() + .map(|(serial_number, _quantity)| serial_number) + .collect(); + Ok((collection_id, serial_numbers)) + }, } } } diff --git a/evm-precompiles/utils/src/constants.rs b/evm-precompiles/utils/src/constants.rs index 7aacba121..332f29b9e 100644 --- a/evm-precompiles/utils/src/constants.rs +++ b/evm-precompiles/utils/src/constants.rs @@ -49,5 +49,5 @@ mod precompile_addresses { /// Precompile address for futurepass registar pub const FUTUREPASS_REGISTRAR_PRECOMPILE: u64 = 65_535; // 0xFFFF /// Precompile address for marketplace - pub const MARKETPLACE_PRECOMPILE: u64 = 1741; // 0x06C3 + pub const MARKETPLACE_PRECOMPILE: u64 = 1741; // 0x06CD }