Skip to content

Commit 9c7eb9f

Browse files
authored
feat(orderbook): Add utility functions for creating metadata bids (#2870)
1 parent 96d78d6 commit 9c7eb9f

14 files changed

Lines changed: 841 additions & 3 deletions

File tree

packages/orderbook/src/api-client/api-client.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ import {
77
ListCollectionBidsResult,
88
ListingResult,
99
ListListingsResult,
10+
ListMetadataBidsResult,
1011
ListTradeResult,
1112
ListTraitBidsResult,
13+
MetadataBidResult,
1214
OrdersService,
1315
TradeResult,
1416
TraitBidResult,
@@ -27,10 +29,12 @@ import {
2729
CreateBidParams,
2830
CreateCollectionBidParams,
2931
CreateListingParams,
32+
CreateMetadataBidParams,
3033
CreateTraitBidParams,
3134
ListBidsParams,
3235
ListCollectionBidsParams,
3336
ListListingsParams,
37+
ListMetadataBidsParams,
3438
ListTraitBidsParams,
3539
ListTradesParams,
3640
} from '../types';
@@ -127,6 +131,22 @@ export class ImmutableApiClient {
127131
});
128132
}
129133

134+
async getMetadataBid(metadataBidId: string): Promise<MetadataBidResult> {
135+
return this.orderbookService.getMetadataBid({
136+
chainName: this.chainName,
137+
metadataBidId,
138+
});
139+
}
140+
141+
async listMetadataBids(
142+
listOrderParams: ListMetadataBidsParams,
143+
): Promise<ListMetadataBidsResult> {
144+
return this.orderbookService.listMetadataBids({
145+
chainName: this.chainName,
146+
...listOrderParams,
147+
});
148+
}
149+
130150
async listTrades(
131151
listTradesParams: ListTradesParams,
132152
): Promise<ListTradeResult> {
@@ -315,6 +335,68 @@ export class ImmutableApiClient {
315335
});
316336
}
317337

