Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: [TD-1677] Add support for bids #2186

Merged
merged 40 commits into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
e47dff9
Update orderbook OpenAPI SDK
lfportal Sep 9, 2024
d4b3687
Add ListingLike helper type
lfportal Sep 9, 2024
21843b8
Refactor Listing type
lfportal Sep 10, 2024
3c8fafa
Merge branch 'main' into TD-1668-update-orderbook-oas-sdk
lfportal Sep 10, 2024
3a2df18
Update with collection bids
lfportal Sep 10, 2024
e5a934a
Update OAS SDK
lfportal Sep 11, 2024
b4c9213
Merge branch 'main' into TD-1668-update-orderbook-oas-sdk
lfportal Sep 11, 2024
3285388
Update orderbook internals to order type agnostic variants
lfportal Sep 11, 2024
a02c4ad
Merge branch 'main' into TD-1668-update-orderbook-oas-sdk
lfportal Sep 11, 2024
00fb039
Move page type down
lfportal Sep 11, 2024
b3ae7b1
Reorg types
lfportal Sep 12, 2024
4cfa467
Grammar
lfportal Sep 12, 2024
db208d6
Merge branch 'main' into TD-1668-update-orderbook-oas-sdk
lfportal Sep 12, 2024
148b4d5
Move listing result
lfportal Sep 12, 2024
e58079f
Documentation links
lfportal Sep 12, 2024
317b471
Type and comment fixes
lfportal Sep 12, 2024
5c1f0e8
Merge branch 'main' into TD-1668-update-orderbook-oas-sdk
lfportal Sep 12, 2024
566ee3a
Order type agnostic mapping for fulfillment
lfportal Sep 12, 2024
7a2d98f
Add an only script to fun test
lfportal Sep 12, 2024
fa99155
Merge branch 'main' into TD-1668-update-orderbook-oas-sdk
lfportal Sep 12, 2024
007cfae
JSDoc updates
lfportal Sep 12, 2024
800fe15
Fix import
lfportal Sep 12, 2024
70e5e21
Renames
lfportal Sep 12, 2024
44facc3
Merge branch 'main' into TD-1668-update-orderbook-oas-sdk
lfportal Sep 16, 2024
67f5d8e
Add functions to manage bids
lfportal Sep 16, 2024
92fd2e4
Merge branch 'main' into TD-1677-add-bids
lfportal Sep 18, 2024
52ddea3
Fix tests
lfportal Sep 18, 2024
88ba0fe
Merge branch 'main' into TD-1677-add-bids
lfportal Sep 18, 2024
30cad11
Merge branch 'main' into TD-1677-add-bids
lfportal Sep 19, 2024
55d9933
Merge branch 'main' into TD-1677-add-bids
lfportal Sep 20, 2024
5385917
Merge branch 'main' into TD-1677-add-bids
lfportal Sep 20, 2024
ed845c7
Fix bid buy and sell mapping
lfportal Sep 22, 2024
f99db36
Merge branch 'main' into TD-1677-add-bids
lfportal Sep 24, 2024
4785b81
misc
lfportal Sep 24, 2024
321c7ad
Fix tests
lfportal Sep 24, 2024
2dc3338
Merge branch 'main' into TD-1677-add-bids
lfportal Sep 24, 2024
f31962c
Redeploy devnet contracts
lfportal Sep 24, 2024
f09110c
Fix test
lfportal Sep 25, 2024
ab46d1d
Merge branch 'main' into TD-1677-add-bids
lfportal Sep 25, 2024
a3ab266
Merge branch 'main' into TD-1677-add-bids
lfportal Sep 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 4 additions & 10 deletions packages/orderbook/src/api-client/api-client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
anything, deepEqual, instance, mock, when,
} from 'ts-mockito';
import type { OrderComponents } from '@opensea/seaport-js/lib/types';
import { OrderType } from '@opensea/seaport-js/lib/constants';
import { ListingResult, OrdersService } from '../openapi/sdk';
import { ItemType } from '../seaport';
import { ImmutableApiClient } from './api-client';
Expand Down Expand Up @@ -172,19 +173,12 @@ describe('ImmutableApiClient', () => {
itemType: ItemType.NATIVE,
endAmount: '1',
startAmount: '1',
identifierOrCriteria: '456',
token: '0x123',
recipient: '0x123',
},
{
itemType: ItemType.NATIVE,
endAmount: '1',
startAmount: '1',
identifierOrCriteria: '456',
token: '0x123',
identifierOrCriteria: '0',
token: '0x',
recipient: '0x123',
},
];
orderComponents.orderType = OrderType.FULL_RESTRICTED;
orderComponents.endTime = new Date().getTime() / 1000;
orderComponents.startTime = new Date().getTime() / 1000;

