Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 3 additions & 2 deletions src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
GetBestListingResponse,
GetOffersResponse,
GetListingsResponse,
GetOrderByHashResponse,
CollectionOffer,
CollectionOrderByOption,
CancelOrderResponse,
Expand Down Expand Up @@ -126,14 +127,14 @@ export class OpenSeaAPI {
* @param orderHash The hash of the order to fetch
* @param protocolAddress The address of the seaport contract
* @param chain The chain where the order is located. Defaults to the chain set in the constructor.
* @returns The {@link OrderV2} returned by the API
* @returns The {@link GetOrderByHashResponse} returned by the API (can be Offer or Listing)
* @throws An error if the order is not found
*/
public async getOrderByHash(
orderHash: string,
protocolAddress: string,
chain: Chain = this.chain,
): Promise<OrderV2> {
): Promise<GetOrderByHashResponse> {
return this.ordersAPI.getOrderByHash(orderHash, protocolAddress, chain);
}

Expand Down
13 changes: 9 additions & 4 deletions src/api/orders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ import {
getOrderByHashPath,
getCancelOrderPath,
} from "./apiPaths";
import { GetOrdersResponse, CancelOrderResponse } from "./types";
import {
GetOrdersResponse,
CancelOrderResponse,
GetOrderByHashResponse,
} from "./types";
import {
FulfillmentDataResponse,
OrderAPIOptions,
Expand Down Expand Up @@ -72,16 +76,17 @@ export class OrdersAPI {

/**
* Gets a single order by its order hash.
* Returns the raw API response which can be either an Offer or Listing.
*/
async getOrderByHash(
orderHash: string,
protocolAddress: string,
chain: Chain = this.chain,
): Promise<OrderV2> {
): Promise<GetOrderByHashResponse> {
const response = await this.fetcher.get<{
order: OrdersQueryResponse["orders"][0];
order: GetOrderByHashResponse;
}>(getOrderByHashPath(chain, protocolAddress, orderHash));
return deserializeOrder(response.order);
return response.order;
}

/**
Expand Down
7 changes: 7 additions & 0 deletions src/api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,13 @@ export type GetBestOfferResponse = Offer | CollectionOffer;
*/
export type GetBestListingResponse = Listing;

/**
* Response from OpenSea API for fetching an order by hash.
* Can be either an Offer or a Listing.
* @category API Response Types
*/
export type GetOrderByHashResponse = Offer | Listing;

/**
* Response from OpenSea API for offchain canceling an order.
* @category API Response Types
Expand Down
75 changes: 43 additions & 32 deletions src/sdk/cancellation.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { OrderComponents } from "@opensea/seaport-js/lib/types";
import { Overrides, Signer } from "ethers";
import { Offer, Listing } from "../api/types";
import { OrderV2 } from "../orders/types";
import { DEFAULT_SEAPORT_CONTRACT_ADDRESS } from "../orders/utils";
import { Chain, EventType } from "../types";
Expand Down Expand Up @@ -55,37 +56,44 @@ export class CancellationManager {
// Check account availability after parameter validation
await this.context.requireAccountIsAvailable(accountAddress);

let orderToCancel: OrderV2;
let orderComponents: OrderComponents;
let effectiveProtocolAddress: string;

if (order) {
// Using OrderV2 object directly
requireValidProtocol(order.protocolAddress);
orderToCancel = order;
effectiveProtocolAddress = order.protocolAddress;
orderComponents = order.protocolData.parameters;
this.context.dispatch(EventType.CancelOrder, {
orderV2: order,
accountAddress,
});
} else if (orderHash) {
// Fetch order from API using order hash
requireValidProtocol(protocolAddress);
orderToCancel = await this.context.api.getOrderByHash(
const fetchedOrder = await this.context.api.getOrderByHash(
orderHash,
protocolAddress,
this.context.chain,
);
requireValidProtocol(orderToCancel.protocolAddress);
requireValidProtocol(fetchedOrder.protocol_address);
effectiveProtocolAddress = fetchedOrder.protocol_address;
orderComponents = fetchedOrder.protocol_data.parameters;
this.context.dispatch(EventType.CancelOrder, {
order: fetchedOrder,
accountAddress,
});
} else {
// Should never reach here due to earlier validation
throw new Error("Invalid input");
}

this.context.dispatch(EventType.CancelOrder, {
orderV2: orderToCancel,
accountAddress,
});

// Transact and get the transaction hash
const transactionHash = await this.cancelSeaportOrders({
orders: [orderToCancel.protocolData.parameters],
orders: [orderComponents],
accountAddress,
domain,
protocolAddress: orderToCancel.protocolAddress,
protocolAddress: effectiveProtocolAddress,
});

// Await transaction confirmation
Expand Down Expand Up @@ -156,17 +164,16 @@ export class CancellationManager {

let orderComponents: OrderComponents[];
let effectiveProtocolAddress = protocolAddress;
let firstOrderV2: OrderV2 | undefined;

if (orders) {
// Extract OrderComponents from either OrderV2 objects or use OrderComponents directly
let firstOrderV2: OrderV2 | undefined;
orderComponents = orders.map((order) => {
if ("protocolData" in order) {
// It's an OrderV2 object
const orderV2 = order as OrderV2;
requireValidProtocol(orderV2.protocolAddress);
effectiveProtocolAddress = orderV2.protocolAddress;
// Save the first OrderV2 for event dispatching
if (!firstOrderV2) {
firstOrderV2 = orderV2;
}
Expand All @@ -176,40 +183,44 @@ export class CancellationManager {
return order as OrderComponents;
}
});
// Dispatch event for the first OrderV2 if available
if (firstOrderV2) {
this.context.dispatch(EventType.CancelOrder, {
orderV2: firstOrderV2,
accountAddress,
});
}
} else if (orderHashes) {
// Fetch orders from the API using order hashes
const fetchedOrders: OrderV2[] = [];
for (const orderHash of orderHashes) {
const order = await this.context.api.getOrderByHash(
orderHash,
const fetchedOrders: (Offer | Listing)[] = [];
for (const hash of orderHashes) {
const fetched = await this.context.api.getOrderByHash(
hash,
protocolAddress,
this.context.chain,
);
fetchedOrders.push(order);
fetchedOrders.push(fetched);
}

// Extract OrderComponents from the fetched orders
orderComponents = fetchedOrders.map((order) => {
requireValidProtocol(order.protocolAddress);
effectiveProtocolAddress = order.protocolAddress;
return order.protocolData.parameters;
orderComponents = fetchedOrders.map((fetched) => {
requireValidProtocol(fetched.protocol_address);
effectiveProtocolAddress = fetched.protocol_address;
return fetched.protocol_data.parameters;
});

// Save the first order for event dispatching
firstOrderV2 = fetchedOrders[0];
// Dispatch event for the first fetched order
if (fetchedOrders.length > 0) {
this.context.dispatch(EventType.CancelOrder, {
order: fetchedOrders[0],
accountAddress,
});
}
} else {
// Should never reach here due to earlier validation
throw new Error("Invalid input");
}

// Dispatch event for the first order if available (for backwards compatibility with cancelOrder)
if (firstOrderV2) {
this.context.dispatch(EventType.CancelOrder, {
orderV2: firstOrderV2,
accountAddress,
});
}

// Transact and get the transaction hash
const transactionHash = await this.cancelSeaportOrders({
orders: orderComponents,
Expand Down
5 changes: 5 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { BigNumberish } from "ethers";
import type { Offer, Listing } from "./api/types";
import type { OrderV2 } from "./orders/types";

/**
Expand Down Expand Up @@ -83,6 +84,10 @@ export interface EventData {
* The {@link OrderV2} object.
*/
orderV2?: OrderV2;
/**
* The order as returned by the API ({@link Offer} or {@link Listing}).
*/
order?: Offer | Listing;
/**
* Array of assets for bulk transfer and batch approval operations.
*/
Expand Down
Loading