338+
async createMetadataBid({
339+
orderHash,
340+
orderComponents,
341+
orderSignature,
342+
makerFees,
343+
metadataId,
344+
}: CreateMetadataBidParams): Promise<MetadataBidResult> {
345+
if (orderComponents.offer.length !== 1) {
346+
throw new Error('Only one item can be listed for a metadata bid');
347+
}
348+
349+
if (orderComponents.consideration.length !== 1) {
350+
throw new Error('Only one item can be used as currency for a metadata bid');
351+
}
352+
353+
if (ItemType.ERC20 !== orderComponents.offer[0].itemType) {
354+
throw new Error('Only ERC20 tokens can be used as the currency item in a metadata bid');
355+
}
356+
357+
if (![ItemType.ERC721_WITH_CRITERIA, ItemType.ERC1155_WITH_CRITERIA]
358+
.includes(orderComponents.consideration[0].itemType)
359+
) {
360+
throw new Error('Only ERC721 / ERC1155 collection based tokens can be bid against');
361+
}
362+
363+
if (!metadataId) {
364+
throw new Error('A metadata_id is required for a metadata bid');
365+
}
366+
367+
return this.orderbookService.createMetadataBid({
368+
chainName: this.chainName,
369+
requestBody: {
370+
account_address: orderComponents.offerer,
371+
buy: orderComponents.consideration.map(mapSeaportItemToImmutableAssetCollectionItem),
372+
fees: makerFees.map((f) => ({
373+
type: Fee.type.MAKER_ECOSYSTEM,
374+
amount: f.amount,
375+
recipient_address: f.recipientAddress,
376+
})),
377+
end_at: new Date(
378+
parseInt(`${orderComponents.endTime.toString()}000`, 10),
379+
).toISOString(),
380+
order_hash: orderHash,
381+
protocol_data: {
382+
order_type:
383+
mapSeaportOrderTypeToImmutableProtocolDataOrderType(orderComponents.orderType),
384+
zone_address: orderComponents.zone,
385+
seaport_address: this.seaportAddress,
386+
seaport_version: SEAPORT_CONTRACT_VERSION_V1_5,
387+
counter: orderComponents.counter.toString(),
388+
},
389+
salt: orderComponents.salt,
390+
sell: orderComponents.offer.map(mapSeaportItemToImmutableERC20Item),
391+
signature: orderSignature,
392+
start_at: new Date(
393+
parseInt(`${orderComponents.startTime.toString()}000`, 10),
394+
).toISOString(),
395+
metadata_id: metadataId,
396+
},
397+
});
398+
}
399+
318400
async createTraitBid({
319401
orderHash,
320402
orderComponents,

packages/orderbook/src/openapi/mapper.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
ERC721Item,
99
FeeType,
1010
Listing,
11+
MetadataBid,
1112
NativeItem,
1213
Order,
1314
Page,
@@ -315,6 +316,75 @@ export function mapTraitBidFromOpenApiOrder(order: OpenApiOrder): TraitBid {
315316
};
316317
}
317318

319+
export function mapMetadataBidFromOpenApiOrder(order: OpenApiOrder): MetadataBid {
320+
if (order.type !== OpenApiOrder.type.METADATA_BID) {
321+
throw new Error('Order type must be METADATA_BID');
322+
}
323+
324+
const sellItems: ERC20Item[] = order.sell.map((item) => {
325+
if (item.type === 'ERC20') {
326+
return {
327+
type: 'ERC20',
328+
contractAddress: item.contract_address,
329+
amount: item.amount,
330+
};
331+
}
332+
333+
throw new Error('Metadata bid sell items must be ERC20');
334+
});
335+
336+
const buyItems: (ERC721CollectionItem | ERC1155CollectionItem)[] = order.buy.map((item) => {
337+
if (item.type === 'ERC721_COLLECTION') {
338+
return {
339+
type: 'ERC721_COLLECTION',
340+
contractAddress: item.contract_address,
341+
amount: item.amount,
342+
};
343+
}
344+
345+
if (item.type === 'ERC1155_COLLECTION') {
346+
return {
347+
type: 'ERC1155_COLLECTION',
348+
contractAddress: item.contract_address,
349+
amount: item.amount,
350+
};
351+
}
352+
353+
throw new Error('Metadata bid buy items must either ERC721_COLLECTION or ERC1155_COLLECTION');
354+
});
355+
356+
return {
357+
id: order.id,
358+
type: order.type,
359+
chain: order.chain,
360+
accountAddress: order.account_address,
361+
sell: sellItems,
362+
buy: buyItems,
363+
fees: order.fees.map((fee) => ({
364+
amount: fee.amount,
365+
recipientAddress: fee.recipient_address,
366+
type: fee.type as unknown as FeeType,
367+
})),
368+
status: order.status,
369+
fillStatus: order.fill_status,
370+
startAt: order.start_at,
371+
endAt: order.end_at,
372+
salt: order.salt,
373+
signature: order.signature,
374+
orderHash: order.order_hash,
375+
protocolData: {
376+
orderType: order.protocol_data.order_type,
377+
counter: order.protocol_data.counter,
378+
seaportAddress: order.protocol_data.seaport_address,
379+
seaportVersion: order.protocol_data.seaport_version,
380+
zoneAddress: order.protocol_data.zone_address,
381+
},
382+
createdAt: order.created_at,
383+
updatedAt: order.updated_at,
384+
metadataId: order.metadata_id ?? '',
385+
};
386+
}
387+
318388
export function mapOrderFromOpenApiOrder(order: OpenApiOrder): Order {
319389
switch (order.type) {
320390
case OpenApiOrder.type.LISTING:
@@ -325,6 +395,8 @@ export function mapOrderFromOpenApiOrder(order: OpenApiOrder): Order {
325395
return mapCollectionBidFromOpenApiOrder(order);
326396
case OpenApiOrder.type.TRAIT_BID:
327397
return mapTraitBidFromOpenApiOrder(order);
398+
case OpenApiOrder.type.METADATA_BID:
399+
return mapMetadataBidFromOpenApiOrder(order);
328400
default:
329401
return exhaustiveSwitch(order.type);
330402
}

packages/orderbook/src/openapi/sdk/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export type { CollectionBidResult } from './models/CollectionBidResult';
2222
export type { CreateBidRequestBody } from './models/CreateBidRequestBody';
2323
export type { CreateCollectionBidRequestBody } from './models/CreateCollectionBidRequestBody';
2424
export type { CreateTraitBidRequestBody } from './models/CreateTraitBidRequestBody';
25+
export type { CreateMetadataBidRequestBody } from './models/CreateMetadataBidRequestBody';
2526
export type { CreateListingRequestBody } from './models/CreateListingRequestBody';
2627
export type { ERC1155CollectionItem } from './models/ERC1155CollectionItem';
2728
export type { ERC1155Item } from './models/ERC1155Item';
@@ -41,6 +42,7 @@ export type { Item } from './models/Item';
4142
export type { ListBidsResult } from './models/ListBidsResult';
4243
export type { ListCollectionBidsResult } from './models/ListCollectionBidsResult';
4344
export type { ListTraitBidsResult } from './models/ListTraitBidsResult';
45+
export type { ListMetadataBidsResult } from './models/ListMetadataBidsResult';
4446
export type { ListingResult } from './models/ListingResult';
4547
export type { ListListingsResult } from './models/ListListingsResult';
4648
export type { ListTradeResult } from './models/ListTradeResult';
@@ -57,6 +59,7 @@ export type { Trade } from './models/Trade';
5759
export type { TradeBlockchainMetadata } from './models/TradeBlockchainMetadata';
5860
export type { TradeResult } from './models/TradeResult';
5961
export type { TraitBidResult } from './models/TraitBidResult';
62+
export type { MetadataBidResult } from './models/MetadataBidResult';
6063
export type { TraitFilter } from './models/TraitFilter';
6164
export type { UnfulfillableOrder } from './models/UnfulfillableOrder';
6265

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/* istanbul ignore file */
2+
/* tslint:disable */
3+
/* eslint-disable */
4+
5+
import type { AssetCollectionItem } from './AssetCollectionItem';
6+
import type { ERC20Item } from './ERC20Item';
7+
import type { Fee } from './Fee';
8+
import type { ProtocolData } from './ProtocolData';
9+
10+
export type CreateMetadataBidRequestBody = {
11+
account_address: string;
12+
order_hash: string;
13+
/**
14+
* Buy item for metadata bid should either be ERC721 or ERC1155 collection item
15+
*/
16+
buy: Array<AssetCollectionItem>;
17+
/**
18+
* Buy fees should only include maker marketplace fees and should be no more than two entries as more entires will incur more gas. It is best practice to have this as few as possible.
19+
*/
20+
fees: Array<Fee>;
21+
/**
22+
* Time after which the Order is considered expired
23+
*/
24+
end_at: string;
25+
protocol_data: ProtocolData;
26+
/**
27+
* A random value added to the create Order request
28+
*/
29+
salt: string;
30+
/**
31+
* Sell item for metadata bid should be an ERC20 item
32+
*/
33+
sell: Array<ERC20Item>;
34+
/**
35+
* Digital signature generated by the user for the specific Order
36+
*/
37+
signature: string;
38+
/**
39+
* Time after which Order is considered active
40+
*/
41+
start_at: string;
42+
/**
43+
* The metadata_id (stack_id) that NFTs must have to fulfil this bid
44+
*/
45+
metadata_id: string;
46+
};
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/* istanbul ignore file */
2+
/* tslint:disable */
3+
/* eslint-disable */
4+
5+
import type { Order } from './Order';
6+
import type { Page } from './Page';
7+
8+
export type ListMetadataBidsResult = {
9+
page: Page;
10+
result: Array<Order>;
11+
};
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/* istanbul ignore file */
2+
/* tslint:disable */
3+
/* eslint-disable */
4+
5+
import type { Order } from './Order';
6+
7+
export type MetadataBidResult = {
8+
result: Order;
9+
};

packages/orderbook/src/openapi/sdk/models/Order.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ export type Order = {
5656
* Trait criteria for trait bids when returned by the API
5757
*/
5858
trait_criteria?: Array<TraitFilter>;
59+
/**
60+
* Metadata ID (stack ID) for metadata bids when returned by the API
61+
*/
62+
metadata_id?: string;
5963
};
6064

6165
export namespace Order {
@@ -68,6 +72,7 @@ export namespace Order {
6872
BID = 'BID',
6973
COLLECTION_BID = 'COLLECTION_BID',
7074
TRAIT_BID = 'TRAIT_BID',
75+
METADATA_BID = 'METADATA_BID',
7176
}
7277

7378

0 commit comments

Comments
 (0)