@opensea/sdk 9.0.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.
Files changed (363) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +70 -0
  3. package/lib/api/accounts.d.ts +27 -0
  4. package/lib/api/accounts.js +46 -0
  5. package/lib/api/accounts.js.map +1 -0
  6. package/lib/api/api.d.ts +489 -0
  7. package/lib/api/api.js +754 -0
  8. package/lib/api/api.js.map +1 -0
  9. package/lib/api/apiPaths.d.ts +46 -0
  10. package/lib/api/apiPaths.js +176 -0
  11. package/lib/api/apiPaths.js.map +1 -0
  12. package/lib/api/chains.d.ts +13 -0
  13. package/lib/api/chains.js +22 -0
  14. package/lib/api/chains.js.map +1 -0
  15. package/lib/api/collections.d.ts +34 -0
  16. package/lib/api/collections.js +80 -0
  17. package/lib/api/collections.js.map +1 -0
  18. package/lib/api/drops.d.ts +21 -0
  19. package/lib/api/drops.js +39 -0
  20. package/lib/api/drops.js.map +1 -0
  21. package/lib/api/events.d.ts +26 -0
  22. package/lib/api/events.js +43 -0
  23. package/lib/api/events.js.map +1 -0
  24. package/lib/api/fetcher.d.ts +24 -0
  25. package/lib/api/fetcher.js +3 -0
  26. package/lib/api/fetcher.js.map +1 -0
  27. package/lib/api/index.d.ts +2 -0
  28. package/lib/api/index.js +19 -0
  29. package/lib/api/index.js.map +1 -0
  30. package/lib/api/listings.d.ts +44 -0
  31. package/lib/api/listings.js +88 -0
  32. package/lib/api/listings.js.map +1 -0
  33. package/lib/api/nfts.d.ts +39 -0
  34. package/lib/api/nfts.js +79 -0
  35. package/lib/api/nfts.js.map +1 -0
  36. package/lib/api/offers.d.ts +54 -0
  37. package/lib/api/offers.js +124 -0
  38. package/lib/api/offers.js.map +1 -0
  39. package/lib/api/orders.d.ts +50 -0
  40. package/lib/api/orders.js +153 -0
  41. package/lib/api/orders.js.map +1 -0
  42. package/lib/api/search.d.ts +13 -0
  43. package/lib/api/search.js +22 -0
  44. package/lib/api/search.js.map +1 -0
  45. package/lib/api/tokens.d.ts +25 -0
  46. package/lib/api/tokens.js +43 -0
  47. package/lib/api/tokens.js.map +1 -0
  48. package/lib/api/types.d.ts +1113 -0
  49. package/lib/api/types.js +65 -0
  50. package/lib/api/types.js.map +1 -0
  51. package/lib/constants.d.ts +22 -0
  52. package/lib/constants.js +52 -0
  53. package/lib/constants.js.map +1 -0
  54. package/lib/index.d.ts +18 -0
  55. package/lib/index.js +24 -0
  56. package/lib/index.js.map +1 -0
  57. package/lib/orders/privateListings.d.ts +12 -0
  58. package/lib/orders/privateListings.js +130 -0
  59. package/lib/orders/privateListings.js.map +1 -0
  60. package/lib/orders/types.d.ts +160 -0
  61. package/lib/orders/types.js +10 -0
  62. package/lib/orders/types.js.map +1 -0
  63. package/lib/orders/utils.d.ts +89 -0
  64. package/lib/orders/utils.js +177 -0
  65. package/lib/orders/utils.js.map +1 -0
  66. package/lib/sdk/assets.d.ts +104 -0
  67. package/lib/sdk/assets.js +398 -0
  68. package/lib/sdk/assets.js.map +1 -0
  69. package/lib/sdk/cancellation.d.ts +97 -0
  70. package/lib/sdk/cancellation.js +235 -0
  71. package/lib/sdk/cancellation.js.map +1 -0
  72. package/lib/sdk/context.d.ts +28 -0
  73. package/lib/sdk/context.js +3 -0
  74. package/lib/sdk/context.js.map +1 -0
  75. package/lib/sdk/fulfillment.d.ts +153 -0
  76. package/lib/sdk/fulfillment.js +298 -0
  77. package/lib/sdk/fulfillment.js.map +1 -0
  78. package/lib/sdk/orders.d.ts +253 -0
  79. package/lib/sdk/orders.js +679 -0
  80. package/lib/sdk/orders.js.map +1 -0
  81. package/lib/sdk/tokens.d.ts +31 -0
  82. package/lib/sdk/tokens.js +65 -0
  83. package/lib/sdk/tokens.js.map +1 -0
  84. package/lib/sdk.d.ts +560 -0
  85. package/lib/sdk.js +693 -0
  86. package/lib/sdk.js.map +1 -0
  87. package/lib/src/api/accounts.d.ts +18 -0
  88. package/lib/src/api/accounts.js +30 -0
  89. package/lib/src/api/accounts.js.map +1 -0
  90. package/lib/src/api/api.d.ts +429 -0
  91. package/lib/src/api/api.js +652 -0
  92. package/lib/src/api/api.js.map +1 -0
  93. package/lib/src/api/apiPaths.d.ts +37 -0
  94. package/lib/src/api/apiPaths.js +140 -0
  95. package/lib/src/api/apiPaths.js.map +1 -0
  96. package/lib/src/api/collections.d.ts +26 -0
  97. package/lib/src/api/collections.js +57 -0
  98. package/lib/src/api/collections.js.map +1 -0
  99. package/lib/src/api/events.d.ts +26 -0
  100. package/lib/src/api/events.js +42 -0
  101. package/lib/src/api/events.js.map +1 -0
  102. package/lib/src/api/fetcher.d.ts +24 -0
  103. package/lib/src/api/fetcher.js +3 -0
  104. package/lib/src/api/fetcher.js.map +1 -0
  105. package/lib/src/api/index.d.ts +2 -0
  106. package/lib/src/api/index.js +19 -0
  107. package/lib/src/api/index.js.map +1 -0
  108. package/lib/src/api/listings.d.ts +44 -0
  109. package/lib/src/api/listings.js +86 -0
  110. package/lib/src/api/listings.js.map +1 -0
  111. package/lib/src/api/nfts.d.ts +35 -0
  112. package/lib/src/api/nfts.js +66 -0
  113. package/lib/src/api/nfts.js.map +1 -0
  114. package/lib/src/api/offers.d.ts +54 -0
  115. package/lib/src/api/offers.js +122 -0
  116. package/lib/src/api/offers.js.map +1 -0
  117. package/lib/src/api/orders.d.ts +50 -0
  118. package/lib/src/api/orders.js +151 -0
  119. package/lib/src/api/orders.js.map +1 -0
  120. package/lib/src/api/search.d.ts +13 -0
  121. package/lib/src/api/search.js +21 -0
  122. package/lib/src/api/search.js.map +1 -0
  123. package/lib/src/api/tokens.d.ts +25 -0
  124. package/lib/src/api/tokens.js +42 -0
  125. package/lib/src/api/tokens.js.map +1 -0
  126. package/lib/src/api/types.d.ts +840 -0
  127. package/lib/src/api/types.js +65 -0
  128. package/lib/src/api/types.js.map +1 -0
  129. package/lib/src/constants.d.ts +22 -0
  130. package/lib/src/constants.js +52 -0
  131. package/lib/src/constants.js.map +1 -0
  132. package/lib/src/index.d.ts +18 -0
  133. package/lib/src/index.js +24 -0
  134. package/lib/src/index.js.map +1 -0
  135. package/lib/src/orders/privateListings.d.ts +12 -0
  136. package/lib/src/orders/privateListings.js +130 -0
  137. package/lib/src/orders/privateListings.js.map +1 -0
  138. package/lib/src/orders/types.d.ts +160 -0
  139. package/lib/src/orders/types.js +10 -0
  140. package/lib/src/orders/types.js.map +1 -0
  141. package/lib/src/orders/utils.d.ts +89 -0
  142. package/lib/src/orders/utils.js +177 -0
  143. package/lib/src/orders/utils.js.map +1 -0
  144. package/lib/src/sdk/assets.d.ts +104 -0
  145. package/lib/src/sdk/assets.js +397 -0
  146. package/lib/src/sdk/assets.js.map +1 -0
  147. package/lib/src/sdk/cancellation.d.ts +97 -0
  148. package/lib/src/sdk/cancellation.js +234 -0
  149. package/lib/src/sdk/cancellation.js.map +1 -0
  150. package/lib/src/sdk/context.d.ts +28 -0
  151. package/lib/src/sdk/context.js +3 -0
  152. package/lib/src/sdk/context.js.map +1 -0
  153. package/lib/src/sdk/fulfillment.d.ts +153 -0
  154. package/lib/src/sdk/fulfillment.js +296 -0
  155. package/lib/src/sdk/fulfillment.js.map +1 -0
  156. package/lib/src/sdk/orders.d.ts +253 -0
  157. package/lib/src/sdk/orders.js +677 -0
  158. package/lib/src/sdk/orders.js.map +1 -0
  159. package/lib/src/sdk/tokens.d.ts +31 -0
  160. package/lib/src/sdk/tokens.js +64 -0
  161. package/lib/src/sdk/tokens.js.map +1 -0
  162. package/lib/src/sdk.d.ts +560 -0
  163. package/lib/src/sdk.js +675 -0
  164. package/lib/src/sdk.js.map +1 -0
  165. package/lib/src/typechain/contracts/ERC1155.d.ts +236 -0
  166. package/lib/src/typechain/contracts/ERC1155.js +3 -0
  167. package/lib/src/typechain/contracts/ERC1155.js.map +1 -0
  168. package/lib/src/typechain/contracts/ERC20.d.ts +141 -0
  169. package/lib/src/typechain/contracts/ERC20.js +3 -0
  170. package/lib/src/typechain/contracts/ERC20.js.map +1 -0
  171. package/lib/src/typechain/contracts/ERC721.d.ts +213 -0
  172. package/lib/src/typechain/contracts/ERC721.js +3 -0
  173. package/lib/src/typechain/contracts/ERC721.js.map +1 -0
  174. package/lib/src/typechain/contracts/Multicall3.d.ts +57 -0
  175. package/lib/src/typechain/contracts/Multicall3.js +3 -0
  176. package/lib/src/typechain/contracts/Multicall3.js.map +1 -0
  177. package/lib/src/typechain/contracts/TransferHelper.d.ts +55 -0
  178. package/lib/src/typechain/contracts/TransferHelper.js +3 -0
  179. package/lib/src/typechain/contracts/TransferHelper.js.map +1 -0
  180. package/lib/src/typechain/contracts/common.d.ts +50 -0
  181. package/lib/src/typechain/contracts/common.js +3 -0
  182. package/lib/src/typechain/contracts/common.js.map +1 -0
  183. package/lib/src/typechain/contracts/factories/ERC1155__factory.d.ts +250 -0
  184. package/lib/src/typechain/contracts/factories/ERC1155__factory.js +332 -0
  185. package/lib/src/typechain/contracts/factories/ERC1155__factory.js.map +1 -0
  186. package/lib/src/typechain/contracts/factories/ERC20__factory.d.ts +174 -0
  187. package/lib/src/typechain/contracts/factories/ERC20__factory.js +240 -0
  188. package/lib/src/typechain/contracts/factories/ERC20__factory.js.map +1 -0
  189. package/lib/src/typechain/contracts/factories/ERC721__factory.d.ts +268 -0
  190. package/lib/src/typechain/contracts/factories/ERC721__factory.js +351 -0
  191. package/lib/src/typechain/contracts/factories/ERC721__factory.js.map +1 -0
  192. package/lib/src/typechain/contracts/factories/Multicall3__factory.d.ts +43 -0
  193. package/lib/src/typechain/contracts/factories/Multicall3__factory.js +68 -0
  194. package/lib/src/typechain/contracts/factories/Multicall3__factory.js.map +1 -0
  195. package/lib/src/typechain/contracts/factories/TransferHelper__factory.d.ts +46 -0
  196. package/lib/src/typechain/contracts/factories/TransferHelper__factory.js +71 -0
  197. package/lib/src/typechain/contracts/factories/TransferHelper__factory.js.map +1 -0
  198. package/lib/src/typechain/contracts/factories/index.d.ts +5 -0
  199. package/lib/src/typechain/contracts/factories/index.js +17 -0
  200. package/lib/src/typechain/contracts/factories/index.js.map +1 -0
  201. package/lib/src/typechain/contracts/index.d.ts +11 -0
  202. package/lib/src/typechain/contracts/index.js +48 -0
  203. package/lib/src/typechain/contracts/index.js.map +1 -0
  204. package/lib/src/types.d.ts +387 -0
  205. package/lib/src/types.js +127 -0
  206. package/lib/src/types.js.map +1 -0
  207. package/lib/src/utils/chain.d.ts +60 -0
  208. package/lib/src/utils/chain.js +248 -0
  209. package/lib/src/utils/chain.js.map +1 -0
  210. package/lib/src/utils/converters.d.ts +37 -0
  211. package/lib/src/utils/converters.js +137 -0
  212. package/lib/src/utils/converters.js.map +1 -0
  213. package/lib/src/utils/dateHelper.d.ts +38 -0
  214. package/lib/src/utils/dateHelper.js +52 -0
  215. package/lib/src/utils/dateHelper.js.map +1 -0
  216. package/lib/src/utils/fees.d.ts +13 -0
  217. package/lib/src/utils/fees.js +29 -0
  218. package/lib/src/utils/fees.js.map +1 -0
  219. package/lib/src/utils/index.d.ts +2 -0
  220. package/lib/src/utils/index.js +19 -0
  221. package/lib/src/utils/index.js.map +1 -0
  222. package/lib/src/utils/protocol.d.ts +75 -0
  223. package/lib/src/utils/protocol.js +161 -0
  224. package/lib/src/utils/protocol.js.map +1 -0
  225. package/lib/src/utils/rateLimit.d.ts +34 -0
  226. package/lib/src/utils/rateLimit.js +97 -0
  227. package/lib/src/utils/rateLimit.js.map +1 -0
  228. package/lib/src/utils/stringHelper.d.ts +18 -0
  229. package/lib/src/utils/stringHelper.js +24 -0
  230. package/lib/src/utils/stringHelper.js.map +1 -0
  231. package/lib/src/utils/utils.d.ts +18 -0
  232. package/lib/src/utils/utils.js +45 -0
  233. package/lib/src/utils/utils.js.map +1 -0
  234. package/lib/typechain/contracts/ERC1155.d.ts +236 -0
  235. package/lib/typechain/contracts/ERC1155.js +3 -0
  236. package/lib/typechain/contracts/ERC1155.js.map +1 -0
  237. package/lib/typechain/contracts/ERC20.d.ts +141 -0
  238. package/lib/typechain/contracts/ERC20.js +3 -0
  239. package/lib/typechain/contracts/ERC20.js.map +1 -0
  240. package/lib/typechain/contracts/ERC721.d.ts +213 -0
  241. package/lib/typechain/contracts/ERC721.js +3 -0
  242. package/lib/typechain/contracts/ERC721.js.map +1 -0
  243. package/lib/typechain/contracts/Multicall3.d.ts +57 -0
  244. package/lib/typechain/contracts/Multicall3.js +3 -0
  245. package/lib/typechain/contracts/Multicall3.js.map +1 -0
  246. package/lib/typechain/contracts/TransferHelper.d.ts +55 -0
  247. package/lib/typechain/contracts/TransferHelper.js +3 -0
  248. package/lib/typechain/contracts/TransferHelper.js.map +1 -0
  249. package/lib/typechain/contracts/common.d.ts +50 -0
  250. package/lib/typechain/contracts/common.js +3 -0
  251. package/lib/typechain/contracts/common.js.map +1 -0
  252. package/lib/typechain/contracts/factories/ERC1155__factory.d.ts +250 -0
  253. package/lib/typechain/contracts/factories/ERC1155__factory.js +332 -0
  254. package/lib/typechain/contracts/factories/ERC1155__factory.js.map +1 -0
  255. package/lib/typechain/contracts/factories/ERC20__factory.d.ts +174 -0
  256. package/lib/typechain/contracts/factories/ERC20__factory.js +240 -0
  257. package/lib/typechain/contracts/factories/ERC20__factory.js.map +1 -0
  258. package/lib/typechain/contracts/factories/ERC721__factory.d.ts +268 -0
  259. package/lib/typechain/contracts/factories/ERC721__factory.js +351 -0
  260. package/lib/typechain/contracts/factories/ERC721__factory.js.map +1 -0
  261. package/lib/typechain/contracts/factories/Multicall3__factory.d.ts +43 -0
  262. package/lib/typechain/contracts/factories/Multicall3__factory.js +68 -0
  263. package/lib/typechain/contracts/factories/Multicall3__factory.js.map +1 -0
  264. package/lib/typechain/contracts/factories/TransferHelper__factory.d.ts +46 -0
  265. package/lib/typechain/contracts/factories/TransferHelper__factory.js +71 -0
  266. package/lib/typechain/contracts/factories/TransferHelper__factory.js.map +1 -0
  267. package/lib/typechain/contracts/factories/index.d.ts +5 -0
  268. package/lib/typechain/contracts/factories/index.js +17 -0
  269. package/lib/typechain/contracts/factories/index.js.map +1 -0
  270. package/lib/typechain/contracts/index.d.ts +11 -0
  271. package/lib/typechain/contracts/index.js +48 -0
  272. package/lib/typechain/contracts/index.js.map +1 -0
  273. package/lib/types.d.ts +389 -0
  274. package/lib/types.js +129 -0
  275. package/lib/types.js.map +1 -0
  276. package/lib/utils/chain.d.ts +64 -0
  277. package/lib/utils/chain.js +211 -0
  278. package/lib/utils/chain.js.map +1 -0
  279. package/lib/utils/chainIds.generated.d.ts +7 -0
  280. package/lib/utils/chainIds.generated.js +37 -0
  281. package/lib/utils/chainIds.generated.js.map +1 -0
  282. package/lib/utils/converters.d.ts +37 -0
  283. package/lib/utils/converters.js +137 -0
  284. package/lib/utils/converters.js.map +1 -0
  285. package/lib/utils/dateHelper.d.ts +38 -0
  286. package/lib/utils/dateHelper.js +52 -0
  287. package/lib/utils/dateHelper.js.map +1 -0
  288. package/lib/utils/fees.d.ts +13 -0
  289. package/lib/utils/fees.js +29 -0
  290. package/lib/utils/fees.js.map +1 -0
  291. package/lib/utils/index.d.ts +2 -0
  292. package/lib/utils/index.js +19 -0
  293. package/lib/utils/index.js.map +1 -0
  294. package/lib/utils/protocol.d.ts +75 -0
  295. package/lib/utils/protocol.js +161 -0
  296. package/lib/utils/protocol.js.map +1 -0
  297. package/lib/utils/rateLimit.d.ts +34 -0
  298. package/lib/utils/rateLimit.js +97 -0
  299. package/lib/utils/rateLimit.js.map +1 -0
  300. package/lib/utils/stringHelper.d.ts +18 -0
  301. package/lib/utils/stringHelper.js +24 -0
  302. package/lib/utils/stringHelper.js.map +1 -0
  303. package/lib/utils/utils.d.ts +18 -0
  304. package/lib/utils/utils.js +45 -0
  305. package/lib/utils/utils.js.map +1 -0
  306. package/package.json +72 -0
  307. package/src/abi/ERC1155.json +314 -0
  308. package/src/abi/ERC20.json +222 -0
  309. package/src/abi/ERC721.json +333 -0
  310. package/src/abi/Multicall3.json +50 -0
  311. package/src/abi/TransferHelper.json +53 -0
  312. package/src/api/accounts.ts +69 -0
  313. package/src/api/api.ts +1152 -0
  314. package/src/api/apiPaths.ts +208 -0
  315. package/src/api/chains.ts +18 -0
  316. package/src/api/collections.ts +120 -0
  317. package/src/api/drops.ts +49 -0
  318. package/src/api/events.ts +71 -0
  319. package/src/api/fetcher.ts +31 -0
  320. package/src/api/index.ts +2 -0
  321. package/src/api/listings.ts +126 -0
  322. package/src/api/nfts.ts +144 -0
  323. package/src/api/offers.ts +242 -0
  324. package/src/api/orders.ts +283 -0
  325. package/src/api/search.ts +21 -0
  326. package/src/api/tokens.ts +67 -0
  327. package/src/api/types.ts +1210 -0
  328. package/src/constants.ts +90 -0
  329. package/src/index.ts +22 -0
  330. package/src/orders/privateListings.ts +173 -0
  331. package/src/orders/types.ts +191 -0
  332. package/src/orders/utils.ts +253 -0
  333. package/src/sdk/assets.ts +591 -0
  334. package/src/sdk/cancellation.ts +346 -0
  335. package/src/sdk/context.ts +33 -0
  336. package/src/sdk/fulfillment.ts +478 -0
  337. package/src/sdk/orders.ts +1149 -0
  338. package/src/sdk/tokens.ts +95 -0
  339. package/src/sdk.ts +1051 -0
  340. package/src/typechain/contracts/ERC1155.ts +440 -0
  341. package/src/typechain/contracts/ERC20.ts +286 -0
  342. package/src/typechain/contracts/ERC721.ts +412 -0
  343. package/src/typechain/contracts/Multicall3.ts +117 -0
  344. package/src/typechain/contracts/TransferHelper.ts +122 -0
  345. package/src/typechain/contracts/common.ts +131 -0
  346. package/src/typechain/contracts/factories/ERC1155__factory.ts +331 -0
  347. package/src/typechain/contracts/factories/ERC20__factory.ts +239 -0
  348. package/src/typechain/contracts/factories/ERC721__factory.ts +350 -0
  349. package/src/typechain/contracts/factories/Multicall3__factory.ts +67 -0
  350. package/src/typechain/contracts/factories/TransferHelper__factory.ts +76 -0
  351. package/src/typechain/contracts/factories/index.ts +8 -0
  352. package/src/typechain/contracts/index.ts +14 -0
  353. package/src/types.ts +413 -0
  354. package/src/utils/chain.ts +224 -0
  355. package/src/utils/chainIds.generated.ts +34 -0
  356. package/src/utils/converters.ts +145 -0
  357. package/src/utils/dateHelper.ts +48 -0
  358. package/src/utils/fees.ts +31 -0
  359. package/src/utils/index.ts +2 -0
  360. package/src/utils/protocol.ts +185 -0
  361. package/src/utils/rateLimit.ts +147 -0
  362. package/src/utils/stringHelper.ts +25 -0
  363. package/src/utils/utils.ts +36 -0
