From b7322f6b6590a0b088759c0b704ee00876469e8d Mon Sep 17 00:00:00 2001 From: Sam Jeston Date: Fri, 3 May 2024 14:39:22 +1000 Subject: [PATCH] Add ERC1155 support to orderbook package --- packages/orderbook/src/openapi/mapper.ts | 15 ++++++++-- packages/orderbook/src/orderbook.ts | 5 +++- .../orderbook/src/seaport/seaport.test.ts | 2 ++ packages/orderbook/src/seaport/seaport.ts | 30 +++++++++++++------ packages/orderbook/src/types.ts | 19 +++++++++--- 5 files changed, 55 insertions(+), 16 deletions(-) diff --git a/packages/orderbook/src/openapi/mapper.ts b/packages/orderbook/src/openapi/mapper.ts index b96f88bcab..abb15b6194 100644 --- a/packages/orderbook/src/openapi/mapper.ts +++ b/packages/orderbook/src/openapi/mapper.ts @@ -9,6 +9,7 @@ import { FeeType, Page, Trade, + ERC1155Item, } from '../types'; export function mapFromOpenApiOrder(order: OpenApiOrder): Order { @@ -31,7 +32,7 @@ export function mapFromOpenApiOrder(order: OpenApiOrder): Order { throw new Error('Buy items must be either ERC20 or NATIVE'); }); - const sellItems: ERC721Item[] = order.sell.map((item) => { + const sellItems: (ERC721Item | ERC1155Item)[] = order.sell.map((item) => { if (item.type === 'ERC721') { return { type: 'ERC721', @@ -40,7 +41,16 @@ export function mapFromOpenApiOrder(order: OpenApiOrder): Order { }; } - throw new Error('Sell items must ERC721'); + if (item.type === 'ERC1155') { + return { + type: 'ERC1155', + contractAddress: item.contract_address, + tokenId: item.token_id, + amount: item.amount, + }; + } + + throw new Error('Sell items must ERC721 or ERC1155'); }); return { @@ -54,6 +64,7 @@ export function mapFromOpenApiOrder(order: OpenApiOrder): Order { recipientAddress: fee.recipient_address, type: fee.type as unknown as FeeType, })), + fillStatus: order.fill_status, chain: order.chain, createdAt: order.created_at, endAt: order.end_at, diff --git a/packages/orderbook/src/orderbook.ts b/packages/orderbook/src/orderbook.ts index 4907f1435e..d6208f3d4c 100644 --- a/packages/orderbook/src/orderbook.ts +++ b/packages/orderbook/src/orderbook.ts @@ -209,12 +209,15 @@ export class Orderbook { * @param {string} listingId - The listingId to fulfil. * @param {string} takerAddress - The address of the account fulfilling the order. * @param {FeeValue[]} takerFees - Taker ecosystem fees to be paid. + * @param {string} amountToFill - Amount of the order to fill, defaults to sell item amount. + * Only applies to ERC1155 orders * @return {FulfillOrderResponse} Approval and fulfilment transactions. */ async fulfillOrder( listingId: string, takerAddress: string, takerFees: FeeValue[], + amountToFill?: string, ): Promise { const fulfillmentDataRes = await this.apiClient.fulfillmentData([ { @@ -246,7 +249,7 @@ export class Orderbook { ); } - return this.seaport.fulfillOrder(orderResult, takerAddress, extraData); + return this.seaport.fulfillOrder(orderResult, takerAddress, extraData, amountToFill); } async fulfillBulkOrders( diff --git a/packages/orderbook/src/seaport/seaport.test.ts b/packages/orderbook/src/seaport/seaport.test.ts index 56795b7fbb..1e38bdcf4c 100644 --- a/packages/orderbook/src/seaport/seaport.test.ts +++ b/packages/orderbook/src/seaport/seaport.test.ts @@ -400,6 +400,7 @@ describe('Seaport', () => { chain: { id: '1', name: 'imtbl-zkevm-local' }, created_at: new Date().toISOString(), end_at: new Date().toISOString(), + fill_status: { numerator: '0', denominator: '0' }, id: '1', order_hash: randomAddress(), protocol_data: { @@ -466,6 +467,7 @@ describe('Seaport', () => { parameters: anything(), signature: immutableOrder.signature, }, + unitsToFill: undefined, extraData: fakeExtraData, tips: [], }, diff --git a/packages/orderbook/src/seaport/seaport.ts b/packages/orderbook/src/seaport/seaport.ts index 07c94c1088..38edc18d62 100644 --- a/packages/orderbook/src/seaport/seaport.ts +++ b/packages/orderbook/src/seaport/seaport.ts @@ -1,6 +1,7 @@ import { Seaport as SeaportLib } from '@opensea/seaport-js'; import { ApprovalAction, + CreateInputItem, CreateOrderAction, ExchangeAction, OrderComponents, @@ -12,6 +13,7 @@ import { mapFromOpenApiOrder } from 'openapi/mapper'; import { Action, ActionType, + ERC1155Item, ERC20Item, ERC721Item, FulfillOrderResponse, @@ -45,7 +47,7 @@ export class Seaport { async prepareSeaportOrder( offerer: string, - listingItem: ERC721Item, + listingItem: ERC721Item | ERC1155Item, considerationItem: ERC20Item | NativeItem, orderStart: Date, orderExpiry: Date, @@ -104,6 +106,7 @@ export class Seaport { order: Order, account: string, extraData: string, + unitsToFill?: string, ): Promise { const { orderComponents, tips } = this.mapImmutableOrderToSeaportOrderComponents(order); const seaportLib = this.getSeaportLib(order); @@ -116,6 +119,7 @@ export class Seaport { parameters: orderComponents, signature: order.signature, }, + unitsToFill, extraData, tips, }, @@ -264,22 +268,30 @@ export class Seaport { private createSeaportOrder( offerer: string, - listingItem: ERC721Item, + listingItem: ERC721Item | ERC1155Item, considerationItem: ERC20Item | NativeItem, orderStart: Date, orderExpiry: Date, ): Promise> { const seaportLib = this.getSeaportLib(); + + const offerItem: CreateInputItem = listingItem.type === 'ERC721' + ? { + itemType: ItemType.ERC721, + token: listingItem.contractAddress, + identifier: listingItem.tokenId, + } + : { + itemType: ItemType.ERC1155, + token: listingItem.contractAddress, + identifier: listingItem.tokenId, + amount: listingItem.amount, + }; + return seaportLib.createOrder( { allowPartialFills: false, - offer: [ - { - itemType: ItemType.ERC721, - token: listingItem.contractAddress, - identifier: listingItem.tokenId, - }, - ], + offer: [offerItem], consideration: [ { token: diff --git a/packages/orderbook/src/types.ts b/packages/orderbook/src/types.ts index 01df436da4..f58a26fed9 100644 --- a/packages/orderbook/src/types.ts +++ b/packages/orderbook/src/types.ts @@ -5,6 +5,13 @@ import { Fee as OpenapiFee, OrdersService, OrderStatus } from './openapi/sdk'; // Strictly re-export only the OrderStatusName enum from the openapi types export { OrderStatusName } from './openapi/sdk'; +export interface ERC1155Item { + type: 'ERC1155'; + contractAddress: string; + tokenId: string; + amount: string; +} + export interface ERC721Item { type: 'ERC721'; contractAddress: string; @@ -29,7 +36,7 @@ export interface RoyaltyInfo { export interface PrepareListingParams { makerAddress: string; - sell: ERC721Item; + sell: ERC721Item | ERC1155Item; buy: ERC20Item | NativeItem; orderExpiry?: Date; } @@ -162,7 +169,7 @@ export interface Order { type: 'LISTING'; accountAddress: string; buy: (ERC20Item | NativeItem)[]; - sell: ERC721Item[]; + sell: (ERC721Item | ERC1155Item)[]; fees: Fee[]; chain: { id: string; @@ -170,6 +177,10 @@ export interface Order { }; createdAt: string; updatedAt: string; + fillStatus: { + numerator: string, + denominator: string, + }; /** * Time after which the Order is considered active */ @@ -180,7 +191,7 @@ export interface Order { endAt: string; orderHash: string; protocolData: { - orderType: 'FULL_RESTRICTED'; + orderType: 'FULL_RESTRICTED' | 'PARTIAL_RESTRICTED'; zoneAddress: string; counter: string; seaportAddress: string; @@ -219,7 +230,7 @@ export interface Trade { name: string; }; buy: (ERC20Item | NativeItem)[]; - sell: ERC721Item[]; + sell: (ERC721Item | ERC1155Item)[]; buyerFees: Fee[]; sellerAddress: string; buyerAddress: string;