Expand Down
140 changes: 96 additions & 44 deletions packages/orderbook/src/api-client/api-client.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
import {
BidResult,
CancelOrdersResult,
Fee,
ListBidsResult,
ListingResult,
ListListingsResult,
ListTradeResult,
OrdersService,
ProtocolData,
TradeResult,
CancelOrdersResult,
} from '../openapi/sdk';
import { FulfillableOrder } from '../openapi/sdk/models/FulfillableOrder';
import { FulfillmentDataRequest } from '../openapi/sdk/models/FulfillmentDataRequest';
import { UnfulfillableOrder } from '../openapi/sdk/models/UnfulfillableOrder';
import { ItemType, SEAPORT_CONTRACT_VERSION_V1_5 } from '../seaport';
import { mapSeaportItemToImmutableItem, mapSeaportOrderTypeToImmutableProtocolDataOrderType } from '../seaport/map-to-immutable-order';
import {
CreateBidParams,
CreateListingParams,
FeeType,
ListBidsParams,
ListListingsParams,
ListTradesParams,
} from '../types';
import { FulfillableOrder } from '../openapi/sdk/models/FulfillableOrder';
import { UnfulfillableOrder } from '../openapi/sdk/models/UnfulfillableOrder';
import { FulfillmentDataRequest } from '../openapi/sdk/models/FulfillmentDataRequest';
import { ItemType, SEAPORT_CONTRACT_VERSION_V1_5 } from '../seaport';

export class ImmutableApiClient {
constructor(
Expand Down Expand Up @@ -47,6 +50,13 @@ export class ImmutableApiClient {
});
}

async getBid(bidId: string): Promise<BidResult> {
return this.orderbookService.getBid({
chainName: this.chainName,
bidId,
});
}

