diff --git a/packages/checkout/sdk-sample-app/src/components/Listings.tsx b/packages/checkout/sdk-sample-app/src/components/Listings.tsx index a46ebd50dd..4fa49b66bc 100644 --- a/packages/checkout/sdk-sample-app/src/components/Listings.tsx +++ b/packages/checkout/sdk-sample-app/src/components/Listings.tsx @@ -15,12 +15,17 @@ interface ListingsProps { export default function Listings({ checkout, provider }: ListingsProps) { const [sellContractAddress, setSellContractAddress] = useState(''); const [orderIdError, setAddressError] = useState(null); + + const [listingId, setListingId] = useState(''); + const [listingIdError, setListingIdError] = useState(null); + const [error, setError] = useState(null); const [loading, setLoading] = useState(false); async function getListingsClick() { - if (!sellContractAddress) { + if (!sellContractAddress && !listingId) { setAddressError('Please enter an collection address'); + setListingIdError('Or enter an collection address'); return; } if (!checkout) { @@ -34,12 +39,18 @@ export default function Listings({ checkout, provider }: ListingsProps) { setError(null); setLoading(true); try { - const orderBook = new Orderbook({baseConfig: {environment: checkout.config.environment}}) - const listingsResult = await orderBook.listListings({ + const orderBook = new Orderbook({baseConfig: {environment: checkout.config.environment}}) + + if (listingId) { + const result = await orderBook.getListing(listingId) + console.log('listings:', result) + } else { + const result = await orderBook.listListings({ sellItemContractAddress: sellContractAddress, status: OrderStatusName.ACTIVE }) - console.log('listings:', listingsResult) + console.log('listings:', result) + } setLoading(false); } catch (err: any) { setError(err); @@ -54,6 +65,13 @@ export default function Listings({ checkout, provider }: ListingsProps) { const updateSellContractAddress = (event: any) => { setSellContractAddress(event.target.value); setAddressError(''); + setListingIdError(''); + } + + const updateListingId = (event: any) => { + setListingId(event.target.value); + setAddressError(''); + setListingIdError(''); } useEffect(() => { @@ -64,13 +82,21 @@ export default function Listings({ checkout, provider }: ListingsProps) { return ( - Sell Collection Address + Collection Address {orderIdError && ( {orderIdError} )}
+ + Listing Id (optional) + + {listingIdError && ( + {listingIdError} + )} + +
Get Listings diff --git a/packages/checkout/sdk-sample-app/src/components/Sell.tsx b/packages/checkout/sdk-sample-app/src/components/Sell.tsx index d8a9ad69ce..1259cdb942 100644 --- a/packages/checkout/sdk-sample-app/src/components/Sell.tsx +++ b/packages/checkout/sdk-sample-app/src/components/Sell.tsx @@ -22,12 +22,19 @@ export default function Sell({ checkout, provider }: SellProps) { const [listingTypeError, setListingTypeError] = useState(''); const [amount, setAmount] = useState(''); const [amountError, setAmountError] = useState(''); + const [expiry, setExpiry] = useState(undefined); + const [expiryError, setExpiryError] = useState(''); const [tokenAddress, setTokenAddress] = useState(''); const [contractAddressError, setContractAddressError] = useState(''); const [error, setError] = useState(null); const [success, setSuccess] = useState(false); const [loading, setLoading] = useState(false); + const isDateValid = (dateStr: string) => { + var dateRegex = /^(\d{4})-(\d{2})-(\d{2})(?:T(\d{2}):(\d{2}):(\d{2})Z)?$/; + return dateRegex.test(dateStr); + } + const getBuyToken = (): BuyToken => { if (listingType === ItemType.NATIVE) { return { @@ -45,21 +52,31 @@ export default function Sell({ checkout, provider }: SellProps) { async function sellClick() { if (!id) { setIdError('Please enter the ID of the ERC721'); + return } if (!collectionAddress) { setCollectionAddressError('Please enter the collection address for the ERC721'); + return } if (!listingType) { setListingTypeError('Please select the listing type'); + return } if (listingType === ItemType.NATIVE && !amount) { setAmountError('Please enter the amount of NATIVE tokens to sell the ERC721 for'); + return } if (listingType === ItemType.ERC20 && !amount) { setAmountError('Please enter the amount of ERC20 tokens to sell the ERC721 for'); + return } if (listingType === ItemType.ERC20 && !tokenAddress) { setContractAddressError('Please enter the contract address for the ERC20'); + return + } + if (expiry && !isDateValid(expiry)) { + setExpiryError('Invalid date - format YYYY-MM-DD or YYYY-MM-DDTHH:MM:SSZ'); + return } if (!id || !collectionAddress || @@ -89,7 +106,8 @@ export default function Sell({ checkout, provider }: SellProps) { makerFees: [{ amount: { percentageDecimal: 0.025 }, recipient: '0xEac347177DbA4a190B632C7d9b8da2AbfF57c772' - }] + }], + orderExpiry: expiry ? new Date(expiry) : undefined }] const result = await checkout.sell({ @@ -118,6 +136,11 @@ export default function Sell({ checkout, provider }: SellProps) { setCollectionAddressError(''); } + const updateExpiry = (event: any) => { + setExpiry(event.target.value); + setError(''); + } + const updateAmount = (event: any) => { const value = event.target.value; setAmount(value); @@ -226,6 +249,13 @@ export default function Sell({ checkout, provider }: SellProps) { {collectionAddressError} )} + + Expiry + + {expiryError && ( + {expiryError} + )} + {tokenForm()}
diff --git a/packages/checkout/sdk/src/smartCheckout/sell/sell.test.ts b/packages/checkout/sdk/src/smartCheckout/sell/sell.test.ts index dd42d6eed2..09f049d2b9 100644 --- a/packages/checkout/sdk/src/smartCheckout/sell/sell.test.ts +++ b/packages/checkout/sdk/src/smartCheckout/sell/sell.test.ts @@ -30,13 +30,14 @@ jest.mock('../actions'); describe('sell', () => { const seaportContractAddress = '0xSEAPORT'; + const walletAddress = '0xADDRESS'; let config: CheckoutConfiguration; let mockProvider: Web3Provider; beforeEach(() => { mockProvider = { getSigner: jest.fn().mockReturnValue({ - getAddress: jest.fn().mockResolvedValue('0xADDRESS'), + getAddress: jest.fn().mockResolvedValue(walletAddress), }), } as unknown as Web3Provider; @@ -89,30 +90,35 @@ describe('sell', () => { erc721TransactionRequirement, ], }); + const mockCreateListing = jest.fn().mockResolvedValue({ result: { id: '1234', }, }); + + const prepareListing = jest.fn().mockResolvedValue({ + actions: [ + { + type: ActionType.SIGNABLE, + purpose: SignablePurpose.CREATE_LISTING, + message: { + domain: '', + types: '', + value: '', + }, + }, + ], + }); + (createOrderbookInstance as jest.Mock).mockReturnValue({ config: jest.fn().mockReturnValue({ seaportContractAddress, }), - prepareListing: jest.fn().mockResolvedValue({ - actions: [ - { - type: ActionType.SIGNABLE, - purpose: SignablePurpose.CREATE_LISTING, - message: { - domain: '', - types: '', - value: '', - }, - }, - ], - }), + prepareListing, createListing: mockCreateListing, }); + (getUnsignedMessage as jest.Mock).mockReturnValue( { orderHash: 'hash', @@ -136,7 +142,8 @@ describe('sell', () => { type: SignTransactionStatusType.SUCCESS, }); - const orders:Array = [{ + const orderExpiry = new Date('2022-03-25'); + const order: SellOrder = { sellToken: { id, collectionAddress: contractAddress, @@ -149,12 +156,13 @@ describe('sell', () => { amount: { percentageDecimal: 0.025 }, recipient: '0xEac347177DbA4a190B632C7d9b8da2AbfF57c772', }], - }]; + orderExpiry, + }; const result = await sell( config, mockProvider, - orders, + [order], ); expect(result).toEqual({ @@ -178,6 +186,19 @@ describe('sell', () => { }, }, ); + expect(prepareListing).toBeCalledWith({ + makerAddress: walletAddress, + buy: { + type: ItemType.NATIVE, + amount: '1000000000000000000', + }, + sell: { + type: ItemType.ERC721, + contractAddress: order.sellToken.collectionAddress, + tokenId: order.sellToken.id, + }, + orderExpiry: order.orderExpiry, + }); expect(signMessage).toBeCalledWith( mockProvider, { diff --git a/packages/checkout/sdk/src/smartCheckout/sell/sell.ts b/packages/checkout/sdk/src/smartCheckout/sell/sell.ts index fc5623f3d8..46e73aa5ff 100644 --- a/packages/checkout/sdk/src/smartCheckout/sell/sell.ts +++ b/packages/checkout/sdk/src/smartCheckout/sell/sell.ts @@ -81,7 +81,12 @@ export const sell = async ( ); } - const { buyToken, sellToken, makerFees } = orders[0]; + const { + buyToken, + sellToken, + makerFees, + orderExpiry, + } = orders[0]; let decimals = 18; if (buyToken.type === ItemType.ERC20) { @@ -121,6 +126,7 @@ export const sell = async ( contractAddress: sellToken.collectionAddress, tokenId: sellToken.id, }, + orderExpiry, }), ); } catch (err: any) { diff --git a/packages/checkout/sdk/src/types/smartCheckout.ts b/packages/checkout/sdk/src/types/smartCheckout.ts index 72c323c19a..a1e7d08da8 100644 --- a/packages/checkout/sdk/src/types/smartCheckout.ts +++ b/packages/checkout/sdk/src/types/smartCheckout.ts @@ -165,8 +165,10 @@ export type SellOrder = { sellToken: SellToken, /** the token info of the price of the item */ buyToken: BuyToken, - /** option array of makerFees to be applied to the listing */ + /** optional array of makerFees to be applied to the listing */ makerFees?: OrderFee[], + /** optional order expiry date. Default order expiry to 2 years from now */ + orderExpiry?: Date; }; /**