@@ -0,0 +1,1149 @@
1
+ import type {
2
+ ConsiderationInputItem,
3
+ CreateInputItem,
4
+ OrderComponents,
5
+ } from "@opensea/seaport-js/lib/types"
6
+ import { type BigNumberish, ZeroAddress } from "ethers"
7
+ import type { CollectionOffer, NFT } from "../api/types"
8
+ import { INVERSE_BASIS_POINT } from "../constants"
9
+ import type { OrderV2, ProtocolData } from "../orders/types"
10
+ import {
11
+ type AssetWithTokenId,
12
+ type Fee,
13
+ type OpenSeaCollection,
14
+ OrderSide,
15
+ type TokenStandard,
16
+ } from "../types"
17
+ import { oneMonthFromNowInSeconds } from "../utils/dateHelper"
18
+ import { pluralize } from "../utils/stringHelper"
19
+ import {
20
+ basisPointsForFee,
21
+ getAssetItemType,
22
+ getFeeRecipient,
23
+ getListingPaymentToken,
24
+ getOfferPaymentToken,
25
+ getSignedZone,
26
+ remapSharedStorefrontAddress,
27
+ totalBasisPointsForFees,
28
+ } from "../utils/utils"
29
+ import type { SDKContext } from "./context"
30
+
31
+ /**
32
+ * Result type for bulk operations that may partially succeed.
33
+ * Contains successfully submitted orders and any failures with error information.
34
+ */
35
+ export interface BulkOrderResult {
36
+ /** Successfully submitted orders */
37
+ successful: OrderV2[]
38
+ /** Failed order submissions with error information */
39
+ failed: Array<{
40
+ /** Index of the failed order in the original input array */
41
+ index: number
42
+ /** The signed order that failed to submit (undefined if order creation failed before signing) */
43
+ order?: ProtocolData
44
+ /** The error that occurred during submission */
45
+ error: Error
46
+ }>
47
+ }
48
+
49
+ /**
50
+ * Manager for order building and creation operations.
51
+ * Handles listing creation, offer creation, and collection offers.
52
+ */
53
+ export class OrdersManager {
54
+ constructor(
55
+ private context: SDKContext,
56
+ private getPriceParametersCallback: (
57
+ orderSide: OrderSide,
58
+ tokenAddress: string,
59
+ amount: BigNumberish,
60
+ ) => Promise<{ basePrice: bigint }>,
61
+ ) {}
62
+
63
+ private getAmountWithBasisPointsApplied(
64
+ amount: bigint,
65
+ basisPoints: bigint,
66
+ ): string {
67
+ return ((amount * basisPoints) / INVERSE_BASIS_POINT).toString()
68
+ }
69
+
70
+ private isNotMarketplaceFee(fee: Fee): boolean {
71
+ return (
72
+ fee.recipient.toLowerCase() !==
73
+ getFeeRecipient(this.context.chain).toLowerCase()
74
+ )
75
+ }
76
+
77
+ private getNFTItems(
78
+ nfts: NFT[],
79
+ quantities: bigint[] = [],
80
+ ): CreateInputItem[] {
81
+ return nfts.map((nft, index) => ({
82
+ itemType: getAssetItemType(
83
+ nft.token_standard.toUpperCase() as TokenStandard,
84
+ ),
85
+ token: remapSharedStorefrontAddress(nft.contract),
86
+ identifier: nft.identifier ?? undefined,
87
+ amount: quantities[index]?.toString() ?? "1",
88
+ }))
89
+ }
90
+
91
+ private async getFees({
92
+ collection,
93
+ seller,
94
+ paymentTokenAddress,
95
+ amount,
96
+ includeOptionalCreatorFees = false,
97
+ isPrivateListing = false,
98
+ }: {
99
+ collection: OpenSeaCollection
100
+ seller?: string
101
+ paymentTokenAddress: string
102
+ amount: bigint
103
+ includeOptionalCreatorFees?: boolean
104
+ isPrivateListing?: boolean
105
+ }): Promise<ConsiderationInputItem[]> {
106
+ let collectionFees = includeOptionalCreatorFees
107
+ ? collection.fees
108
+ : collection.fees.filter(fee => fee.required)
109
+ if (isPrivateListing) {
110
+ collectionFees = collectionFees.filter(fee =>
111
+ this.isNotMarketplaceFee(fee),
112
+ )
113
+ }
114
+ const collectionFeesBasisPoints = totalBasisPointsForFees(collectionFees)
115
+ const sellerBasisPoints = INVERSE_BASIS_POINT - collectionFeesBasisPoints
116
+
117
+ const getConsiderationItem = (basisPoints: bigint, recipient?: string) => {
118
+ return {
119
+ token: paymentTokenAddress,
120
+ amount: this.getAmountWithBasisPointsApplied(amount, basisPoints),
121
+ recipient,
122
+ }
123
+ }
124
+
125
+ const considerationItems: ConsiderationInputItem[] = []
126
+
127
+ if (seller) {
128
+ considerationItems.push(getConsiderationItem(sellerBasisPoints, seller))
129
+ }
130
+ if (collectionFeesBasisPoints > 0) {
131
+ for (const fee of collectionFees) {
132
+ considerationItems.push(
133
+ getConsiderationItem(basisPointsForFee(fee), fee.recipient),
134
+ )
135
+ }
136
+ }
137
+ return considerationItems
138
+ }
139
+
140
+ /**
141
+ * Build listing order without submitting to API
142
+ * @param options Listing parameters
143
+ * @returns OrderWithCounter ready for API submission or onchain validation
144
+ */
145
+ private async _buildListingOrder({
146
+ asset,
147
+ accountAddress,
148
+ amount,
149
+ quantity = 1,
150
+ domain,
151
+ salt,
152
+ listingTime,
153
+ expirationTime,
154
+ buyerAddress,
155
+ includeOptionalCreatorFees = false,
156
+ zone = ZeroAddress,
157
+ }: {
158
+ asset: AssetWithTokenId
159
+ accountAddress: string
160
+ amount: BigNumberish
161
+ quantity?: BigNumberish
162
+ domain?: string
163
+ salt?: BigNumberish
164
+ listingTime?: number
165
+ expirationTime?: number
166
+ buyerAddress?: string
167
+ includeOptionalCreatorFees?: boolean
168
+ zone?: string
169
+ }) {
170
+ await this.context.requireAccountIsAvailable(accountAddress)
171
+
172
+ const { nft } = await this.context.api.getNFT(
173
+ asset.tokenAddress,
174
+ asset.tokenId,
175
+ )
176
+ const offerAssetItems = this.getNFTItems([nft], [BigInt(quantity ?? 1)])
177
+
178
+ const collection = await this.context.api.getCollection(nft.collection)
179
+
180
+ const paymentTokenAddress =
181
+ collection.pricingCurrencies?.listingCurrency?.address ??
182
+ getListingPaymentToken(this.context.chain)
183
+
184
+ const { basePrice } = await this.getPriceParametersCallback(
185
+ OrderSide.LISTING,
186
+ paymentTokenAddress,
187
+ amount,
188
+ )
189
+
190
+ const considerationFeeItems = await this.getFees({
191
+ collection,
192
+ seller: accountAddress,
193
+ paymentTokenAddress,
194
+ amount: basePrice,
195
+ includeOptionalCreatorFees,
196
+ isPrivateListing: !!buyerAddress,
197
+ })
198
+
199
+ if (buyerAddress) {
200
+ const { getPrivateListingConsiderations } = await import(
201
+ "../orders/privateListings"
202
+ )
203
+ considerationFeeItems.push(
204
+ ...getPrivateListingConsiderations(offerAssetItems, buyerAddress),
205
+ )
206
+ }
207
+
208
+ if (collection.requiredZone) {
209
+ zone = collection.requiredZone
210
+ }
211
+
212
+ const { executeAllActions } = await this.context.seaport.createOrder(
213
+ {
214
+ offer: offerAssetItems,
215
+ consideration: considerationFeeItems,
216
+ startTime: listingTime?.toString(),
217
+ endTime:
218
+ expirationTime?.toString() ?? oneMonthFromNowInSeconds().toString(),
219
+ zone,
220
+ domain,
221
+ salt: BigInt(salt ?? 0).toString(),
222
+ restrictedByZone: zone !== ZeroAddress,
223
+ allowPartialFills: true,
224
+ },
225
+ accountAddress,
226
+ )
227
+
228
+ return executeAllActions()
229
+ }
230
+
231
+ /**
232
+ * Build listing order components without submitting to API
233
+ * @param options Listing parameters
234
+ * @returns OrderComponents ready for onchain validation
235
+ */
236
+ async buildListingOrderComponents({
237
+ asset,
238
+ accountAddress,
239
+ amount,
240
+ quantity = 1,
241
+ domain,
242
+ salt,
243
+ listingTime,
244
+ expirationTime,
245
+ buyerAddress,
246
+ includeOptionalCreatorFees = false,
247
+ zone = ZeroAddress,
248
+ }: {
249
+ asset: AssetWithTokenId
250
+ accountAddress: string
251
+ amount: BigNumberish
252
+ quantity?: BigNumberish
253
+ domain?: string
254
+ salt?: BigNumberish
255
+ listingTime?: number
256
+ expirationTime?: number
257
+ buyerAddress?: string
258
+ includeOptionalCreatorFees?: boolean
259
+ zone?: string
260
+ }): Promise<OrderComponents> {
261
+ const order = await this._buildListingOrder({
262
+ asset,
263
+ accountAddress,
264
+ amount,
265
+ quantity,
266
+ domain,
267
+ salt,
268
+ listingTime,
269
+ expirationTime,
270
+ buyerAddress,
271
+ includeOptionalCreatorFees,
272
+ zone,
273
+ })
274
+ return order.parameters
275
+ }
276
+
277
+ /**
278
+ * Build offer order without submitting to API
279
+ * @param options Offer parameters
280
+ * @returns OrderWithCounter ready for API submission or onchain validation
281
+ */
282
+ private async _buildOfferOrder({
283
+ asset,
284
+ accountAddress,
285
+ amount,
286
+ quantity = 1,
287
+ domain,
288
+ salt,
289
+ expirationTime,
290
+ zone = getSignedZone(this.context.chain),
291
+ }: {
292
+ asset: AssetWithTokenId
293
+ accountAddress: string
294
+ amount: BigNumberish
295
+ quantity?: BigNumberish
296
+ domain?: string
297
+ salt?: BigNumberish
298
+ expirationTime?: BigNumberish
299
+ zone?: string
300
+ }) {
301
+ await this.context.requireAccountIsAvailable(accountAddress)
302
+
303
+ const { nft } = await this.context.api.getNFT(
304
+ asset.tokenAddress,
305
+ asset.tokenId,
306
+ )
307
+ const considerationAssetItems = this.getNFTItems(
308
+ [nft],
309
+ [BigInt(quantity ?? 1)],
310
+ )
311
+
312
+ const collection = await this.context.api.getCollection(nft.collection)
313
+
314
+ const paymentTokenAddress =
315
+ collection.pricingCurrencies?.offerCurrency?.address ??
316
+ getOfferPaymentToken(this.context.chain)
317
+
318
+ const { basePrice } = await this.getPriceParametersCallback(
319
+ OrderSide.OFFER,
320
+ paymentTokenAddress,
321
+ amount,
322
+ )
323
+
324
+ const considerationFeeItems = await this.getFees({
325
+ collection,
326
+ paymentTokenAddress,
327
+ amount: basePrice,
328
+ })
329
+
330
+ if (collection.requiredZone) {
331
+ zone = collection.requiredZone
332
+ }
333
+
334
+ const { executeAllActions } = await this.context.seaport.createOrder(
335
+ {
336
+ offer: [
337
+ {
338
+ token: paymentTokenAddress,
339
+ amount: basePrice.toString(),
340
+ },
341
+ ],
342
+ consideration: [...considerationAssetItems, ...considerationFeeItems],
343
+ endTime:
344
+ expirationTime !== undefined
345
+ ? BigInt(expirationTime).toString()
346
+ : oneMonthFromNowInSeconds().toString(),
347
+ zone,
348
+ domain,
349
+ salt: BigInt(salt ?? 0).toString(),
350
+ restrictedByZone: zone !== ZeroAddress,
351
+ allowPartialFills: true,
352
+ },
353
+ accountAddress,
354
+ )
355
+
356
+ return executeAllActions()
357
+ }
358
+
359
+ /**
360
+ * Build offer order components without submitting to API
361
+ * @param options Offer parameters
362
+ * @returns OrderComponents ready for onchain validation
363
+ */
364
+ async buildOfferOrderComponents({
365
+ asset,
366
+ accountAddress,
367
+ amount,
368
+ quantity = 1,
369
+ domain,
370
+ salt,
371
+ expirationTime,
372
+ zone = getSignedZone(this.context.chain),
373
+ }: {
374
+ asset: AssetWithTokenId
375
+ accountAddress: string
376
+ amount: BigNumberish
377
+ quantity?: BigNumberish
378
+ domain?: string
379
+ salt?: BigNumberish
380
+ expirationTime?: BigNumberish
381
+ zone?: string
382
+ }): Promise<OrderComponents> {
383
+ const order = await this._buildOfferOrder({
384
+ asset,
385
+ accountAddress,
386
+ amount,
387
+ quantity,
388
+ domain,
389
+ salt,
390
+ expirationTime,
391
+ zone,
392
+ })
393
+ return order.parameters
394
+ }
395
+
396
+ /**
397
+ * Create and submit an offer on an asset.
398
+ * @param options
399
+ * @param options.asset The asset to trade. tokenAddress and tokenId must be defined.
400
+ * @param options.accountAddress Address of the wallet making the offer.
401
+ * @param options.amount Amount in decimal format (e.g., "1.5" for 1.5 ETH, not wei). Automatically converted to base units.
402
+ * @param options.quantity Number of assets to bid for. Defaults to 1.
403
+ * @param options.domain Optional domain for onchain attribution. Hashed and included in salt.
404
+ * @param options.salt Arbitrary salt. Auto-generated if not provided.
405
+ * @param options.expirationTime Expiration time for the order, in UTC seconds
406
+ * @param options.zone Zone for order protection. Defaults to chain's signed zone.
407
+ *
408
+ * @returns The {@link OrderV2} that was created.
409
+ *
410
+ * @throws Error if the asset does not contain a token id.
411
+ * @throws Error if the accountAddress is not available through wallet or provider.
412
+ * @throws Error if the amount is not greater than 0.
413
+ */
414
+ async createOffer({
415
+ asset,
416
+ accountAddress,
417
+ amount,
418
+ quantity = 1,
419
+ domain,
420
+ salt,
421
+ expirationTime,
422
+ zone = getSignedZone(this.context.chain),
423
+ }: {
424
+ asset: AssetWithTokenId
425
+ accountAddress: string
426
+ amount: BigNumberish
427
+ quantity?: BigNumberish
428
+ domain?: string
429
+ salt?: BigNumberish
430
+ expirationTime?: BigNumberish
431
+ zone?: string
432
+ }): Promise<OrderV2> {
433
+ const order = await this._buildOfferOrder({
434
+ asset,
435
+ accountAddress,
436
+ amount,
437
+ quantity,
438
+ domain,
439
+ salt,
440
+ expirationTime,
441
+ zone,
442
+ })
443
+
444
+ return this.context.api.postOrder(order, {
445
+ protocol: "seaport",
446
+ protocolAddress: this.context.seaport.contract.target as string,
447
+ side: OrderSide.OFFER,
448
+ })
449
+ }
450
+
451
+ /**
452
+ * Create and submit a listing for an asset.
453
+ * @param options
454
+ * @param options.asset The asset to trade. tokenAddress and tokenId must be defined.
455
+ * @param options.accountAddress Address of the wallet making the listing
456
+ * @param options.amount Amount in decimal format (e.g., "1.5" for 1.5 ETH, not wei). Automatically converted to base units.
457
+ * @param options.quantity Number of assets to list. Defaults to 1.
458
+ * @param options.domain Optional domain for onchain attribution. Hashed and included in salt. This can be used for onchain order attribution to assist with analytics.
459
+ * @param options.salt Arbitrary salt. Auto-generated if not provided.
460
+ * @param options.listingTime Optional time when the order will become fulfillable, in UTC seconds. Undefined means it will start now.
461
+ * @param options.expirationTime Expiration time for the order, in UTC seconds.
462
+ * @param options.buyerAddress Optional address that's allowed to purchase this item. If specified, no other address will be able to take the order, unless its value is the null address.
463
+ * @param options.includeOptionalCreatorFees If true, optional creator fees will be included in the listing. Default: false.
464
+ * @param options.zone Zone for order protection. Defaults to no zone.
465
+ * @returns The {@link OrderV2} that was created.
466
+ *
467
+ * @throws Error if the asset does not contain a token id.
468
+ * @throws Error if the accountAddress is not available through wallet or provider.
469
+ * @throws Error if the amount is not greater than 0.
470
+ */
471
+ async createListing({
472
+ asset,
473
+ accountAddress,
474
+ amount,
475
+ quantity = 1,
476
+ domain,
477
+ salt,
478
+ listingTime,
479
+ expirationTime,
480
+ buyerAddress,
481
+ includeOptionalCreatorFees = false,
482
+ zone = ZeroAddress,
483
+ }: {
484
+ asset: AssetWithTokenId
485
+ accountAddress: string
486
+ amount: BigNumberish
487
+ quantity?: BigNumberish
488
+ domain?: string
489
+ salt?: BigNumberish
490
+ listingTime?: number
491
+ expirationTime?: number
492
+ buyerAddress?: string
493
+ includeOptionalCreatorFees?: boolean
494
+ zone?: string
495
+ }): Promise<OrderV2> {
496
+ const order = await this._buildListingOrder({
497
+ asset,
498
+ accountAddress,
499
+ amount,
500
+ quantity,
501
+ domain,
502
+ salt,
503
+ listingTime,
504
+ expirationTime,
505
+ buyerAddress,
506
+ includeOptionalCreatorFees,
507
+ zone,
508
+ })
509
+
510
+ return this.context.api.postOrder(order, {
511
+ protocol: "seaport",
512
+ protocolAddress: this.context.seaport.contract.target as string,
513
+ side: OrderSide.LISTING,
514
+ })
515
+ }
516
+
517
+ /**
518
+ * Create and submit multiple listings using Seaport's bulk order creation.
519
+ * This method uses a single signature for all listings and submits them individually to the OpenSea API with rate limit handling.
520
+ * All listings must be from the same account address.
521
+ *
522
+ * Note: If only one listing is provided, this method will use a normal order signature instead of a bulk signature,
523
+ * as bulk signatures are more expensive to decode onchain due to the merkle proof verification.
524
+ *
525
+ * @param options
526
+ * @param options.listings Array of listing parameters. Each listing requires asset, amount, and optionally other listing parameters.
527
+ * @param options.accountAddress Address of the wallet making the listings
528
+ * @param options.continueOnError If true, continue submitting remaining listings even if some fail. Default: false (throw on first error).
529
+ * @param options.onProgress Optional callback for progress updates. Called after each listing is submitted (successfully or not).
530
+ * @returns {@link BulkOrderResult} containing successful orders and any failures.
531
+ *
532
+ * @throws Error if listings array is empty
533
+ * @throws Error if the accountAddress is not available through wallet or provider.
534
+ * @throws Error if any asset does not contain a token id.
535
+ * @throws Error if continueOnError is false and any submission fails.
536
+ */
537
+ async createBulkListings({
538
+ listings,
539
+ accountAddress,
540
+ continueOnError = false,
541
+ onProgress,
542
+ }: {
543
+ listings: Array<{
544
+ asset: AssetWithTokenId
545
+ amount: BigNumberish
546
+ quantity?: BigNumberish
547
+ domain?: string
548
+ salt?: BigNumberish
549
+ listingTime?: number
550
+ expirationTime?: number
551
+ buyerAddress?: string
552
+ includeOptionalCreatorFees?: boolean
553
+ zone?: string
554
+ }>
555
+ accountAddress: string
556
+ continueOnError?: boolean
557
+ onProgress?: (completed: number, total: number) => void
558
+ }): Promise<BulkOrderResult> {
559
+ if (listings.length === 0) {
560
+ throw new Error("Listings array cannot be empty")
561
+ }
562
+
563
+ // If only one listing, use normal signature to avoid bulk signature overhead
564
+ if (listings.length === 1) {
565
+ try {
566
+ const order = await this.createListing({
567
+ ...listings[0],
568
+ accountAddress,
569
+ })
570
+ return {
571
+ successful: [order],
572
+ failed: [],
573
+ }
574
+ } catch (error) {
575
+ if (continueOnError) {
576
+ return {
577
+ successful: [],
578
+ failed: [
579
+ {
580
+ index: 0,
581
+ order: {} as ProtocolData, // Order wasn't created
582
+ error: error as Error,
583
+ },
584
+ ],
585
+ }
586
+ }
587
+ throw error
588
+ }
589
+ }
590
+
591
+ await this.context.requireAccountIsAvailable(accountAddress)
592
+
593
+ // Build metadata array for each listing
594
+ const listingMetadata: Array<{
595
+ nft: NFT
596
+ collection: OpenSeaCollection
597
+ paymentTokenAddress: string
598
+ zone: string
599
+ domain?: string
600
+ salt?: BigNumberish
601
+ listingTime?: number
602
+ expirationTime?: number
603
+ }> = []
604
+
605
+ // Build all order inputs
606
+ for (const listing of listings) {
607
+ const {
608
+ asset,
609
+ amount,
610
+ quantity = 1,
611
+ domain,
612
+ salt,
613
+ listingTime,
614
+ expirationTime,
615
+ buyerAddress,
616
+ includeOptionalCreatorFees = false,
617
+ zone = ZeroAddress,
618
+ } = listing
619
+
620
+ // Fetch NFT and collection data
621
+ const { nft } = await this.context.api.getNFT(
622
+ asset.tokenAddress,
623
+ asset.tokenId,
624
+ )
625
+ const collection = await this.context.api.getCollection(nft.collection)
626
+
627
+ const paymentTokenAddress =
628
+ collection.pricingCurrencies?.listingCurrency?.address ??
629
+ getListingPaymentToken(this.context.chain)
630
+
631
+ const offerAssetItems = this.getNFTItems([nft], [BigInt(quantity ?? 1)])
632
+
633
+ const { basePrice } = await this.getPriceParametersCallback(
634
+ OrderSide.LISTING,
635
+ paymentTokenAddress,
636
+ amount,
637
+ )
638
+
639
+ const considerationFeeItems = await this.getFees({
640
+ collection,
641
+ seller: accountAddress,
642
+ paymentTokenAddress,
643
+ amount: basePrice,
644
+ includeOptionalCreatorFees,
645
+ isPrivateListing: !!buyerAddress,
646
+ })
647
+
648
+ if (buyerAddress) {
649
+ const { getPrivateListingConsiderations } = await import(
650
+ "../orders/privateListings"
651
+ )
652
+ considerationFeeItems.push(
653
+ ...getPrivateListingConsiderations(offerAssetItems, buyerAddress),
654
+ )
655
+ }
656
+
657
+ let finalZone = zone
658
+ if (collection.requiredZone) {
659
+ finalZone = collection.requiredZone
660
+ }
661
+
662
+ listingMetadata.push({
663
+ nft,
664
+ collection,
665
+ paymentTokenAddress,
666
+ zone: finalZone,
667
+ domain,
668
+ salt,
669
+ listingTime,
670
+ expirationTime,
671
+ })
672
+ }
673
+
674
+ // Create the bulk orders using seaport's createBulkOrders method
675
+ const createOrderInputsForSeaport = listings.map((listing, index) => {
676
+ const {
677
+ amount,
678
+ quantity = 1,
679
+ listingTime,
680
+ expirationTime,
681
+ buyerAddress,
682
+ includeOptionalCreatorFees = false,
683
+ } = listing
684
+
685
+ const metadata = listingMetadata[index]
686
+ const offerAssetItems = this.getNFTItems(
687
+ [metadata.nft],
688
+ [BigInt(quantity ?? 1)],
689
+ )
690
+
691
+ return this.getPriceParametersCallback(
692
+ OrderSide.LISTING,
693
+ metadata.paymentTokenAddress,
694
+ amount,
695
+ ).then(async ({ basePrice }) => {
696
+ const considerationFeeItems = await this.getFees({
697
+ collection: metadata.collection,
698
+ seller: accountAddress,
699
+ paymentTokenAddress: metadata.paymentTokenAddress,
700
+ amount: basePrice,
701
+ includeOptionalCreatorFees,
702
+ isPrivateListing: !!buyerAddress,
703
+ })
704
+
705
+ if (buyerAddress) {
706
+ const { getPrivateListingConsiderations } = await import(
707
+ "../orders/privateListings"
708
+ )
709
+ considerationFeeItems.push(
710
+ ...getPrivateListingConsiderations(offerAssetItems, buyerAddress),
711
+ )
712
+ }
713
+
714
+ return {
715
+ offer: offerAssetItems,
716
+ consideration: considerationFeeItems,
717
+ startTime: listingTime?.toString(),
718
+ endTime:
719
+ expirationTime?.toString() ?? oneMonthFromNowInSeconds().toString(),
720
+ zone: metadata.zone,
721
+ domain: metadata.domain,
722
+ salt: metadata.salt
723
+ ? BigInt(metadata.salt ?? 0).toString()
724
+ : undefined,
725
+ restrictedByZone: metadata.zone !== ZeroAddress,
726
+ allowPartialFills: true,
727
+ }
728
+ })
729
+ })
730
+
731
+ const resolvedInputs = await Promise.all(createOrderInputsForSeaport)
732
+
733
+ const { executeAllActions } = await this.context.seaport.createBulkOrders(
734
+ resolvedInputs,
735
+ accountAddress,
736
+ )
737
+
738
+ const orders = await executeAllActions()
739
+
740
+ // Submit each order individually to the OpenSea API
741
+ // Rate limiting is handled automatically by the API client
742
+ this.context.logger(
743
+ `Starting submission of ${orders.length} bulk-signed ${pluralize(orders.length, "listing")} to OpenSea API...`,
744
+ )
745
+
746
+ const submittedOrders: OrderV2[] = []
747
+ const failedOrders: BulkOrderResult["failed"] = []
748
+
749
+ for (let i = 0; i < orders.length; i++) {
750
+ this.context.logger(`Submitting listing ${i + 1}/${orders.length}...`)
751
+ try {
752
+ const submittedOrder = await this.context.api.postOrder(orders[i], {
753
+ protocol: "seaport",
754
+ protocolAddress: this.context.seaport.contract.target as string,
755
+ side: OrderSide.LISTING,
756
+ })
757
+ submittedOrders.push(submittedOrder)
758
+ this.context.logger(`Completed listing ${i + 1}/${orders.length}`)
759
+ } catch (error) {
760
+ const errorMessage = (error as Error).message
761
+ this.context.logger(
762
+ `Failed listing ${i + 1}/${orders.length}: ${errorMessage}`,
763
+ )
764
+ failedOrders.push({
765
+ index: i,
766
+ order: orders[i],
767
+ error: error as Error,
768
+ })
769
+
770
+ // If not continuing on error, throw immediately
771
+ if (!continueOnError) {
772
+ throw error
773
+ }
774
+ }
775
+
776
+ // Call progress callback after each listing (successful or failed)
777
+ onProgress?.(i + 1, orders.length)
778
+ }
779
+
780
+ if (submittedOrders.length > 0) {
781
+ this.context.logger(
782
+ `Successfully submitted ${submittedOrders.length}/${orders.length} ${pluralize(submittedOrders.length, "listing")}`,
783
+ )
784
+ }
785
+
786
+ if (failedOrders.length > 0) {
787
+ this.context.logger(
788
+ `Failed to submit ${failedOrders.length}/${orders.length} ${pluralize(failedOrders.length, "listing")}`,
789
+ )
790
+ }
791
+
792
+ return {
793
+ successful: submittedOrders,
794
+ failed: failedOrders,
795
+ }
796
+ }
797
+
798
+ /**
799
+ * Create and submit multiple offers using Seaport's bulk order creation.
800
+ * This method uses a single signature for all offers and submits them individually to the OpenSea API with rate limit handling.
801
+ * All offers must be from the same account address.
802
+ *
803
+ * Note: If only one offer is provided, this method will use a normal order signature instead of a bulk signature,
804
+ * as bulk signatures are more expensive to decode onchain due to the merkle proof verification.
805
+ *
806
+ * @param options
807
+ * @param options.offers Array of offer parameters. Each offer requires asset, amount, and optionally other offer parameters.
808
+ * @param options.accountAddress Address of the wallet making the offers
809
+ * @param options.continueOnError If true, continue submitting remaining offers even if some fail. Default: false (throw on first error).
810
+ * @param options.onProgress Optional callback for progress updates. Called after each offer is submitted (successfully or not).
811
+ * @returns {@link BulkOrderResult} containing successful orders and any failures.
812
+ *
813
+ * @throws Error if offers array is empty
814
+ * @throws Error if the accountAddress is not available through wallet or provider.
815
+ * @throws Error if any asset does not contain a token id.
816
+ * @throws Error if continueOnError is false and any submission fails.
817
+ */
818
+ async createBulkOffers({
819
+ offers,
820
+ accountAddress,
821
+ continueOnError = false,
822
+ onProgress,
823
+ }: {
824
+ offers: Array<{
825
+ asset: AssetWithTokenId
826
+ amount: BigNumberish
827
+ quantity?: BigNumberish
828
+ domain?: string
829
+ salt?: BigNumberish
830
+ expirationTime?: BigNumberish
831
+ zone?: string
832
+ }>
833
+ accountAddress: string
834
+ continueOnError?: boolean
835
+ onProgress?: (completed: number, total: number) => void
836
+ }): Promise<BulkOrderResult> {
837
+ if (offers.length === 0) {
838
+ throw new Error("Offers array cannot be empty")
839
+ }
840
+
841
+ // If only one offer, use normal signature to avoid bulk signature overhead
842
+ if (offers.length === 1) {
843
+ try {
844
+ const order = await this.createOffer({
845
+ ...offers[0],
846
+ accountAddress,
847
+ })
848
+ return {
849
+ successful: [order],
850
+ failed: [],
851
+ }
852
+ } catch (error) {
853
+ if (continueOnError) {
854
+ return {
855
+ successful: [],
856
+ failed: [
857
+ {
858
+ index: 0,
859
+ order: {} as ProtocolData, // Order wasn't created
860
+ error: error as Error,
861
+ },
862
+ ],
863
+ }
864
+ }
865
+ throw error
866
+ }
867
+ }
868
+
869
+ await this.context.requireAccountIsAvailable(accountAddress)
870
+
871
+ // Build metadata array for each offer
872
+ const offerMetadata: Array<{
873
+ nft: NFT
874
+ collection: OpenSeaCollection
875
+ paymentTokenAddress: string
876
+ zone: string
877
+ domain?: string
878
+ salt?: BigNumberish
879
+ expirationTime?: BigNumberish
880
+ }> = []
881
+
882
+ // Build all order inputs
883
+ for (const offer of offers) {
884
+ const {
885
+ asset,
886
+ domain,
887
+ salt,
888
+ expirationTime,
889
+ zone = getSignedZone(this.context.chain),
890
+ } = offer
891
+
892
+ // Fetch NFT and collection data
893
+ const { nft } = await this.context.api.getNFT(
894
+ asset.tokenAddress,
895
+ asset.tokenId,
896
+ )
897
+ const collection = await this.context.api.getCollection(nft.collection)
898
+
899
+ const paymentTokenAddress =
900
+ collection.pricingCurrencies?.offerCurrency?.address ??
901
+ getOfferPaymentToken(this.context.chain)
902
+
903
+ let finalZone = zone
904
+ if (collection.requiredZone) {
905
+ finalZone = collection.requiredZone
906
+ }
907
+
908
+ offerMetadata.push({
909
+ nft,
910
+ collection,
911
+ paymentTokenAddress,
912
+ zone: finalZone,
913
+ domain,
914
+ salt,
915
+ expirationTime,
916
+ })
917
+ }
918
+
919
+ // Create the bulk orders using seaport's createBulkOrders method
920
+ const createOrderInputsForSeaport = offers.map((offer, index) => {
921
+ const { amount, quantity = 1 } = offer
922
+
923
+ const metadata = offerMetadata[index]
924
+ const considerationAssetItems = this.getNFTItems(
925
+ [metadata.nft],
926
+ [BigInt(quantity ?? 1)],
927
+ )
928
+
929
+ return this.getPriceParametersCallback(
930
+ OrderSide.OFFER,
931
+ metadata.paymentTokenAddress,
932
+ amount,
933
+ ).then(async ({ basePrice }) => {
934
+ const considerationFeeItems = await this.getFees({
935
+ collection: metadata.collection,
936
+ paymentTokenAddress: metadata.paymentTokenAddress,
937
+ amount: basePrice,
938
+ })
939
+
940
+ return {
941
+ offer: [
942
+ {
943
+ token: metadata.paymentTokenAddress,
944
+ amount: basePrice.toString(),
945
+ },
946
+ ],
947
+ consideration: [...considerationAssetItems, ...considerationFeeItems],
948
+ endTime:
949
+ metadata.expirationTime !== undefined
950
+ ? BigInt(metadata.expirationTime).toString()
951
+ : oneMonthFromNowInSeconds().toString(),
952
+ zone: metadata.zone,
953
+ domain: metadata.domain,
954
+ salt: metadata.salt
955
+ ? BigInt(metadata.salt ?? 0).toString()
956
+ : undefined,
957
+ restrictedByZone: metadata.zone !== ZeroAddress,
958
+ allowPartialFills: true,
959
+ }
960
+ })
961
+ })
962
+
963
+ const resolvedInputs = await Promise.all(createOrderInputsForSeaport)
964
+
965
+ const { executeAllActions } = await this.context.seaport.createBulkOrders(
966
+ resolvedInputs,
967
+ accountAddress,
968
+ )
969
+
970
+ const orders = await executeAllActions()
971
+
972
+ // Submit each order individually to the OpenSea API
973
+ // Rate limiting is handled automatically by the API client
974
+ this.context.logger(
975
+ `Starting submission of ${orders.length} bulk-signed ${pluralize(orders.length, "offer")} to OpenSea API...`,
976
+ )
977
+
978
+ const submittedOrders: OrderV2[] = []
979
+ const failedOrders: BulkOrderResult["failed"] = []
980
+
981
+ for (let i = 0; i < orders.length; i++) {
982
+ this.context.logger(`Submitting offer ${i + 1}/${orders.length}...`)
983
+ try {
984
+ const submittedOrder = await this.context.api.postOrder(orders[i], {
985
+ protocol: "seaport",
986
+ protocolAddress: this.context.seaport.contract.target as string,
987
+ side: OrderSide.OFFER,
988
+ })
989
+ submittedOrders.push(submittedOrder)
990
+ this.context.logger(`Completed offer ${i + 1}/${orders.length}`)
991
+ } catch (error) {
992
+ const errorMessage = (error as Error).message
993
+ this.context.logger(
994
+ `Failed offer ${i + 1}/${orders.length}: ${errorMessage}`,
995
+ )
996
+ failedOrders.push({
997
+ index: i,
998
+ order: orders[i],
999
+ error: error as Error,
1000
+ })
1001
+
1002
+ // If not continuing on error, throw immediately
1003
+ if (!continueOnError) {
1004
+ throw error
1005
+ }
1006
+ }
1007
+
1008
+ // Call progress callback after each offer (successful or failed)
1009
+ onProgress?.(i + 1, orders.length)
1010
+ }
1011
+
1012
+ if (submittedOrders.length > 0) {
1013
+ this.context.logger(
1014
+ `Successfully submitted ${submittedOrders.length}/${orders.length} ${pluralize(submittedOrders.length, "offer")}`,
1015
+ )
1016
+ }
1017
+
1018
+ if (failedOrders.length > 0) {
1019
+ this.context.logger(
1020
+ `Failed to submit ${failedOrders.length}/${orders.length} ${pluralize(failedOrders.length, "offer")}`,
1021
+ )
1022
+ }
1023
+
1024
+ return {
1025
+ successful: submittedOrders,
1026
+ failed: failedOrders,
1027
+ }
1028
+ }
1029
+
1030
+ /**
1031
+ * Create and submit a collection offer.
1032
+ * @param options
1033
+ * @param options.collectionSlug Identifier for the collection.
1034
+ * @param options.accountAddress Address of the wallet making the offer.
1035
+ * @param options.amount Amount in decimal format (e.g., "1.5" for 1.5 ETH, not wei). Automatically converted to base units.
1036
+ * @param options.quantity Number of assets to bid for.
1037
+ * @param options.domain Optional domain for onchain attribution. Hashed and included in salt. This can be used for onchain order attribution to assist with analytics.
1038
+ * @param options.salt Arbitrary salt. Auto-generated if not provided.
1039
+ * @param options.expirationTime Expiration time for the order, in UTC seconds.
1040
+ * @param options.offerProtectionEnabled Use signed zone for protection against disabled items. Default: true.
1041
+ * @param options.traitType If defined, the trait name to create the collection offer for.
1042
+ * @param options.traitValue If defined, the trait value to create the collection offer for.
1043
+ * @param options.traits If defined, an array of traits to create the multi-trait collection offer for.
1044
+ * @param options.numericTraits If defined, an array of numeric trait criteria with min/max ranges.
1045
+ * @returns The {@link CollectionOffer} that was created.
1046
+ */
1047
+ async createCollectionOffer({
1048
+ collectionSlug,
1049
+ accountAddress,
1050
+ amount,
1051
+ quantity,
1052
+ domain,
1053
+ salt,
1054
+ expirationTime,
1055
+ offerProtectionEnabled = true,
1056
+ traitType,
1057
+ traitValue,
1058
+ traits,
1059
+ numericTraits,
1060
+ }: {
1061
+ collectionSlug: string
1062
+ accountAddress: string
1063
+ amount: BigNumberish
1064
+ quantity: number
1065
+ domain?: string
1066
+ salt?: BigNumberish
1067
+ expirationTime?: number | string
1068
+ offerProtectionEnabled?: boolean
1069
+ traitType?: string
1070
+ traitValue?: string
1071
+ traits?: Array<{ type: string; value: string }>
1072
+ numericTraits?: Array<{ type: string; min?: number; max?: number }>
1073
+ }): Promise<CollectionOffer | null> {
1074
+ await this.context.requireAccountIsAvailable(accountAddress)
1075
+
1076
+ const collection = await this.context.api.getCollection(collectionSlug)
1077
+
1078
+ const paymentTokenAddress =
1079
+ collection.pricingCurrencies?.offerCurrency?.address ??
1080
+ getOfferPaymentToken(this.context.chain)
1081
+
1082
+ const buildOfferResult = await this.context.api.buildOffer(
1083
+ accountAddress,
1084
+ quantity,
1085
+ collectionSlug,
1086
+ offerProtectionEnabled,
1087
+ traitType,
1088
+ traitValue,
1089
+ traits,
1090
+ numericTraits,
1091
+ )
1092
+ const item = buildOfferResult.partialParameters.consideration[0]
1093
+ const convertedConsiderationItem = {
1094
+ itemType: item.itemType,
1095
+ token: item.token,
1096
+ identifier: item.identifierOrCriteria,
1097
+ amount: item.startAmount,
1098
+ }
1099
+
1100
+ const { basePrice } = await this.getPriceParametersCallback(
1101
+ OrderSide.OFFER,
1102
+ paymentTokenAddress,
1103
+ amount,
1104
+ )
1105
+ const considerationFeeItems = await this.getFees({
1106
+ collection,
1107
+ paymentTokenAddress,
1108
+ amount: basePrice,
1109
+ })
1110
+
1111
+ const considerationItems = [
1112
+ convertedConsiderationItem,
1113
+ ...considerationFeeItems,
1114
+ ]
1115
+
1116
+ const payload = {
1117
+ offerer: accountAddress,
1118
+ offer: [
1119
+ {
1120
+ token: paymentTokenAddress,
1121
+ amount: basePrice.toString(),
1122
+ },
1123
+ ],
1124
+ consideration: considerationItems,
1125
+ endTime:
1126
+ expirationTime?.toString() ?? oneMonthFromNowInSeconds().toString(),
1127
+ zone: buildOfferResult.partialParameters.zone,
1128
+ domain,
1129
+ salt: BigInt(salt ?? 0).toString(),
1130
+ restrictedByZone: true,
1131
+ allowPartialFills: true,
1132
+ }
1133
+
1134
+ const { executeAllActions } = await this.context.seaport.createOrder(
1135
+ payload,
1136
+ accountAddress,
1137
+ )
1138
+ const order = await executeAllActions()
1139
+
1140
+ return this.context.api.postCollectionOffer(
1141
+ order,
1142
+ collectionSlug,
1143
+ traitType,
1144
+ traitValue,
1145
+ traits,
1146
+ numericTraits,
1147
+ )
1148
+ }
1149
+ }