async getTrade(tradeId: string): Promise<TradeResult> {
return this.orderbookService.getTrade({
chainName: this.chainName,
Expand All @@ -63,6 +73,15 @@ export class ImmutableApiClient {
});
}

async listBids(
listOrderParams: ListBidsParams,
): Promise<ListBidsResult> {
return this.orderbookService.listBids({
chainName: this.chainName,
...listOrderParams,
});
}

async listTrades(
listTradesParams: ListTradesParams,
): Promise<ListTradeResult> {
Expand Down Expand Up @@ -94,66 +113,99 @@ export class ImmutableApiClient {
makerFees,
}: CreateListingParams): Promise<ListingResult> {
if (orderComponents.offer.length !== 1) {
throw new Error('Only one item can be listed at a time');
throw new Error('Only one item can be listed for a listing');
}

if (Number(orderComponents.offer[0].itemType) !== ItemType.ERC721
&& Number(orderComponents.offer[0].itemType) !== ItemType.ERC1155) {
if (orderComponents.consideration.length !== 1) {
throw new Error('Only one item can be used as currency for a listing');
}

if (![ItemType.ERC721, ItemType.ERC1155].includes(orderComponents.offer[0].itemType)) {
throw new Error('Only ERC721 / ERC1155 tokens can be listed');
}

const orderTypes = [
...orderComponents.consideration.map((c) => c.itemType),
];
const isSameConsiderationType = new Set(orderTypes).size === 1;
if (!isSameConsiderationType) {
throw new Error('All consideration items must be of the same type');
if (![ItemType.NATIVE, ItemType.ERC20].includes(orderComponents.consideration[0].itemType)) {
throw new Error('Only Native / ERC20 tokens can be used as currency items in a listing');
}

return this.orderbookService.createListing({
chainName: this.chainName,
requestBody: {
account_address: orderComponents.offerer,
buy: [
{
type:
Number(orderComponents.consideration[0].itemType)
=== ItemType.NATIVE
? 'NATIVE'
: 'ERC20',
amount: orderComponents.consideration[0].startAmount,
contract_address: orderComponents.consideration[0].token,
},
],
fees: makerFees.map((x) => ({
amount: x.amount,
type: FeeType.MAKER_ECOSYSTEM as unknown as Fee.type,
recipient_address: x.recipientAddress,
buy: orderComponents.consideration.map(mapSeaportItemToImmutableItem),
fees: makerFees.map((f) => ({
type: Fee.type.MAKER_ECOSYSTEM,
amount: f.amount,
recipient_address: f.recipientAddress,
})),
end_at: new Date(
parseInt(`${orderComponents.endTime.toString()}000`, 10),
).toISOString(),
order_hash: orderHash,
protocol_data: {
order_type:
mapSeaportOrderTypeToImmutableProtocolDataOrderType(orderComponents.orderType),
zone_address: orderComponents.zone,
seaport_address: this.seaportAddress,
seaport_version: SEAPORT_CONTRACT_VERSION_V1_5,
counter: orderComponents.counter.toString(),
},
salt: orderComponents.salt,
sell: orderComponents.offer.map(mapSeaportItemToImmutableItem),
signature: orderSignature,
start_at: new Date(
parseInt(`${orderComponents.startTime.toString()}000`, 10),
).toISOString(),
},
});
}

async createBid({
orderHash,
orderComponents,
orderSignature,
makerFees,
}: CreateBidParams): Promise<BidResult> {
if (orderComponents.offer.length !== 1) {
throw new Error('Only one item can be listed for a bid');
}

if (orderComponents.consideration.length !== 1) {
throw new Error('Only one item can be used as currency for a bid');
}

if (ItemType.ERC20 !== orderComponents.offer[0].itemType) {
throw new Error('Only ERC20 tokens can be used as the currency item in a bid');
}

if (![ItemType.ERC721, ItemType.ERC1155].includes(orderComponents.consideration[0].itemType)) {
throw new Error('Only ERC721 / ERC1155 tokens can be bid against');
}

return this.orderbookService.createBid({
chainName: this.chainName,
requestBody: {
account_address: orderComponents.offerer,
buy: orderComponents.consideration.map(mapSeaportItemToImmutableItem),
fees: makerFees.map((f) => ({
type: Fee.type.MAKER_ECOSYSTEM,
amount: f.amount,
recipient_address: f.recipientAddress,
})),
end_at: new Date(
parseInt(`${orderComponents.endTime.toString()}000`, 10),
).toISOString(),
order_hash: orderHash,
protocol_data: {
order_type: Number(orderComponents.offer[0].itemType) === ItemType.ERC1155
? ProtocolData.order_type.PARTIAL_RESTRICTED : ProtocolData.order_type.FULL_RESTRICTED,
order_type:
mapSeaportOrderTypeToImmutableProtocolDataOrderType(orderComponents.orderType),
zone_address: orderComponents.zone,
seaport_address: this.seaportAddress,
seaport_version: SEAPORT_CONTRACT_VERSION_V1_5,
counter: orderComponents.counter.toString(),
},
salt: orderComponents.salt,
sell: Number(orderComponents.offer[0].itemType) === ItemType.ERC1155
? [{
contract_address: orderComponents.offer[0].token,
token_id: orderComponents.offer[0].identifierOrCriteria,
type: 'ERC1155',
amount: orderComponents.offer[0].startAmount,
}] : [{
contract_address: orderComponents.offer[0].token,
token_id: orderComponents.offer[0].identifierOrCriteria,
type: 'ERC721',
}],
sell: orderComponents.offer.map(mapSeaportItemToImmutableItem),
signature: orderSignature,
start_at: new Date(
parseInt(`${orderComponents.startTime.toString()}000`, 10),
Expand Down
8 changes: 4 additions & 4 deletions packages/orderbook/src/openapi/mapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ export function mapBidFromOpenApiOrder(order: OpenApiOrder): Bid {
throw new Error('Order type must be BID');
}

const sellItems: ERC20Item[] = order.buy.map((item) => {
const sellItems: ERC20Item[] = order.sell.map((item) => {
if (item.type === 'ERC20') {
return {
type: 'ERC20',
Expand All @@ -111,7 +111,7 @@ export function mapBidFromOpenApiOrder(order: OpenApiOrder): Bid {
throw new Error('Bid sell items must be ERC20');
});

const buyItems: (ERC721Item | ERC1155Item)[] = order.sell.map((item) => {
const buyItems: (ERC721Item | ERC1155Item)[] = order.buy.map((item) => {
if (item.type === 'ERC721') {
return {
type: 'ERC721',
Expand Down Expand Up @@ -168,7 +168,7 @@ export function mapCollectionBidFromOpenApiOrder(order: OpenApiOrder): Collectio
throw new Error('Order type must be COLLECTION_BID');
}

const sellItems: ERC20Item[] = order.buy.map((item) => {
const sellItems: ERC20Item[] = order.sell.map((item) => {
if (item.type === 'ERC20') {
return {
type: 'ERC20',
Expand All @@ -180,7 +180,7 @@ export function mapCollectionBidFromOpenApiOrder(order: OpenApiOrder): Collectio
throw new Error('Collection bid sell items must be ERC20');
});

const buyItems: (ERC721CollectionItem | ERC1155CollectionItem)[] = order.sell.map((item) => {
const buyItems: (ERC721CollectionItem | ERC1155CollectionItem)[] = order.buy.map((item) => {
if (item.type === 'ERC721_COLLECTION') {
return {
type: 'ERC721_COLLECTION',
Expand Down
Loading
Loading