@bosonprotocol/core-sdk 1.40.1 → 1.40.2-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/accounts/interface.d.ts.map +1 -1
- package/dist/cjs/core-sdk.d.ts +3 -1
- package/dist/cjs/core-sdk.d.ts.map +1 -1
- package/dist/cjs/core-sdk.js +5 -1
- package/dist/cjs/core-sdk.js.map +1 -1
- package/dist/cjs/erc721/handler.d.ts +6 -0
- package/dist/cjs/erc721/handler.d.ts.map +1 -1
- package/dist/cjs/erc721/handler.js +13 -1
- package/dist/cjs/erc721/handler.js.map +1 -1
- package/dist/cjs/exchanges/handler.d.ts +8 -1
- package/dist/cjs/exchanges/handler.d.ts.map +1 -1
- package/dist/cjs/exchanges/handler.js +14 -1
- package/dist/cjs/exchanges/handler.js.map +1 -1
- package/dist/cjs/exchanges/mixin.d.ts.map +1 -1
- package/dist/cjs/exchanges/mixin.js +2 -6
- package/dist/cjs/exchanges/mixin.js.map +1 -1
- package/dist/cjs/marketplaces/mixin.d.ts +6 -0
- package/dist/cjs/marketplaces/mixin.d.ts.map +1 -0
- package/dist/cjs/marketplaces/mixin.js +19 -0
- package/dist/cjs/marketplaces/mixin.js.map +1 -0
- package/dist/cjs/marketplaces/opensea.d.ts +62 -0
- package/dist/cjs/marketplaces/opensea.d.ts.map +1 -0
- package/dist/cjs/marketplaces/opensea.js +230 -0
- package/dist/cjs/marketplaces/opensea.js.map +1 -0
- package/dist/cjs/marketplaces/types.d.ts +67 -0
- package/dist/cjs/marketplaces/types.d.ts.map +1 -0
- package/dist/cjs/marketplaces/types.js +18 -0
- package/dist/cjs/marketplaces/types.js.map +1 -0
- package/dist/cjs/price-discovery/handler.d.ts +11 -0
- package/dist/cjs/price-discovery/handler.d.ts.map +1 -0
- package/dist/cjs/price-discovery/handler.js +57 -0
- package/dist/cjs/price-discovery/handler.js.map +1 -0
- package/dist/cjs/price-discovery/index.d.ts +3 -0
- package/dist/cjs/price-discovery/index.d.ts.map +1 -0
- package/dist/cjs/price-discovery/index.js +29 -0
- package/dist/cjs/price-discovery/index.js.map +1 -0
- package/dist/cjs/price-discovery/interface.d.ts +6 -0
- package/dist/cjs/price-discovery/interface.d.ts.map +1 -0
- package/dist/cjs/price-discovery/interface.js +11 -0
- package/dist/cjs/price-discovery/interface.js.map +1 -0
- package/dist/cjs/price-discovery/mixin.d.ts +13 -0
- package/dist/cjs/price-discovery/mixin.d.ts.map +1 -0
- package/dist/cjs/price-discovery/mixin.js +36 -0
- package/dist/cjs/price-discovery/mixin.js.map +1 -0
- package/dist/cjs/seaport/handler.d.ts +0 -45
- package/dist/cjs/seaport/handler.d.ts.map +1 -1
- package/dist/cjs/seaport/handler.js +0 -27
- package/dist/cjs/seaport/handler.js.map +1 -1
- package/dist/cjs/seaport/index.d.ts +1 -1
- package/dist/cjs/seaport/index.d.ts.map +1 -1
- package/dist/cjs/seaport/index.js +2 -2
- package/dist/cjs/seaport/interface.d.ts +71 -0
- package/dist/cjs/seaport/interface.d.ts.map +1 -0
- package/dist/cjs/seaport/interface.js +42 -0
- package/dist/cjs/seaport/interface.js.map +1 -0
- package/dist/cjs/seaport/mixin.d.ts +4 -0
- package/dist/cjs/seaport/mixin.d.ts.map +1 -0
- package/dist/cjs/seaport/mixin.js +8 -0
- package/dist/cjs/seaport/mixin.js.map +1 -0
- package/dist/cjs/voucher/mixin.d.ts +5 -1
- package/dist/cjs/voucher/mixin.d.ts.map +1 -1
- package/dist/cjs/voucher/mixin.js +14 -2
- package/dist/cjs/voucher/mixin.js.map +1 -1
- package/dist/esm/accounts/interface.d.ts.map +1 -1
- package/dist/esm/core-sdk.d.ts +3 -1
- package/dist/esm/core-sdk.d.ts.map +1 -1
- package/dist/esm/core-sdk.js +5 -1
- package/dist/esm/core-sdk.js.map +1 -1
- package/dist/esm/erc721/handler.d.ts +6 -0
- package/dist/esm/erc721/handler.d.ts.map +1 -1
- package/dist/esm/erc721/handler.js +9 -0
- package/dist/esm/erc721/handler.js.map +1 -1
- package/dist/esm/exchanges/handler.d.ts +8 -1
- package/dist/esm/exchanges/handler.d.ts.map +1 -1
- package/dist/esm/exchanges/handler.js +11 -1
- package/dist/esm/exchanges/handler.js.map +1 -1
- package/dist/esm/exchanges/mixin.d.ts.map +1 -1
- package/dist/esm/exchanges/mixin.js +3 -7
- package/dist/esm/exchanges/mixin.js.map +1 -1
- package/dist/esm/marketplaces/mixin.d.ts +6 -0
- package/dist/esm/marketplaces/mixin.d.ts.map +1 -0
- package/dist/esm/marketplaces/mixin.js +15 -0
- package/dist/esm/marketplaces/mixin.js.map +1 -0
- package/dist/esm/marketplaces/opensea.d.ts +62 -0
- package/dist/esm/marketplaces/opensea.d.ts.map +1 -0
- package/dist/esm/marketplaces/opensea.js +214 -0
- package/dist/esm/marketplaces/opensea.js.map +1 -0
- package/dist/esm/marketplaces/types.d.ts +67 -0
- package/dist/esm/marketplaces/types.d.ts.map +1 -0
- package/dist/esm/marketplaces/types.js +15 -0
- package/dist/esm/marketplaces/types.js.map +1 -0
- package/dist/esm/price-discovery/handler.d.ts +11 -0
- package/dist/esm/price-discovery/handler.d.ts.map +1 -0
- package/dist/esm/price-discovery/handler.js +19 -0
- package/dist/esm/price-discovery/handler.js.map +1 -0
- package/dist/esm/price-discovery/index.d.ts +3 -0
- package/dist/esm/price-discovery/index.d.ts.map +1 -0
- package/dist/esm/price-discovery/index.js +3 -0
- package/dist/esm/price-discovery/index.js.map +1 -0
- package/dist/esm/price-discovery/interface.d.ts +6 -0
- package/dist/esm/price-discovery/interface.d.ts.map +1 -0
- package/dist/esm/price-discovery/interface.js +7 -0
- package/dist/esm/price-discovery/interface.js.map +1 -0
- package/dist/esm/price-discovery/mixin.d.ts +13 -0
- package/dist/esm/price-discovery/mixin.d.ts.map +1 -0
- package/dist/esm/price-discovery/mixin.js +21 -0
- package/dist/esm/price-discovery/mixin.js.map +1 -0
- package/dist/esm/seaport/handler.d.ts +0 -45
- package/dist/esm/seaport/handler.d.ts.map +1 -1
- package/dist/esm/seaport/handler.js +0 -23
- package/dist/esm/seaport/handler.js.map +1 -1
- package/dist/esm/seaport/index.d.ts +1 -1
- package/dist/esm/seaport/index.d.ts.map +1 -1
- package/dist/esm/seaport/index.js +1 -1
- package/dist/esm/seaport/index.js.map +1 -1
- package/dist/esm/seaport/interface.d.ts +71 -0
- package/dist/esm/seaport/interface.d.ts.map +1 -0
- package/dist/esm/seaport/interface.js +37 -0
- package/dist/esm/seaport/interface.js.map +1 -0
- package/dist/esm/seaport/mixin.d.ts +4 -0
- package/dist/esm/seaport/mixin.d.ts.map +1 -0
- package/dist/esm/seaport/mixin.js +4 -0
- package/dist/esm/seaport/mixin.js.map +1 -0
- package/dist/esm/voucher/mixin.d.ts +5 -1
- package/dist/esm/voucher/mixin.d.ts.map +1 -1
- package/dist/esm/voucher/mixin.js +12 -2
- package/dist/esm/voucher/mixin.js.map +1 -1
- package/package.json +4 -3
- package/src/accounts/interface.ts +1 -1
- package/src/core-sdk.ts +8 -2
- package/src/erc721/handler.ts +15 -0
- package/src/exchanges/handler.ts +19 -2
- package/src/exchanges/mixin.ts +5 -6
- package/src/marketplaces/mixin.ts +24 -0
- package/src/marketplaces/opensea.ts +361 -0
- package/src/marketplaces/types.ts +73 -0
- package/src/price-discovery/handler.ts +37 -0
- package/src/price-discovery/index.ts +2 -0
- package/src/price-discovery/interface.ts +18 -0
- package/src/price-discovery/mixin.ts +30 -0
- package/src/seaport/handler.ts +0 -57
- package/src/seaport/index.ts +1 -1
- package/src/seaport/interface.ts +100 -0
- package/src/seaport/mixin.ts +3 -0
- package/src/voucher/mixin.ts +21 -2
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
import { formatUnits } from "@ethersproject/units";
|
|
2
|
+
import {
|
|
3
|
+
AssetWithTokenId,
|
|
4
|
+
FulfillmentDataResponse,
|
|
5
|
+
GetNFTResponse,
|
|
6
|
+
NFT,
|
|
7
|
+
OrderAPIOptions,
|
|
8
|
+
OrderSide,
|
|
9
|
+
OrderV2,
|
|
10
|
+
OrdersQueryOptions,
|
|
11
|
+
ProtocolData
|
|
12
|
+
} from "opensea-js";
|
|
13
|
+
import { BigNumber, BigNumberish } from "@ethersproject/bignumber";
|
|
14
|
+
import {
|
|
15
|
+
Listing,
|
|
16
|
+
Marketplace,
|
|
17
|
+
MarketplaceType,
|
|
18
|
+
Order,
|
|
19
|
+
SignedOrder
|
|
20
|
+
} from "./types";
|
|
21
|
+
import {
|
|
22
|
+
ConsiderationItem,
|
|
23
|
+
CreateInputItem,
|
|
24
|
+
CreateOrderAction,
|
|
25
|
+
CreateOrderInput,
|
|
26
|
+
OfferItem,
|
|
27
|
+
OrderParameters,
|
|
28
|
+
OrderUseCase
|
|
29
|
+
} from "@opensea/seaport-js/lib/types";
|
|
30
|
+
import {
|
|
31
|
+
ItemType,
|
|
32
|
+
NO_CONDUIT,
|
|
33
|
+
OPENSEA_CONDUIT_ADDRESS
|
|
34
|
+
} from "@opensea/seaport-js/lib/constants";
|
|
35
|
+
import {
|
|
36
|
+
ContractAddresses,
|
|
37
|
+
PriceDiscoveryStruct,
|
|
38
|
+
Side
|
|
39
|
+
} from "@bosonprotocol/common";
|
|
40
|
+
import {
|
|
41
|
+
CriteriaResolver,
|
|
42
|
+
encodeMatchAdvancedOrders,
|
|
43
|
+
AdvancedOrder,
|
|
44
|
+
Fulfillment
|
|
45
|
+
} from "../seaport/interface";
|
|
46
|
+
|
|
47
|
+
export type OpenSeaListing = {
|
|
48
|
+
asset: AssetWithTokenId;
|
|
49
|
+
accountAddress: string;
|
|
50
|
+
startAmount: BigNumberish;
|
|
51
|
+
endAmount?: BigNumberish;
|
|
52
|
+
quantity?: BigNumberish;
|
|
53
|
+
domain?: string;
|
|
54
|
+
salt?: BigNumberish;
|
|
55
|
+
listingTime?: number;
|
|
56
|
+
expirationTime?: number;
|
|
57
|
+
paymentTokenAddress?: string;
|
|
58
|
+
buyerAddress?: string;
|
|
59
|
+
englishAuction?: boolean;
|
|
60
|
+
excludeOptionalCreatorFees?: boolean;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export type OpenSeaSDKHandler = {
|
|
64
|
+
api: {
|
|
65
|
+
apiBaseUrl: string;
|
|
66
|
+
getOrder(order: Omit<OrdersQueryOptions, "limit">): Promise<OrderV2>;
|
|
67
|
+
generateFulfillmentData(
|
|
68
|
+
fulfillerAddress: string,
|
|
69
|
+
orderHash: string,
|
|
70
|
+
protocolAddress: string,
|
|
71
|
+
side: OrderSide
|
|
72
|
+
): Promise<FulfillmentDataResponse>;
|
|
73
|
+
getNFT(address: string, identifier: string): Promise<GetNFTResponse>;
|
|
74
|
+
postOrder(
|
|
75
|
+
order: ProtocolData,
|
|
76
|
+
apiOptions: OrderAPIOptions
|
|
77
|
+
): Promise<OrderV2>;
|
|
78
|
+
};
|
|
79
|
+
seaport_v1_6: {
|
|
80
|
+
createOrder(
|
|
81
|
+
input: CreateOrderInput,
|
|
82
|
+
accountAddress?: string,
|
|
83
|
+
exactApproval?: boolean
|
|
84
|
+
): Promise<OrderUseCase<CreateOrderAction>>;
|
|
85
|
+
};
|
|
86
|
+
createListing(listing: OpenSeaListing): Promise<OrderV2>;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export class OpenSeaMarketplace extends Marketplace {
|
|
90
|
+
constructor(
|
|
91
|
+
_type: MarketplaceType,
|
|
92
|
+
protected _handler: OpenSeaSDKHandler,
|
|
93
|
+
protected _contracts: ContractAddresses,
|
|
94
|
+
protected _feeRecipient: string
|
|
95
|
+
) {
|
|
96
|
+
super(_type);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
public async createListing(listing: Listing): Promise<Order> {
|
|
100
|
+
const osListing = this.convertListing(listing);
|
|
101
|
+
const osOrder = await this._handler.createListing(osListing);
|
|
102
|
+
return this.convertOsOrder(osOrder);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
public async createBidOrder(listing: Listing): Promise<Order> {
|
|
106
|
+
const fees = BigNumber.from(listing.price).mul(250).div(10000);
|
|
107
|
+
const domain = undefined;
|
|
108
|
+
const salt = undefined;
|
|
109
|
+
const quantity = undefined;
|
|
110
|
+
const { nft } = await this._handler.api.getNFT(
|
|
111
|
+
listing.asset.contract,
|
|
112
|
+
listing.asset.tokenId
|
|
113
|
+
);
|
|
114
|
+
// getNFTItems(nfts: NFT[], quantities: bigint[]): CreateInputItem[]; is a private method in OpenSea SDK, but need to hack it here
|
|
115
|
+
const considerationAssetItems = this._handler["getNFTItems"](
|
|
116
|
+
[nft],
|
|
117
|
+
// [BigInt(quantity ?? 1)]
|
|
118
|
+
[BigInt(1)]
|
|
119
|
+
);
|
|
120
|
+
const considerationFeeItems = [
|
|
121
|
+
{
|
|
122
|
+
itemType: 1,
|
|
123
|
+
token: listing.exchangeToken.address,
|
|
124
|
+
identifierOrCriteria: "0",
|
|
125
|
+
amount: fees.toString(),
|
|
126
|
+
startAmount: fees.toString(),
|
|
127
|
+
endAmount: fees.toString(),
|
|
128
|
+
recipient: this._feeRecipient
|
|
129
|
+
}
|
|
130
|
+
];
|
|
131
|
+
const orderParams = {
|
|
132
|
+
offer: [
|
|
133
|
+
{
|
|
134
|
+
itemType: 1,
|
|
135
|
+
token: listing.exchangeToken.address,
|
|
136
|
+
amount: listing.price,
|
|
137
|
+
startAmount: listing.price,
|
|
138
|
+
endAmount: listing.price
|
|
139
|
+
}
|
|
140
|
+
],
|
|
141
|
+
consideration: [...considerationAssetItems, ...considerationFeeItems],
|
|
142
|
+
endTime: listing.expirationTime.toString(),
|
|
143
|
+
zone: listing.zone || "0x0000000000000000000000000000000000000000",
|
|
144
|
+
domain,
|
|
145
|
+
// salt: BigInt(salt ?? 0).toString(),
|
|
146
|
+
salt: BigInt(0).toString(),
|
|
147
|
+
restrictedByZone: true,
|
|
148
|
+
allowPartialFills: false
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const { executeAllActions } = await this._handler.seaport_v1_6.createOrder(
|
|
152
|
+
orderParams,
|
|
153
|
+
listing.offerer
|
|
154
|
+
);
|
|
155
|
+
const order = await executeAllActions();
|
|
156
|
+
const protocolAddress = listing.protocolAddress || this._contracts.seaport;
|
|
157
|
+
if (!protocolAddress) {
|
|
158
|
+
throw new Error(
|
|
159
|
+
`Seaport protocol address must be specified in Lsiting or CoreSDK config`
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const osOrder = await this._handler.api.postOrder(order, {
|
|
164
|
+
protocol: "seaport",
|
|
165
|
+
protocolAddress,
|
|
166
|
+
side: OrderSide.BID
|
|
167
|
+
});
|
|
168
|
+
return this.convertOsOrder(osOrder);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
public async generateFulfilmentData(asset: {
|
|
172
|
+
contract: string;
|
|
173
|
+
tokenId: string;
|
|
174
|
+
}): Promise<PriceDiscoveryStruct> {
|
|
175
|
+
// Asumption: we're fulfilling a Bid Order (don't know if it makes sense with an Ask order)
|
|
176
|
+
const osOrder = await this._handler.api.getOrder({
|
|
177
|
+
assetContractAddress: asset.contract,
|
|
178
|
+
tokenId: asset.tokenId,
|
|
179
|
+
side: OrderSide.BID
|
|
180
|
+
});
|
|
181
|
+
const orderInfo = this.extractOrderInfo(osOrder.protocolData.parameters);
|
|
182
|
+
const ffd = await this._handler.api.generateFulfillmentData(
|
|
183
|
+
this._contracts.priceDiscoveryClient, // the address of the PriceDiscoveryClient contract, which will call the fulfilment method
|
|
184
|
+
osOrder.orderHash,
|
|
185
|
+
osOrder.protocolAddress,
|
|
186
|
+
osOrder.side
|
|
187
|
+
);
|
|
188
|
+
const inputData = ffd.fulfillment_data.transaction
|
|
189
|
+
.input_data as unknown as {
|
|
190
|
+
orders: AdvancedOrder[];
|
|
191
|
+
criteriaResolvers: CriteriaResolver[];
|
|
192
|
+
fulfillments: Fulfillment[];
|
|
193
|
+
recipient: string;
|
|
194
|
+
};
|
|
195
|
+
const price = inputData.orders[1].parameters.consideration[0].startAmount; // offer price minus opensea fees
|
|
196
|
+
const side = Side.Bid; // ?
|
|
197
|
+
const priceDiscoveryContract = osOrder.protocolAddress; // seaport contract address
|
|
198
|
+
const conduit =
|
|
199
|
+
osOrder.protocolData.parameters.conduitKey === NO_CONDUIT
|
|
200
|
+
? osOrder.protocolAddress // Seaport is used as conduit
|
|
201
|
+
: OPENSEA_CONDUIT_ADDRESS; // TODO: might not work on mocked networks
|
|
202
|
+
const priceDiscoveryData = encodeMatchAdvancedOrders(
|
|
203
|
+
inputData.orders,
|
|
204
|
+
inputData.criteriaResolvers,
|
|
205
|
+
inputData.fulfillments,
|
|
206
|
+
inputData.recipient
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
return {
|
|
210
|
+
price,
|
|
211
|
+
side,
|
|
212
|
+
priceDiscoveryContract,
|
|
213
|
+
conduit,
|
|
214
|
+
priceDiscoveryData
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
protected convertListing(listing: Listing): OpenSeaListing {
|
|
219
|
+
return {
|
|
220
|
+
asset: {
|
|
221
|
+
tokenAddress: listing.asset.contract,
|
|
222
|
+
tokenId: listing.asset.tokenId
|
|
223
|
+
},
|
|
224
|
+
accountAddress: listing.offerer,
|
|
225
|
+
startAmount: formatUnits(listing.price, listing.exchangeToken.decimals),
|
|
226
|
+
expirationTime: listing.expirationTime,
|
|
227
|
+
paymentTokenAddress: listing.exchangeToken.address,
|
|
228
|
+
englishAuction: listing.auction
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
protected convertOsOrder(osOrder: OrderV2 | undefined): Order | undefined {
|
|
233
|
+
if (!osOrder) {
|
|
234
|
+
return undefined;
|
|
235
|
+
}
|
|
236
|
+
const orderInfo = this.extractOrderInfo(osOrder.protocolData.parameters);
|
|
237
|
+
const side = orderInfo.side === OrderSide.ASK ? Side.Ask : Side.Bid;
|
|
238
|
+
return {
|
|
239
|
+
offerer: orderInfo.offerer,
|
|
240
|
+
side,
|
|
241
|
+
contract: orderInfo.nftContract,
|
|
242
|
+
tokenId: orderInfo.tokenId,
|
|
243
|
+
exchangeToken: {
|
|
244
|
+
address: orderInfo.exchangeToken
|
|
245
|
+
// decimals: ???
|
|
246
|
+
},
|
|
247
|
+
price: orderInfo.price,
|
|
248
|
+
startTime: osOrder.listingTime,
|
|
249
|
+
endTime: osOrder.expirationTime,
|
|
250
|
+
orderHash: osOrder.orderHash,
|
|
251
|
+
protocolAddress: osOrder.protocolAddress,
|
|
252
|
+
maker: osOrder.maker ? { address: osOrder.maker.address } : undefined,
|
|
253
|
+
taker: osOrder.taker ? { address: osOrder.taker.address } : undefined
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
protected extractOrderInfo(
|
|
258
|
+
parameters: OrderParameters,
|
|
259
|
+
side?: OrderSide
|
|
260
|
+
): {
|
|
261
|
+
offerer: string;
|
|
262
|
+
side: OrderSide;
|
|
263
|
+
nftContract: string;
|
|
264
|
+
tokenId: string;
|
|
265
|
+
price: string;
|
|
266
|
+
fees: string;
|
|
267
|
+
sellerProfit: string;
|
|
268
|
+
exchangeToken: string;
|
|
269
|
+
} {
|
|
270
|
+
let price: string;
|
|
271
|
+
let exchangeToken: string;
|
|
272
|
+
let nft: ConsiderationItem | OfferItem;
|
|
273
|
+
const nftBid = parameters.consideration.find(
|
|
274
|
+
(c) => c.itemType === ItemType.ERC721
|
|
275
|
+
);
|
|
276
|
+
const nftAsk = parameters.offer.find((c) => c.itemType === ItemType.ERC721);
|
|
277
|
+
if (side === OrderSide.BID && !nftBid) {
|
|
278
|
+
console.warn(`NFT not found in order consideration`);
|
|
279
|
+
}
|
|
280
|
+
if (side === OrderSide.ASK && !nftAsk) {
|
|
281
|
+
console.warn(`NFT not found in order offer`);
|
|
282
|
+
}
|
|
283
|
+
if (!side) {
|
|
284
|
+
if (nftBid && !nftAsk) {
|
|
285
|
+
side = OrderSide.BID;
|
|
286
|
+
} else if (!nftBid && nftAsk) {
|
|
287
|
+
side = OrderSide.ASK;
|
|
288
|
+
} else if (nftBid && nftAsk) {
|
|
289
|
+
console.warn(
|
|
290
|
+
`NFT found in both consideration and offer. Unable to detect the order side`
|
|
291
|
+
);
|
|
292
|
+
} else {
|
|
293
|
+
console.warn(
|
|
294
|
+
`No NFT found in consideration or offer. Unable to detect the order side`
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
if (side === OrderSide.BID) {
|
|
299
|
+
price = parameters.offer
|
|
300
|
+
.filter((c) => c.itemType === ItemType.ERC20)
|
|
301
|
+
.reduce(
|
|
302
|
+
(total, current) => total.add(current.startAmount),
|
|
303
|
+
BigNumber.from(0)
|
|
304
|
+
)
|
|
305
|
+
.toString();
|
|
306
|
+
exchangeToken = parameters.offer.find(
|
|
307
|
+
(c) => c.itemType === ItemType.ERC20
|
|
308
|
+
)?.token;
|
|
309
|
+
nft = nftBid;
|
|
310
|
+
} else {
|
|
311
|
+
// ASK
|
|
312
|
+
price = parameters.consideration
|
|
313
|
+
.filter((c) => c.itemType === ItemType.ERC20)
|
|
314
|
+
.reduce(
|
|
315
|
+
(total, current) => total.add(current.startAmount),
|
|
316
|
+
BigNumber.from(0)
|
|
317
|
+
)
|
|
318
|
+
.toString();
|
|
319
|
+
exchangeToken = parameters.consideration.find(
|
|
320
|
+
(c) => c.itemType === ItemType.ERC20
|
|
321
|
+
)?.token;
|
|
322
|
+
nft = nftAsk;
|
|
323
|
+
}
|
|
324
|
+
const fees = price
|
|
325
|
+
? BigNumber.from(price).mul(250).div(10000).toString()
|
|
326
|
+
: "0";
|
|
327
|
+
const sellerProfit = price
|
|
328
|
+
? BigNumber.from(price).mul(9750).div(10000).toString()
|
|
329
|
+
: "0";
|
|
330
|
+
return {
|
|
331
|
+
offerer: parameters.offerer,
|
|
332
|
+
side,
|
|
333
|
+
nftContract: nft?.token || "0x0",
|
|
334
|
+
tokenId: nft?.identifierOrCriteria || "0x0",
|
|
335
|
+
price,
|
|
336
|
+
fees,
|
|
337
|
+
sellerProfit,
|
|
338
|
+
exchangeToken
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
public async getOrder(
|
|
343
|
+
asset: {
|
|
344
|
+
contract: string;
|
|
345
|
+
tokenId: string;
|
|
346
|
+
},
|
|
347
|
+
side: Side
|
|
348
|
+
): Promise<SignedOrder> {
|
|
349
|
+
const osOrder = await this._handler.api.getOrder({
|
|
350
|
+
assetContractAddress: asset.contract,
|
|
351
|
+
tokenId: asset.tokenId,
|
|
352
|
+
side: side === Side.Ask ? OrderSide.ASK : OrderSide.BID
|
|
353
|
+
});
|
|
354
|
+
return osOrder
|
|
355
|
+
? {
|
|
356
|
+
...this.convertOsOrder(osOrder),
|
|
357
|
+
signature: osOrder.protocolData?.signature
|
|
358
|
+
}
|
|
359
|
+
: undefined;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { PriceDiscoveryStruct, Side } from "@bosonprotocol/common";
|
|
2
|
+
import { OpenSeaSDKHandler } from "./opensea";
|
|
3
|
+
|
|
4
|
+
export type DefaultHandler = {
|
|
5
|
+
// Just here as an example for other type of merketplace in the future
|
|
6
|
+
dummy(): () => void;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export enum MarketplaceType {
|
|
10
|
+
DEFAULT,
|
|
11
|
+
OPENSEA
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export enum OrderSide {}
|
|
15
|
+
|
|
16
|
+
export type MarketplaceHandler = OpenSeaSDKHandler | DefaultHandler;
|
|
17
|
+
|
|
18
|
+
export type Listing = {
|
|
19
|
+
asset: {
|
|
20
|
+
contract: string;
|
|
21
|
+
tokenId: string;
|
|
22
|
+
};
|
|
23
|
+
offerer: string;
|
|
24
|
+
price: string;
|
|
25
|
+
expirationTime: number;
|
|
26
|
+
exchangeToken: { address: string; decimals: number };
|
|
27
|
+
auction: boolean;
|
|
28
|
+
zone?: string;
|
|
29
|
+
protocolAddress?: string;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export type Order = {
|
|
33
|
+
offerer: string;
|
|
34
|
+
startTime: number;
|
|
35
|
+
endTime: number;
|
|
36
|
+
orderHash: string;
|
|
37
|
+
protocolAddress: string;
|
|
38
|
+
side: Side;
|
|
39
|
+
maker: {
|
|
40
|
+
address: string;
|
|
41
|
+
};
|
|
42
|
+
taker: {
|
|
43
|
+
address: string;
|
|
44
|
+
};
|
|
45
|
+
contract: string;
|
|
46
|
+
tokenId: string;
|
|
47
|
+
price: string;
|
|
48
|
+
exchangeToken: {
|
|
49
|
+
address: string;
|
|
50
|
+
decimals?: number;
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export type SignedOrder = Order & {
|
|
55
|
+
signature: string;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export abstract class Marketplace {
|
|
59
|
+
constructor(protected _type: MarketplaceType) {}
|
|
60
|
+
public abstract createListing(listing: Listing): Promise<Order>;
|
|
61
|
+
public abstract createBidOrder(listing: Listing): Promise<Order>;
|
|
62
|
+
public abstract getOrder(
|
|
63
|
+
asset: {
|
|
64
|
+
contract: string;
|
|
65
|
+
tokenId: string;
|
|
66
|
+
},
|
|
67
|
+
side: Side
|
|
68
|
+
): Promise<SignedOrder>;
|
|
69
|
+
public abstract generateFulfilmentData(asset: {
|
|
70
|
+
contract: string;
|
|
71
|
+
tokenId: string;
|
|
72
|
+
}): Promise<PriceDiscoveryStruct>;
|
|
73
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Web3LibAdapter,
|
|
3
|
+
PriceDiscoveryStruct,
|
|
4
|
+
TransactionResponse
|
|
5
|
+
} from "@bosonprotocol/common";
|
|
6
|
+
import { BigNumber, BigNumberish } from "@ethersproject/bignumber";
|
|
7
|
+
import * as exchanges from "../exchanges/handler";
|
|
8
|
+
import { getOfferById } from "../offers/subgraph";
|
|
9
|
+
import { AddressZero } from "@ethersproject/constants";
|
|
10
|
+
import { encodeCommitToPriceDiscoveryOffer } from "./interface";
|
|
11
|
+
|
|
12
|
+
export async function commitToPriceDiscoveryOffer(args: {
|
|
13
|
+
contractAddress: string;
|
|
14
|
+
subgraphUrl: string;
|
|
15
|
+
web3Lib: Web3LibAdapter;
|
|
16
|
+
buyer: string;
|
|
17
|
+
tokenIdOrOfferId: BigNumberish;
|
|
18
|
+
priceDiscovery: PriceDiscoveryStruct;
|
|
19
|
+
}): Promise<TransactionResponse> {
|
|
20
|
+
let { offerId } = exchanges.parseTokenId(args.tokenIdOrOfferId);
|
|
21
|
+
if (offerId.eq(0)) {
|
|
22
|
+
offerId = BigNumber.from(args.tokenIdOrOfferId);
|
|
23
|
+
}
|
|
24
|
+
const offer = await getOfferById(args.subgraphUrl, offerId);
|
|
25
|
+
|
|
26
|
+
// TODO: do we need to check allowance?
|
|
27
|
+
|
|
28
|
+
return args.web3Lib.sendTransaction({
|
|
29
|
+
to: args.contractAddress,
|
|
30
|
+
data: encodeCommitToPriceDiscoveryOffer(
|
|
31
|
+
args.buyer,
|
|
32
|
+
args.tokenIdOrOfferId,
|
|
33
|
+
args.priceDiscovery
|
|
34
|
+
),
|
|
35
|
+
value: offer.exchangeToken.address === AddressZero ? offer.price : "0"
|
|
36
|
+
});
|
|
37
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { abis, PriceDiscoveryStruct } from "@bosonprotocol/common";
|
|
2
|
+
import { Interface } from "@ethersproject/abi";
|
|
3
|
+
import { BigNumberish } from "@ethersproject/bignumber";
|
|
4
|
+
|
|
5
|
+
export const priceDiscoveryHandlerIface = new Interface(
|
|
6
|
+
abis.IBosonPriceDiscoveryHandlerABI
|
|
7
|
+
);
|
|
8
|
+
|
|
9
|
+
export function encodeCommitToPriceDiscoveryOffer(
|
|
10
|
+
buyer: string,
|
|
11
|
+
tokenIdOrOfferId: BigNumberish,
|
|
12
|
+
priceDiscovery: PriceDiscoveryStruct
|
|
13
|
+
) {
|
|
14
|
+
return priceDiscoveryHandlerIface.encodeFunctionData(
|
|
15
|
+
"commitToPriceDiscoveryOffer",
|
|
16
|
+
[buyer, tokenIdOrOfferId, priceDiscovery]
|
|
17
|
+
);
|
|
18
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import {
|
|
2
|
+
TransactionResponse,
|
|
3
|
+
PriceDiscoveryStruct
|
|
4
|
+
} from "@bosonprotocol/common";
|
|
5
|
+
import { BaseCoreSDK } from "./../mixins/base-core-sdk";
|
|
6
|
+
import { commitToPriceDiscoveryOffer } from "./handler";
|
|
7
|
+
import { BigNumberish } from "@ethersproject/bignumber";
|
|
8
|
+
|
|
9
|
+
export class PriceDiscoveryMixin extends BaseCoreSDK {
|
|
10
|
+
/**
|
|
11
|
+
* Commits to a price discovery offer (first step of an exchange)
|
|
12
|
+
* @param groupToCreate - group with the contract condition
|
|
13
|
+
* @param overrides - Optional overrides.
|
|
14
|
+
* @returns Transaction response.
|
|
15
|
+
*/
|
|
16
|
+
public async commitToPriceDiscoveryOffer(
|
|
17
|
+
buyer: string,
|
|
18
|
+
tokenIdOrOfferId: BigNumberish,
|
|
19
|
+
priceDiscovery: PriceDiscoveryStruct
|
|
20
|
+
): Promise<TransactionResponse> {
|
|
21
|
+
return commitToPriceDiscoveryOffer({
|
|
22
|
+
buyer,
|
|
23
|
+
tokenIdOrOfferId,
|
|
24
|
+
priceDiscovery,
|
|
25
|
+
web3Lib: this._web3Lib,
|
|
26
|
+
subgraphUrl: this._subgraphUrl,
|
|
27
|
+
contractAddress: this._protocolDiamond
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
package/src/seaport/handler.ts
CHANGED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { BigNumberish } from "@ethersproject/bignumber";
|
|
2
|
-
import { Interface } from "@ethersproject/abi";
|
|
3
|
-
import { seaportAbi } from "./abi";
|
|
4
|
-
|
|
5
|
-
const seaportIface = new Interface(seaportAbi);
|
|
6
|
-
|
|
7
|
-
export enum eOrderType {
|
|
8
|
-
FULL_OPEN = 0,
|
|
9
|
-
PARTIAL_OPEN = 1,
|
|
10
|
-
FULL_RESTRICTED = 2,
|
|
11
|
-
PARTIAL_RESTRICTED = 3,
|
|
12
|
-
CONTRACT = 4
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export enum eItemType {
|
|
16
|
-
NATIVE = 0,
|
|
17
|
-
ERC20 = 1,
|
|
18
|
-
ERC721 = 2,
|
|
19
|
-
ERC1155 = 3,
|
|
20
|
-
ERC721_WITH_CRITERIA = 4,
|
|
21
|
-
ERC1155_WITH_CRITERIA = 5
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
type OfferItem = {
|
|
25
|
-
itemType: eItemType;
|
|
26
|
-
token: string;
|
|
27
|
-
identifierOrCriteria: BigNumberish;
|
|
28
|
-
startAmount: BigNumberish;
|
|
29
|
-
endAmount: BigNumberish;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
type ConsiderationItem = OfferItem & {
|
|
33
|
-
recipient: string;
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
type OrderParameters = {
|
|
37
|
-
offerer: string;
|
|
38
|
-
zone: string;
|
|
39
|
-
offer: OfferItem[];
|
|
40
|
-
consideration: ConsiderationItem[];
|
|
41
|
-
orderType: eOrderType;
|
|
42
|
-
startTime: BigNumberish;
|
|
43
|
-
endTime: BigNumberish;
|
|
44
|
-
zoneHash: string;
|
|
45
|
-
salt: BigNumberish;
|
|
46
|
-
conduitKey: string;
|
|
47
|
-
totalOriginalConsiderationItems: BigNumberish;
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
export type Order = {
|
|
51
|
-
parameters: OrderParameters;
|
|
52
|
-
signature: string;
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
export function encodeValidate(orders: Order[]) {
|
|
56
|
-
return seaportIface.encodeFunctionData("validate", [orders]);
|
|
57
|
-
}
|
package/src/seaport/index.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export * as
|
|
1
|
+
export * as iface from "./interface";
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { abis } from "@bosonprotocol/common";
|
|
2
|
+
import { Interface } from "@ethersproject/abi";
|
|
3
|
+
import { BigNumberish } from "@ethersproject/bignumber";
|
|
4
|
+
|
|
5
|
+
const seaportIface = new Interface(abis.SeaportABI);
|
|
6
|
+
|
|
7
|
+
export enum eOrderType {
|
|
8
|
+
FULL_OPEN = 0,
|
|
9
|
+
PARTIAL_OPEN = 1,
|
|
10
|
+
FULL_RESTRICTED = 2,
|
|
11
|
+
PARTIAL_RESTRICTED = 3,
|
|
12
|
+
CONTRACT = 4
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export enum eItemType {
|
|
16
|
+
NATIVE = 0,
|
|
17
|
+
ERC20 = 1,
|
|
18
|
+
ERC721 = 2,
|
|
19
|
+
ERC1155 = 3,
|
|
20
|
+
ERC721_WITH_CRITERIA = 4,
|
|
21
|
+
ERC1155_WITH_CRITERIA = 5
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
type OfferItem = {
|
|
25
|
+
itemType: eItemType;
|
|
26
|
+
token: string;
|
|
27
|
+
identifierOrCriteria: BigNumberish;
|
|
28
|
+
startAmount: BigNumberish;
|
|
29
|
+
endAmount: BigNumberish;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
type ConsiderationItem = OfferItem & {
|
|
33
|
+
recipient: string;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
type OrderParameters = {
|
|
37
|
+
offerer: string;
|
|
38
|
+
zone: string;
|
|
39
|
+
offer: OfferItem[];
|
|
40
|
+
consideration: ConsiderationItem[];
|
|
41
|
+
orderType: eOrderType;
|
|
42
|
+
startTime: BigNumberish;
|
|
43
|
+
endTime: BigNumberish;
|
|
44
|
+
zoneHash: string;
|
|
45
|
+
salt: BigNumberish;
|
|
46
|
+
conduitKey: string;
|
|
47
|
+
totalOriginalConsiderationItems: BigNumberish;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export type Order = {
|
|
51
|
+
parameters: OrderParameters;
|
|
52
|
+
signature: string;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export type AdvancedOrder = Order & {
|
|
56
|
+
numerator: BigNumberish;
|
|
57
|
+
denominator: BigNumberish;
|
|
58
|
+
extraData: string;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export enum Side {
|
|
62
|
+
OFFER = 0, // items that can be spent
|
|
63
|
+
CONSIDERATION = 1 // items that must be received
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export type CriteriaResolver = {
|
|
67
|
+
orderIndex: BigNumberish;
|
|
68
|
+
side: number;
|
|
69
|
+
index: BigNumberish;
|
|
70
|
+
identifier: BigNumberish;
|
|
71
|
+
criteriaProof: string[];
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export type Fulfillment = {
|
|
75
|
+
offerComponents: FulfillmentComponent[];
|
|
76
|
+
considerationComponents: FulfillmentComponent[];
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export type FulfillmentComponent = {
|
|
80
|
+
orderIndex: BigNumberish;
|
|
81
|
+
itemIndex: BigNumberish;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export function encodeValidate(orders: Order[]) {
|
|
85
|
+
return seaportIface.encodeFunctionData("validate", [orders]);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function encodeMatchAdvancedOrders(
|
|
89
|
+
advancedOrders: AdvancedOrder[],
|
|
90
|
+
criteriaResolvers: CriteriaResolver[],
|
|
91
|
+
fulfillments: Fulfillment[],
|
|
92
|
+
recipient: string
|
|
93
|
+
) {
|
|
94
|
+
return seaportIface.encodeFunctionData("matchAdvancedOrders", [
|
|
95
|
+
advancedOrders,
|
|
96
|
+
criteriaResolvers,
|
|
97
|
+
fulfillments,
|
|
98
|
+
recipient
|
|
99
|
+
]);
|
|
100
|
+
}
|