@medialane/sdk 0.62.0 → 0.64.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.
@@ -1,9 +1,9 @@
1
1
  'use strict';
2
2
 
3
3
  var zod = require('zod');
4
- var starknet = require('starknet');
5
4
  var sha3_js = require('@noble/hashes/sha3.js');
6
5
  var base = require('@scure/base');
6
+ var starknet = require('starknet');
7
7
 
8
8
  // src/config.ts
9
9
 
@@ -37,9 +37,9 @@ var COORDINATES = {
37
37
  creatorCoinFactoryClassHash: "0x51765926b1344c9a20b8cd4b5abe7b7d47375ae97cf6804db3ea5d4b05a9b55",
38
38
  creatorCoinStartBlock: 10474544,
39
39
  ekuboCore: "0x00000005dd3d2f4429af886cd1a3b08289dbcea99a294197e9eb43b0e0325b4b",
40
- ipTicketsFactory: "0x0664c2d6a4da9ee3ff053ceeba7579c01f2fedfd9d2b57b4c07af3734bd4acab",
41
- ipTicketCollectionClassHash: "0x086f59c416e365e2bee4ceff9f1dcb96198f2342d50ba4621f60b831863adb6",
42
- ipTicketsStartBlock: 11404656,
40
+ ipTicketsFactory: "0x03ffef4162fe2c44e17d6be2aad3553cab0ac2274cd0b1bb3fafb12fd66695c1",
41
+ ipTicketCollectionClassHash: "0x036393fb4241ed55bd12d8b328ed877d08100fadf9150a650e2b84cbab95e1d4",
42
+ ipTicketsStartBlock: 11689800,
43
43
  ipClubRegistry: "0x00e189c619b6bb07d78973a149641c59c37eb0716f8584d7520bce12d303eede",
44
44
  ipClubNftClassHash: "0x02bc9b20cca21b04245e9215bf7121f4d7295b195890e449b472b573017fb889",
45
45
  ipClubStartBlock: 11404776,
@@ -126,843 +126,1253 @@ function resolveConfig(raw) {
126
126
  feeConfig: resolveFeeConfig(parsed.feeConfig)
127
127
  };
128
128
  }
129
- var STARKNET_DOMAIN = [
130
- { name: "name", type: "shortstring" },
131
- { name: "version", type: "shortstring" },
132
- { name: "chainId", type: "shortstring" },
133
- { name: "revision", type: "shortstring" }
134
- ];
135
- var OFFER_ITEM = [
136
- { name: "item_type", type: "shortstring" },
137
- { name: "token", type: "ContractAddress" },
138
- { name: "identifier_or_criteria", type: "felt" },
139
- { name: "amount", type: "felt" }
140
- ];
141
- var CONSIDERATION_ITEM = [
142
- { name: "item_type", type: "shortstring" },
143
- { name: "token", type: "ContractAddress" },
144
- { name: "identifier_or_criteria", type: "felt" },
145
- { name: "amount", type: "felt" },
146
- { name: "recipient", type: "ContractAddress" }
147
- ];
148
- var ORDER_PARAMETERS = [
149
- { name: "offerer", type: "ContractAddress" },
150
- { name: "marketplace", type: "ContractAddress" },
151
- { name: "offer", type: "OfferItem" },
152
- { name: "consideration", type: "ConsiderationItem" },
153
- { name: "royalty_max_bps", type: "felt" },
154
- { name: "start_time", type: "felt" },
155
- { name: "end_time", type: "felt" },
156
- { name: "salt", type: "felt" },
157
- { name: "counter", type: "felt" }
158
- ];
159
- var ORDER_CANCELLATION = [
160
- { name: "order_hash", type: "felt" },
161
- { name: "offerer", type: "ContractAddress" }
162
- ];
163
- var DOMAIN_VERSION = {
164
- erc721: "5",
165
- erc1155: "4"
166
- };
167
- function buildDomain(standard, chainId) {
168
- return {
169
- name: "Medialane",
170
- version: DOMAIN_VERSION[standard],
171
- chainId,
172
- revision: starknet.TypedDataRevision.ACTIVE
173
- };
129
+ function normalizeAddress(chain, address) {
130
+ switch (chain) {
131
+ case "STARKNET":
132
+ return normalizeStarknet(address);
133
+ case "ETHEREUM":
134
+ case "BASE":
135
+ return normalizeEvm(address);
136
+ case "SOLANA":
137
+ return normalizeSolana(address);
138
+ case "STELLAR":
139
+ return normalizeStellar(address);
140
+ case "BITCOIN":
141
+ throw new Error("BITCOIN address normalization not implemented");
142
+ }
174
143
  }
175
- function buildOrderTypedData(message, chainId) {
176
- return {
177
- domain: buildDomain("erc721", chainId),
178
- primaryType: "OrderParameters",
179
- types: {
180
- StarknetDomain: STARKNET_DOMAIN,
181
- OrderParameters: ORDER_PARAMETERS,
182
- OfferItem: OFFER_ITEM,
183
- ConsiderationItem: CONSIDERATION_ITEM
184
- },
185
- message
186
- };
144
+ function normalizeStarknet(address) {
145
+ try {
146
+ const hex = BigInt(address).toString(16);
147
+ return "0x" + hex.padStart(64, "0").toLowerCase();
148
+ } catch {
149
+ throw new Error(`Invalid STARKNET address: "${address}"`);
150
+ }
187
151
  }
188
- function build1155OrderTypedData(message, chainId) {
189
- return {
190
- domain: buildDomain("erc1155", chainId),
191
- primaryType: "OrderParameters",
192
- types: {
193
- StarknetDomain: STARKNET_DOMAIN,
194
- OrderParameters: ORDER_PARAMETERS,
195
- OfferItem: OFFER_ITEM,
196
- ConsiderationItem: CONSIDERATION_ITEM
197
- },
198
- message
199
- };
152
+ function normalizeEvm(address) {
153
+ const m = /^0x([0-9a-fA-F]{40})$/.exec(address);
154
+ if (!m) throw new Error(`Invalid ETHEREUM/BASE address: "${address}"`);
155
+ const lower = m[1].toLowerCase();
156
+ const hash5 = sha3_js.keccak_256(new TextEncoder().encode(lower));
157
+ let out = "0x";
158
+ for (let i = 0; i < 40; i++) {
159
+ const nibble = hash5[i >> 1] >> (i % 2 === 0 ? 4 : 0) & 15;
160
+ out += nibble >= 8 ? lower[i].toUpperCase() : lower[i];
161
+ }
162
+ return out;
200
163
  }
201
- function buildCancellationTypedData(message, chainId) {
202
- return {
203
- domain: buildDomain("erc721", chainId),
204
- primaryType: "OrderCancellation",
205
- types: {
206
- StarknetDomain: STARKNET_DOMAIN,
207
- OrderCancellation: ORDER_CANCELLATION
208
- },
209
- message
210
- };
164
+ function normalizeSolana(address) {
165
+ try {
166
+ const bytes = base.base58.decode(address);
167
+ if (bytes.length !== 32) throw new Error("not a 32-byte key");
168
+ return address;
169
+ } catch {
170
+ throw new Error(`Invalid SOLANA address: "${address}"`);
171
+ }
211
172
  }
212
- function build1155CancellationTypedData(message, chainId) {
213
- return {
214
- domain: buildDomain("erc1155", chainId),
215
- primaryType: "OrderCancellation",
216
- types: {
217
- StarknetDomain: STARKNET_DOMAIN,
218
- OrderCancellation: ORDER_CANCELLATION
219
- },
220
- message
221
- };
173
+ var STELLAR_VERSION_BYTES = /* @__PURE__ */ new Set([6 << 3, 2 << 3]);
174
+ function normalizeStellar(address) {
175
+ const upper = address.toUpperCase();
176
+ if (!/^[GC][A-Z2-7]{55}$/.test(upper)) {
177
+ throw new Error(`Invalid STELLAR address: "${address}"`);
178
+ }
179
+ let decoded;
180
+ try {
181
+ decoded = base.base32.decode(upper);
182
+ } catch {
183
+ throw new Error(`Invalid STELLAR address: "${address}"`);
184
+ }
185
+ if (decoded.length !== 35 || !STELLAR_VERSION_BYTES.has(decoded[0])) {
186
+ throw new Error(`Invalid STELLAR address: "${address}"`);
187
+ }
188
+ const payload = decoded.subarray(0, 33);
189
+ const checksum = decoded[33] | decoded[34] << 8;
190
+ if (crc16xmodem(payload) !== checksum) {
191
+ throw new Error(`Invalid STELLAR address: "${address}"`);
192
+ }
193
+ return upper;
222
194
  }
223
- function encodeByteArray(str) {
224
- const bytes = new TextEncoder().encode(str);
225
- const fullChunks = [];
226
- let i = 0;
227
- while (i + 31 <= bytes.length) {
228
- let val = 0n;
229
- for (const b of bytes.slice(i, i + 31)) {
230
- val = val << 8n | BigInt(b);
195
+ function crc16xmodem(bytes) {
196
+ let crc = 0;
197
+ for (const byte of bytes) {
198
+ crc ^= byte << 8;
199
+ for (let i = 0; i < 8; i++) {
200
+ crc = crc & 32768 ? (crc << 1 ^ 4129) & 65535 : crc << 1 & 65535;
231
201
  }
232
- fullChunks.push(starknet.num.toHex(val));
233
- i += 31;
234
202
  }
235
- const remaining = bytes.slice(i);
236
- let pendingVal = 0n;
237
- for (const b of remaining) {
238
- pendingVal = pendingVal << 8n | BigInt(b);
239
- }
240
- return [
241
- fullChunks.length.toString(),
242
- ...fullChunks,
243
- starknet.num.toHex(pendingVal),
244
- remaining.length.toString()
245
- ];
203
+ return crc;
246
204
  }
247
205
 
248
- // src/starknet/abis/ipMarketplace.ts
249
- var IPMarketplaceABI = [
250
- {
251
- "type": "impl",
252
- "name": "Medialane721Impl",
253
- "interface_name": "medialane_marketplace_erc721::core::interface::IMedialane"
254
- },
255
- {
256
- "type": "struct",
257
- "name": "medialane_marketplace_erc721::core::types::OfferItem",
258
- "members": [
259
- {
260
- "name": "item_type",
261
- "type": "core::felt252"
262
- },
263
- {
264
- "name": "token",
265
- "type": "core::starknet::contract_address::ContractAddress"
266
- },
267
- {
268
- "name": "identifier_or_criteria",
269
- "type": "core::felt252"
270
- },
271
- {
272
- "name": "amount",
273
- "type": "core::felt252"
206
+ // src/utils/retry.ts
207
+ var DEFAULT_MAX_ATTEMPTS = 3;
208
+ var DEFAULT_BASE_DELAY_MS = 300;
209
+ var DEFAULT_MAX_DELAY_MS = 5e3;
210
+ function sleep(ms) {
211
+ return new Promise((resolve) => setTimeout(resolve, ms));
212
+ }
213
+ async function withRetry(fn, opts) {
214
+ const maxAttempts = opts?.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
215
+ const baseDelayMs = opts?.baseDelayMs ?? DEFAULT_BASE_DELAY_MS;
216
+ const maxDelayMs = opts?.maxDelayMs ?? DEFAULT_MAX_DELAY_MS;
217
+ let lastError;
218
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
219
+ try {
220
+ return await fn();
221
+ } catch (err) {
222
+ lastError = err;
223
+ if (err instanceof MedialaneApiError && err.status < 500) {
224
+ throw err;
274
225
  }
275
- ]
276
- },
277
- {
278
- "type": "struct",
279
- "name": "medialane_marketplace_erc721::core::types::ConsiderationItem",
280
- "members": [
281
- {
282
- "name": "item_type",
283
- "type": "core::felt252"
284
- },
285
- {
286
- "name": "token",
287
- "type": "core::starknet::contract_address::ContractAddress"
288
- },
289
- {
290
- "name": "identifier_or_criteria",
291
- "type": "core::felt252"
292
- },
293
- {
294
- "name": "amount",
295
- "type": "core::felt252"
296
- },
297
- {
298
- "name": "recipient",
299
- "type": "core::starknet::contract_address::ContractAddress"
226
+ const isRetryable = err instanceof MedialaneApiError && err.status >= 500 || err instanceof TypeError;
227
+ if (!isRetryable || attempt === maxAttempts - 1) {
228
+ throw err;
300
229
  }
301
- ]
302
- },
303
- {
304
- "type": "struct",
305
- "name": "medialane_marketplace_erc721::core::types::OrderParameters",
306
- "members": [
307
- {
308
- "name": "offerer",
309
- "type": "core::starknet::contract_address::ContractAddress"
310
- },
311
- {
312
- "name": "marketplace",
313
- "type": "core::starknet::contract_address::ContractAddress"
314
- },
315
- {
316
- "name": "offer",
317
- "type": "medialane_marketplace_erc721::core::types::OfferItem"
318
- },
319
- {
320
- "name": "consideration",
321
- "type": "medialane_marketplace_erc721::core::types::ConsiderationItem"
322
- },
323
- {
324
- "name": "royalty_max_bps",
325
- "type": "core::felt252"
326
- },
327
- {
328
- "name": "start_time",
329
- "type": "core::felt252"
330
- },
331
- {
332
- "name": "end_time",
333
- "type": "core::felt252"
334
- },
335
- {
336
- "name": "salt",
337
- "type": "core::felt252"
338
- },
339
- {
340
- "name": "counter",
341
- "type": "core::felt252"
342
- }
343
- ]
344
- },
345
- {
346
- "type": "struct",
347
- "name": "medialane_marketplace_erc721::core::types::Order",
348
- "members": [
349
- {
350
- "name": "parameters",
351
- "type": "medialane_marketplace_erc721::core::types::OrderParameters"
352
- },
353
- {
354
- "name": "signature",
355
- "type": "core::array::Array::<core::felt252>"
356
- }
357
- ]
358
- },
359
- {
360
- "type": "struct",
361
- "name": "medialane_marketplace_erc721::core::types::OrderCancellation",
362
- "members": [
363
- {
364
- "name": "order_hash",
365
- "type": "core::felt252"
366
- },
367
- {
368
- "name": "offerer",
369
- "type": "core::starknet::contract_address::ContractAddress"
370
- }
371
- ]
372
- },
373
- {
374
- "type": "struct",
375
- "name": "medialane_marketplace_erc721::core::types::CancelRequest",
376
- "members": [
377
- {
378
- "name": "cancelation",
379
- "type": "medialane_marketplace_erc721::core::types::OrderCancellation"
380
- },
381
- {
382
- "name": "signature",
383
- "type": "core::array::Array::<core::felt252>"
384
- }
385
- ]
386
- },
387
- {
388
- "type": "enum",
389
- "name": "medialane_marketplace_erc721::core::types::OrderStatus",
390
- "variants": [
391
- {
392
- "name": "None",
393
- "type": "()"
394
- },
395
- {
396
- "name": "Created",
397
- "type": "()"
398
- },
399
- {
400
- "name": "Filled",
401
- "type": "()"
402
- },
403
- {
404
- "name": "Cancelled",
405
- "type": "()"
230
+ const jitter = Math.random() * baseDelayMs;
231
+ const delay = Math.min(baseDelayMs * Math.pow(2, attempt) + jitter, maxDelayMs);
232
+ await sleep(delay);
233
+ }
234
+ }
235
+ throw lastError;
236
+ }
237
+
238
+ // src/api/client.ts
239
+ function deriveErrorCode(status) {
240
+ if (status === 404) return "TOKEN_NOT_FOUND";
241
+ if (status === 429) return "RATE_LIMITED";
242
+ if (status === 410) return "INTENT_EXPIRED";
243
+ if (status === 401 || status === 403) return "UNAUTHORIZED";
244
+ if (status === 400) return "INVALID_PARAMS";
245
+ return "UNKNOWN";
246
+ }
247
+ var MedialaneApiError = class extends Error {
248
+ constructor(status, message) {
249
+ super(message);
250
+ this.status = status;
251
+ this.name = "MedialaneApiError";
252
+ this.code = deriveErrorCode(status);
253
+ }
254
+ };
255
+ var ApiClient = class {
256
+ constructor(baseUrl, apiKey, retryOptions, chain = "STARKNET") {
257
+ this.baseUrl = baseUrl;
258
+ this.chain = chain;
259
+ this.baseHeaders = apiKey ? { "x-api-key": apiKey } : {};
260
+ this.retryOptions = retryOptions;
261
+ }
262
+ /** Normalize an address for this client's chain (chain-scoped — Decision B). */
263
+ addr(a) {
264
+ return normalizeAddress(this.chain, a);
265
+ }
266
+ async request(path, init) {
267
+ const url = `${this.baseUrl.replace(/\/$/, "")}${path}`;
268
+ const headers = { ...this.baseHeaders };
269
+ if (!(init?.body instanceof FormData)) {
270
+ headers["Content-Type"] = "application/json";
271
+ }
272
+ const res = await withRetry(async () => {
273
+ const response = await fetch(url, {
274
+ ...init,
275
+ headers: { ...headers, ...init?.headers }
276
+ });
277
+ if (!response.ok) {
278
+ const text = await response.text().catch(() => response.statusText);
279
+ let message = text;
280
+ try {
281
+ const body = JSON.parse(text);
282
+ if (body.error) message = body.error;
283
+ } catch {
284
+ }
285
+ throw new MedialaneApiError(response.status, message);
406
286
  }
407
- ]
408
- },
409
- {
410
- "type": "struct",
411
- "name": "medialane_marketplace_erc721::core::types::OrderDetails",
412
- "members": [
413
- {
414
- "name": "offerer",
415
- "type": "core::starknet::contract_address::ContractAddress"
416
- },
417
- {
418
- "name": "offer",
419
- "type": "medialane_marketplace_erc721::core::types::OfferItem"
420
- },
421
- {
422
- "name": "consideration",
423
- "type": "medialane_marketplace_erc721::core::types::ConsiderationItem"
424
- },
425
- {
426
- "name": "royalty_max_bps",
427
- "type": "core::felt252"
428
- },
429
- {
430
- "name": "start_time",
431
- "type": "core::integer::u64"
432
- },
433
- {
434
- "name": "end_time",
435
- "type": "core::integer::u64"
436
- },
437
- {
438
- "name": "order_status",
439
- "type": "medialane_marketplace_erc721::core::types::OrderStatus"
440
- },
441
- {
442
- "name": "counter",
443
- "type": "core::felt252"
287
+ return response;
288
+ }, this.retryOptions);
289
+ return res.json();
290
+ }
291
+ get(path) {
292
+ return this.request(path, { method: "GET" });
293
+ }
294
+ post(path, body) {
295
+ return this.request(path, { method: "POST", body: JSON.stringify(body) });
296
+ }
297
+ patch(path, body) {
298
+ return this.request(path, { method: "PATCH", body: JSON.stringify(body) });
299
+ }
300
+ del(path) {
301
+ return this.request(path, { method: "DELETE" });
302
+ }
303
+ async checkResponse(res, options) {
304
+ if (options?.allow404 && res.status === 404) return null;
305
+ if (options?.allow403 && res.status === 403) return null;
306
+ if (!res.ok) {
307
+ const text = await res.text().catch(() => res.statusText);
308
+ let message = text;
309
+ try {
310
+ const body = JSON.parse(text);
311
+ if (body.error) message = body.error;
312
+ } catch {
444
313
  }
445
- ]
446
- },
447
- {
448
- "type": "interface",
449
- "name": "medialane_marketplace_erc721::core::interface::IMedialane",
450
- "items": [
451
- {
452
- "type": "function",
453
- "name": "register_order",
454
- "inputs": [
455
- {
456
- "name": "order",
457
- "type": "medialane_marketplace_erc721::core::types::Order"
458
- }
459
- ],
460
- "outputs": [],
461
- "state_mutability": "external"
462
- },
463
- {
464
- "type": "function",
465
- "name": "fulfill_order",
466
- "inputs": [
467
- {
468
- "name": "order_hash",
469
- "type": "core::felt252"
470
- }
471
- ],
472
- "outputs": [],
473
- "state_mutability": "external"
474
- },
475
- {
476
- "type": "function",
477
- "name": "cancel_order",
478
- "inputs": [
479
- {
480
- "name": "cancel_request",
481
- "type": "medialane_marketplace_erc721::core::types::CancelRequest"
482
- }
483
- ],
484
- "outputs": [],
485
- "state_mutability": "external"
314
+ throw new MedialaneApiError(res.status, message);
315
+ }
316
+ return res.json();
317
+ }
318
+ // ─── Orders ────────────────────────────────────────────────────────────────
319
+ getOrders(query = {}) {
320
+ const params = new URLSearchParams();
321
+ if (query.status) params.set("status", query.status);
322
+ if (query.collection) params.set("collection", query.collection);
323
+ if (query.currency) params.set("currency", query.currency);
324
+ if (query.sort) params.set("sort", query.sort);
325
+ if (query.page !== void 0) params.set("page", String(query.page));
326
+ if (query.limit !== void 0) params.set("limit", String(query.limit));
327
+ if (query.offerer) params.set("offerer", this.addr(query.offerer));
328
+ if (query.minPrice) params.set("minPrice", query.minPrice);
329
+ if (query.maxPrice) params.set("maxPrice", query.maxPrice);
330
+ if (query.chain) params.set("chain", query.chain);
331
+ const qs = params.toString();
332
+ return this.get(`/v1/orders${qs ? `?${qs}` : ""}`);
333
+ }
334
+ getOrder(orderHash) {
335
+ return this.get(`/v1/orders/${orderHash}`);
336
+ }
337
+ getActiveOrdersForToken(contract, tokenId) {
338
+ return this.get(`/v1/orders/token/${this.addr(contract)}/${tokenId}`);
339
+ }
340
+ getOrdersByUser(address, page = 1, limit = 20) {
341
+ return this.get(
342
+ `/v1/orders/user/${this.addr(address)}?page=${page}&limit=${limit}`
343
+ );
344
+ }
345
+ // ─── Tokens ────────────────────────────────────────────────────────────────
346
+ getToken(contract, tokenId, wait = false) {
347
+ return this.get(
348
+ `/v1/tokens/${contract}/${tokenId}${wait ? "?wait=true" : ""}`
349
+ );
350
+ }
351
+ getTokensByOwner(address, page = 1, limit = 20) {
352
+ return this.get(
353
+ `/v1/tokens/owned/${this.addr(address)}?page=${page}&limit=${limit}`
354
+ );
355
+ }
356
+ getTokenHistory(contract, tokenId, page = 1, limit = 20) {
357
+ return this.get(
358
+ `/v1/tokens/${contract}/${tokenId}/history?page=${page}&limit=${limit}`
359
+ );
360
+ }
361
+ // ─── Collections ───────────────────────────────────────────────────────────
362
+ getCollections(page = 1, limit = 20, isKnown, sort, service, chain) {
363
+ const params = new URLSearchParams({ page: String(page), limit: String(limit) });
364
+ if (isKnown !== void 0) params.set("isKnown", String(isKnown));
365
+ if (sort) params.set("sort", sort);
366
+ if (service) params.set("service", service);
367
+ if (chain) params.set("chain", chain);
368
+ return this.get(`/v1/collections?${params}`);
369
+ }
370
+ getCollectionsByOwner(owner, page = 1, limit = 50) {
371
+ const params = new URLSearchParams({ owner: this.addr(owner), page: String(page), limit: String(limit) });
372
+ return this.get(`/v1/collections?${params}`);
373
+ }
374
+ getCollection(contract) {
375
+ return this.get(`/v1/collections/${this.addr(contract)}`);
376
+ }
377
+ getCollectionTokens(contract, page = 1, limit = 20, sort = "recent") {
378
+ return this.get(
379
+ `/v1/collections/${this.addr(contract)}/tokens?page=${page}&limit=${limit}&sort=${sort}`
380
+ );
381
+ }
382
+ // ─── Activities ────────────────────────────────────────────────────────────
383
+ getActivities(query = {}) {
384
+ const params = new URLSearchParams();
385
+ if (query.type) params.set("type", query.type);
386
+ if (query.page !== void 0) params.set("page", String(query.page));
387
+ if (query.limit !== void 0) params.set("limit", String(query.limit));
388
+ if (query.chain) params.set("chain", query.chain);
389
+ const qs = params.toString();
390
+ return this.get(`/v1/activities${qs ? `?${qs}` : ""}`);
391
+ }
392
+ getActivitiesByAddress(address, page = 1, limit = 20) {
393
+ return this.get(
394
+ `/v1/activities/${this.addr(address)}?page=${page}&limit=${limit}`
395
+ );
396
+ }
397
+ // ─── Comments ──────────────────────────────────────────────────────────────
398
+ getTokenComments(contract, tokenId, opts = {}) {
399
+ const params = new URLSearchParams();
400
+ if (opts.page !== void 0) params.set("page", String(opts.page));
401
+ if (opts.limit !== void 0) params.set("limit", String(opts.limit));
402
+ const qs = params.toString();
403
+ return this.get(
404
+ `/v1/tokens/${this.addr(contract)}/${tokenId}/comments${qs ? `?${qs}` : ""}`
405
+ );
406
+ }
407
+ // ─── Search ────────────────────────────────────────────────────────────────
408
+ search(q, limit = 10, chain) {
409
+ const params = new URLSearchParams({ q, limit: String(limit) });
410
+ if (chain) params.set("chain", chain);
411
+ return this.get(
412
+ `/v1/search?${params.toString()}`
413
+ );
414
+ }
415
+ // ─── Intents ───────────────────────────────────────────────────────────────
416
+ createListingIntent(params) {
417
+ return this.post("/v1/intents/listing", params);
418
+ }
419
+ createOfferIntent(params) {
420
+ return this.post("/v1/intents/offer", params);
421
+ }
422
+ createFulfillIntent(params) {
423
+ return this.post("/v1/intents/fulfill", params);
424
+ }
425
+ createCancelIntent(params) {
426
+ return this.post("/v1/intents/cancel", params);
427
+ }
428
+ getIntent(id) {
429
+ return this.get(`/v1/intents/${id}`);
430
+ }
431
+ submitIntentSignature(id, signature) {
432
+ return this.patch(`/v1/intents/${id}/signature`, { signature });
433
+ }
434
+ confirmIntent(id, txHash) {
435
+ return this.patch(`/v1/intents/${id}/confirm`, { txHash });
436
+ }
437
+ createMintIntent(params) {
438
+ return this.post("/v1/intents/mint", params);
439
+ }
440
+ createCollectionIntent(params) {
441
+ return this.post("/v1/intents/create-collection", params);
442
+ }
443
+ /**
444
+ * Create a counter-offer intent. The seller proposes a new price in response
445
+ * to a buyer's active bid. clerkToken is optional — the endpoint authenticates
446
+ * via the tenant API key; pass a Clerk JWT only if your backend requires it.
447
+ */
448
+ createCounterOfferIntent(params, clerkToken) {
449
+ const extraHeaders = clerkToken ? { "Authorization": `Bearer ${clerkToken}` } : {};
450
+ return this.request("/v1/intents/counter-offer", {
451
+ method: "POST",
452
+ body: JSON.stringify(params),
453
+ headers: extraHeaders
454
+ });
455
+ }
456
+ /**
457
+ * Fetch counter-offers. Pass `originalOrderHash` (buyer view) or
458
+ * `sellerAddress` (seller view) — at least one is required.
459
+ */
460
+ getCounterOffers(query) {
461
+ const params = new URLSearchParams();
462
+ if (query.originalOrderHash) params.set("originalOrderHash", query.originalOrderHash);
463
+ if (query.sellerAddress) params.set("sellerAddress", query.sellerAddress);
464
+ if (query.page !== void 0) params.set("page", String(query.page));
465
+ if (query.limit !== void 0) params.set("limit", String(query.limit));
466
+ return this.get(`/v1/orders/counter-offers?${params}`);
467
+ }
468
+ // ─── Metadata ──────────────────────────────────────────────────────────────
469
+ getMetadataSignedUrl() {
470
+ return this.get("/v1/metadata/signed-url");
471
+ }
472
+ uploadMetadata(metadata) {
473
+ return this.post("/v1/metadata/upload", metadata);
474
+ }
475
+ resolveMetadata(uri) {
476
+ const params = new URLSearchParams({ uri });
477
+ return this.get(`/v1/metadata/resolve?${params.toString()}`);
478
+ }
479
+ uploadFile(file) {
480
+ const formData = new FormData();
481
+ formData.append("file", file);
482
+ return this.request("/v1/metadata/upload-file", {
483
+ method: "POST",
484
+ body: formData
485
+ });
486
+ }
487
+ // ─── Portal (tenant self-service) ──────────────────────────────────────────
488
+ getMe() {
489
+ return this.get("/v1/portal/me");
490
+ }
491
+ getApiKeys() {
492
+ return this.get("/v1/portal/keys");
493
+ }
494
+ createApiKey(label) {
495
+ return this.post("/v1/portal/keys", label ? { label } : {});
496
+ }
497
+ deleteApiKey(id) {
498
+ return this.del(`/v1/portal/keys/${id}`);
499
+ }
500
+ getUsage() {
501
+ return this.get("/v1/portal/usage");
502
+ }
503
+ getWebhooks() {
504
+ return this.get("/v1/portal/webhooks");
505
+ }
506
+ createWebhook(params) {
507
+ return this.post("/v1/portal/webhooks", params);
508
+ }
509
+ deleteWebhook(id) {
510
+ return this.del(
511
+ `/v1/portal/webhooks/${id}`
512
+ );
513
+ }
514
+ // ─── Collection Claims ──────────────────────────────────────────────────────
515
+ /**
516
+ * Path 1: On-chain auto claim. Sends both x-api-key (tenant auth) and
517
+ * Authorization: Bearer (Clerk JWT) simultaneously.
518
+ */
519
+ async claimCollection(contractAddress, walletAddress, clerkToken) {
520
+ const url = `${this.baseUrl.replace(/\/$/, "")}/v1/collections/claim`;
521
+ const res = await fetch(url, {
522
+ method: "POST",
523
+ headers: {
524
+ "x-api-key": this.baseHeaders["x-api-key"] ?? "",
525
+ "Content-Type": "application/json",
526
+ "Authorization": `Bearer ${clerkToken}`
486
527
  },
487
- {
488
- "type": "function",
489
- "name": "increment_counter",
490
- "inputs": [],
491
- "outputs": [],
492
- "state_mutability": "external"
528
+ body: JSON.stringify({ contractAddress, walletAddress })
529
+ });
530
+ return this.checkResponse(res);
531
+ }
532
+ /**
533
+ * Path 3: Manual off-chain claim request (email-based).
534
+ */
535
+ requestCollectionClaim(params) {
536
+ return this.request("/v1/collections/claim/request", {
537
+ method: "POST",
538
+ body: JSON.stringify(params)
539
+ });
540
+ }
541
+ // ─── Collection Profiles ────────────────────────────────────────────────────
542
+ async getCollectionProfile(contractAddress) {
543
+ const url = `${this.baseUrl.replace(/\/$/, "")}/v1/collections/${this.addr(contractAddress)}/profile`;
544
+ const res = await fetch(url, { headers: this.baseHeaders });
545
+ return this.checkResponse(res, { allow404: true });
546
+ }
547
+ /**
548
+ * Update collection profile. Requires Clerk JWT for ownership check.
549
+ */
550
+ async updateCollectionProfile(contractAddress, data, clerkToken) {
551
+ const url = `${this.baseUrl.replace(/\/$/, "")}/v1/collections/${this.addr(contractAddress)}/profile`;
552
+ const res = await fetch(url, {
553
+ method: "PATCH",
554
+ headers: {
555
+ "x-api-key": this.baseHeaders["x-api-key"] ?? "",
556
+ "Content-Type": "application/json",
557
+ "Authorization": `Bearer ${clerkToken}`
493
558
  },
494
- {
495
- "type": "function",
496
- "name": "get_order_details",
497
- "inputs": [
498
- {
499
- "name": "order_hash",
500
- "type": "core::felt252"
501
- }
502
- ],
503
- "outputs": [
504
- {
505
- "type": "medialane_marketplace_erc721::core::types::OrderDetails"
506
- }
507
- ],
508
- "state_mutability": "view"
509
- },
510
- {
511
- "type": "function",
512
- "name": "get_order_hash",
513
- "inputs": [
514
- {
515
- "name": "parameters",
516
- "type": "medialane_marketplace_erc721::core::types::OrderParameters"
517
- },
518
- {
519
- "name": "signer",
520
- "type": "core::starknet::contract_address::ContractAddress"
521
- }
522
- ],
523
- "outputs": [
524
- {
525
- "type": "core::felt252"
526
- }
527
- ],
528
- "state_mutability": "view"
529
- },
530
- {
531
- "type": "function",
532
- "name": "get_cancellation_hash",
533
- "inputs": [
534
- {
535
- "name": "cancellation",
536
- "type": "medialane_marketplace_erc721::core::types::OrderCancellation"
537
- },
538
- {
539
- "name": "signer",
540
- "type": "core::starknet::contract_address::ContractAddress"
541
- }
542
- ],
543
- "outputs": [
544
- {
545
- "type": "core::felt252"
546
- }
547
- ],
548
- "state_mutability": "view"
549
- },
550
- {
551
- "type": "function",
552
- "name": "get_counter",
553
- "inputs": [
554
- {
555
- "name": "offerer",
556
- "type": "core::starknet::contract_address::ContractAddress"
557
- }
558
- ],
559
- "outputs": [
560
- {
561
- "type": "core::felt252"
562
- }
563
- ],
564
- "state_mutability": "view"
565
- },
566
- {
567
- "type": "function",
568
- "name": "get_native_token_address",
569
- "inputs": [],
570
- "outputs": [
571
- {
572
- "type": "core::starknet::contract_address::ContractAddress"
573
- }
574
- ],
575
- "state_mutability": "view"
576
- }
577
- ]
578
- },
579
- {
580
- "type": "constructor",
581
- "name": "constructor",
582
- "inputs": [
583
- {
584
- "name": "native_token_address",
585
- "type": "core::starknet::contract_address::ContractAddress"
586
- }
587
- ]
588
- },
589
- {
590
- "type": "event",
591
- "name": "medialane_marketplace_erc721::core::events::OrderCreated",
592
- "kind": "struct",
593
- "members": [
594
- {
595
- "name": "order_hash",
596
- "type": "core::felt252",
597
- "kind": "key"
598
- },
599
- {
600
- "name": "offerer",
601
- "type": "core::starknet::contract_address::ContractAddress",
602
- "kind": "key"
603
- }
604
- ]
605
- },
606
- {
607
- "type": "struct",
608
- "name": "core::integer::u256",
609
- "members": [
610
- {
611
- "name": "low",
612
- "type": "core::integer::u128"
613
- },
614
- {
615
- "name": "high",
616
- "type": "core::integer::u128"
617
- }
618
- ]
619
- },
620
- {
621
- "type": "event",
622
- "name": "medialane_marketplace_erc721::core::events::OrderFulfilled",
623
- "kind": "struct",
624
- "members": [
625
- {
626
- "name": "order_hash",
627
- "type": "core::felt252",
628
- "kind": "key"
629
- },
630
- {
631
- "name": "offerer",
632
- "type": "core::starknet::contract_address::ContractAddress",
633
- "kind": "key"
559
+ body: JSON.stringify(data)
560
+ });
561
+ return this.checkResponse(res);
562
+ }
563
+ async getGatedContent(contractAddress, clerkToken) {
564
+ const url = `${this.baseUrl.replace(/\/$/, "")}/v1/collections/${this.addr(contractAddress)}/gated-content`;
565
+ const res = await fetch(url, {
566
+ headers: { ...this.baseHeaders, "Authorization": `Bearer ${clerkToken}` }
567
+ });
568
+ return this.checkResponse(res, { allow404: true, allow403: true });
569
+ }
570
+ // ─── Creator Profiles ───────────────────────────────────────────────────────
571
+ /** List all creators with an approved username. */
572
+ async getCreators(opts = {}) {
573
+ const params = new URLSearchParams();
574
+ if (opts.search) params.set("search", opts.search);
575
+ if (opts.page) params.set("page", String(opts.page));
576
+ if (opts.limit) params.set("limit", String(opts.limit));
577
+ const url = `${this.baseUrl.replace(/\/$/, "")}/v1/creators?${params}`;
578
+ const res = await fetch(url, { headers: this.baseHeaders });
579
+ return this.checkResponse(res);
580
+ }
581
+ async getCreatorProfile(walletAddress) {
582
+ const url = `${this.baseUrl.replace(/\/$/, "")}/v1/creators/${this.addr(walletAddress)}/profile`;
583
+ const res = await fetch(url, { headers: this.baseHeaders });
584
+ return this.checkResponse(res, { allow404: true });
585
+ }
586
+ /** Resolve a username slug to a creator profile (public). */
587
+ async getCreatorByUsername(username) {
588
+ const url = `${this.baseUrl.replace(/\/$/, "")}/v1/creators/by-username/${encodeURIComponent(username.toLowerCase().trim())}`;
589
+ const res = await fetch(url, { headers: this.baseHeaders });
590
+ return this.checkResponse(res, { allow404: true });
591
+ }
592
+ /**
593
+ * Update creator profile. Requires Clerk JWT; wallet must match authenticated user.
594
+ */
595
+ async updateCreatorProfile(walletAddress, data, clerkToken) {
596
+ const url = `${this.baseUrl.replace(/\/$/, "")}/v1/creators/${this.addr(walletAddress)}/profile`;
597
+ const res = await fetch(url, {
598
+ method: "PATCH",
599
+ headers: {
600
+ "x-api-key": this.baseHeaders["x-api-key"] ?? "",
601
+ "Content-Type": "application/json",
602
+ "Authorization": `Bearer ${clerkToken}`
634
603
  },
635
- {
636
- "name": "fulfiller",
637
- "type": "core::starknet::contract_address::ContractAddress",
638
- "kind": "key"
604
+ body: JSON.stringify(data)
605
+ });
606
+ return this.checkResponse(res);
607
+ }
608
+ // ─── Collection Slug Claims ───────────────────────────────────────────────────
609
+ /** Check if a collection slug is available (public, no auth). */
610
+ async checkCollectionSlugAvailability(slug) {
611
+ const url = `${this.baseUrl.replace(/\/$/, "")}/v1/collection-slug-claims/check/${encodeURIComponent(slug.toLowerCase().trim())}`;
612
+ const res = await fetch(url, { headers: this.baseHeaders });
613
+ return this.checkResponse(res);
614
+ }
615
+ /** Submit a slug claim for a collection. Requires Clerk JWT — caller must be the collection owner. */
616
+ async submitCollectionSlugClaim(contractAddress, slug, clerkToken, notifyEmail) {
617
+ const url = `${this.baseUrl.replace(/\/$/, "")}/v1/collection-slug-claims`;
618
+ const res = await fetch(url, {
619
+ method: "POST",
620
+ headers: {
621
+ "x-api-key": this.baseHeaders["x-api-key"] ?? "",
622
+ "Content-Type": "application/json",
623
+ Authorization: `Bearer ${clerkToken}`
639
624
  },
640
- {
641
- "name": "sale_amount",
642
- "type": "core::integer::u256",
643
- "kind": "data"
625
+ body: JSON.stringify({ contractAddress, slug, notifyEmail })
626
+ });
627
+ return this.checkResponse(res);
628
+ }
629
+ /** Returns all slug claims submitted by the authenticated wallet. Requires Clerk JWT. */
630
+ async getMyCollectionSlugClaims(clerkToken) {
631
+ const url = `${this.baseUrl.replace(/\/$/, "")}/v1/collection-slug-claims/me`;
632
+ const res = await fetch(url, {
633
+ headers: { ...this.baseHeaders, Authorization: `Bearer ${clerkToken}` }
634
+ });
635
+ return this.checkResponse(res);
636
+ }
637
+ /** Resolve a collection slug to a full collection. Returns null if not found. */
638
+ async getCollectionBySlug(slug) {
639
+ const url = `${this.baseUrl.replace(/\/$/, "")}/v1/collections/by-slug/${encodeURIComponent(slug.toLowerCase().trim())}`;
640
+ const res = await fetch(url, { headers: this.baseHeaders });
641
+ return this.checkResponse(res, { allow404: true });
642
+ }
643
+ // ─── User Wallet ─────────────────────────────────────────────────────────────
644
+ /**
645
+ * Upsert the authenticated user's wallet address in the backend DB.
646
+ * Call after onboarding when ChipiPay confirms the wallet address.
647
+ * Requires Clerk JWT; no tenant API key needed.
648
+ */
649
+ /**
650
+ * Frictionless wallet registration. Tenant API key only (no Clerk JWT required).
651
+ * Idempotent — backend's ensureAccountForWallet upserts and upgrades existing
652
+ * UNKNOWN walletType rows when a more specific value is supplied.
653
+ */
654
+ async registerUser(params) {
655
+ return this.post("/v1/users/register", params);
656
+ }
657
+ async upsertMyWallet(clerkToken, options = {}) {
658
+ const url = `${this.baseUrl.replace(/\/$/, "")}/v1/users/me`;
659
+ const body = {
660
+ walletType: options.walletType ?? "UNKNOWN",
661
+ appSource: options.appSource ?? "MEDIALANE_SDK"
662
+ };
663
+ if (options.chain) body.chain = options.chain;
664
+ const res = await fetch(url, {
665
+ method: "POST",
666
+ headers: {
667
+ "Content-Type": "application/json",
668
+ "Authorization": `Bearer ${clerkToken}`
644
669
  },
645
- {
646
- "name": "royalty_receiver",
647
- "type": "core::starknet::contract_address::ContractAddress",
648
- "kind": "data"
670
+ body: JSON.stringify(body)
671
+ });
672
+ return this.checkResponse(res);
673
+ }
674
+ /**
675
+ * Get the authenticated user's stored wallet address from the backend DB.
676
+ * Returns null if the user has not completed onboarding yet.
677
+ * Requires Clerk JWT; no tenant API key needed.
678
+ */
679
+ async getMyWallet(clerkToken) {
680
+ const url = `${this.baseUrl.replace(/\/$/, "")}/v1/users/me`;
681
+ const res = await fetch(url, {
682
+ headers: { "Authorization": `Bearer ${clerkToken}` }
683
+ });
684
+ return this.checkResponse(res, { allow404: true });
685
+ }
686
+ // ─── Remix Licensing ─────────────────────────────────────────────────────────
687
+ /**
688
+ * Get public remixes of a token (open to everyone).
689
+ */
690
+ getTokenRemixes(contract, tokenId, opts = {}) {
691
+ const params = new URLSearchParams();
692
+ if (opts.page !== void 0) params.set("page", String(opts.page));
693
+ if (opts.limit !== void 0) params.set("limit", String(opts.limit));
694
+ const qs = params.toString();
695
+ return this.get(
696
+ `/v1/tokens/${this.addr(contract)}/${tokenId}/remixes${qs ? `?${qs}` : ""}`
697
+ );
698
+ }
699
+ /**
700
+ * Submit a custom remix offer for a token. Requires Clerk JWT.
701
+ */
702
+ submitRemixOffer(params, clerkToken) {
703
+ return this.request("/v1/remix-offers", {
704
+ method: "POST",
705
+ body: JSON.stringify(params),
706
+ headers: { "Authorization": `Bearer ${clerkToken}` }
707
+ });
708
+ }
709
+ /**
710
+ * Submit an auto remix offer for a token with an open license. Requires Clerk JWT.
711
+ */
712
+ submitAutoRemixOffer(params, clerkToken) {
713
+ return this.request("/v1/remix-offers/auto", {
714
+ method: "POST",
715
+ body: JSON.stringify(params),
716
+ headers: { "Authorization": `Bearer ${clerkToken}` }
717
+ });
718
+ }
719
+ /**
720
+ * Record a self-remix (owner remixing their own token). Requires Clerk JWT.
721
+ */
722
+ confirmSelfRemix(params, clerkToken) {
723
+ return this.request("/v1/remix-offers/self/confirm", {
724
+ method: "POST",
725
+ body: JSON.stringify(params),
726
+ headers: { "Authorization": `Bearer ${clerkToken}` }
727
+ });
728
+ }
729
+ /**
730
+ * List remix offers by role. Requires Clerk JWT.
731
+ * role="creator" — offers where you are the original creator.
732
+ * role="requester" — offers you made.
733
+ */
734
+ async getRemixOffers(query, clerkToken) {
735
+ const params = new URLSearchParams({ role: query.role });
736
+ if (query.page !== void 0) params.set("page", String(query.page));
737
+ if (query.limit !== void 0) params.set("limit", String(query.limit));
738
+ const url = `${this.baseUrl.replace(/\/$/, "")}/v1/remix-offers?${params}`;
739
+ const res = await fetch(url, {
740
+ headers: { ...this.baseHeaders, "Authorization": `Bearer ${clerkToken}` }
741
+ });
742
+ return this.checkResponse(res);
743
+ }
744
+ /**
745
+ * Get a single remix offer. Clerk JWT optional (price/currency hidden for non-participants).
746
+ */
747
+ async getRemixOffer(id, clerkToken) {
748
+ const url = `${this.baseUrl.replace(/\/$/, "")}/v1/remix-offers/${id}`;
749
+ const headers = { ...this.baseHeaders };
750
+ if (clerkToken) headers["Authorization"] = `Bearer ${clerkToken}`;
751
+ const res = await fetch(url, { headers });
752
+ return this.checkResponse(res);
753
+ }
754
+ /**
755
+ * Creator approves a remix offer (authorises the requester to mint). Requires Clerk JWT.
756
+ */
757
+ confirmRemixOffer(id, params, clerkToken) {
758
+ return this.request(`/v1/remix-offers/${id}/confirm`, {
759
+ method: "POST",
760
+ body: JSON.stringify(params),
761
+ headers: { "Authorization": `Bearer ${clerkToken}` }
762
+ });
763
+ }
764
+ /**
765
+ * Creator rejects a remix offer. Requires Clerk JWT.
766
+ */
767
+ rejectRemixOffer(id, clerkToken) {
768
+ return this.request(`/v1/remix-offers/${id}/reject`, {
769
+ method: "POST",
770
+ body: JSON.stringify({}),
771
+ headers: { "Authorization": `Bearer ${clerkToken}` }
772
+ });
773
+ }
774
+ /**
775
+ * Requester extends the expiry of a pending remix offer by 1–30 days.
776
+ * Requires Clerk JWT.
777
+ */
778
+ extendRemixOffer(id, days, clerkToken) {
779
+ return this.request(`/v1/remix-offers/${id}/extend`, {
780
+ method: "POST",
781
+ body: JSON.stringify({ days }),
782
+ headers: { "Authorization": `Bearer ${clerkToken}` }
783
+ });
784
+ }
785
+ // ─── POP Protocol ──────────────────────────────────────────────────────────
786
+ getPopCollections(opts = {}) {
787
+ return this.getCollections(opts.page ?? 1, opts.limit ?? 20, void 0, opts.sort, "POP_PROTOCOL");
788
+ }
789
+ async getPopEligibility(collection, wallet) {
790
+ const res = await this.get(
791
+ `/v1/pop/eligibility/${this.addr(collection)}/${this.addr(wallet)}`
792
+ );
793
+ return res.data;
794
+ }
795
+ async getPopEligibilityBatch(collection, wallets) {
796
+ const params = new URLSearchParams({ wallets: wallets.map((w) => this.addr(w)).join(",") });
797
+ const res = await this.get(
798
+ `/v1/pop/eligibility/${this.addr(collection)}?${params}`
799
+ );
800
+ return res.data;
801
+ }
802
+ // ─── Coins (fungible — ERC-20 etc.) ───────────────────────────────────────────
803
+ // Coins are a separate model from Collections (spec 2026-06-14). Price/liquidity
804
+ // is read live from Ekubo (CreatorCoinService.getPrice), never from these.
805
+ getCoins(opts = {}) {
806
+ const params = new URLSearchParams();
807
+ if (opts.page) params.set("page", String(opts.page));
808
+ if (opts.limit) params.set("limit", String(opts.limit));
809
+ if (opts.service) params.set("service", opts.service);
810
+ if (opts.chain) params.set("chain", opts.chain);
811
+ const qs = params.toString();
812
+ return this.get(`/v1/coins${qs ? `?${qs}` : ""}`);
813
+ }
814
+ getCoin(contract) {
815
+ return this.get(`/v1/coins/${this.addr(contract)}`);
816
+ }
817
+ // ─── Collection Drop ────────────────────────────────────────────────────────
818
+ getDropCollections(opts = {}) {
819
+ return this.getCollections(opts.page ?? 1, opts.limit ?? 20, void 0, opts.sort, "COLLECTION_DROP");
820
+ }
821
+ async getDropMintStatus(collection, wallet) {
822
+ const res = await this.get(
823
+ `/v1/drop/mint-status/${this.addr(collection)}/${this.addr(wallet)}`
824
+ );
825
+ return res.data;
826
+ }
827
+ // ─── Rewards (v0.49.0) ─────────────────────────────────────────────────────
828
+ // Scores are recomputed on a schedule by the backend (~15 min) — reads only.
829
+ /** Score + level + progress + badges for one address (zeroed for unknown). */
830
+ async getRewards(address) {
831
+ const res = await this.get(`/v1/rewards/${this.addr(address)}`);
832
+ return res.data;
833
+ }
834
+ /** Paginated XP leaderboard. */
835
+ getRewardsLeaderboard(page = 1, limit = 50) {
836
+ return this.get(`/v1/rewards?page=${page}&limit=${limit}`);
837
+ }
838
+ /** Point-event history for an address. */
839
+ getRewardsEvents(address, page = 1, limit = 20) {
840
+ return this.get(
841
+ `/v1/rewards/${this.addr(address)}/events?page=${page}&limit=${limit}`
842
+ );
843
+ }
844
+ /** Reward configuration: level ladder, enabled action XP values, badge catalog. */
845
+ async getRewardsConfig() {
846
+ const res = await this.get(`/v1/rewards/config`);
847
+ return res.data;
848
+ }
849
+ /** Minimal level info for up to 50 addresses — one call per list page. */
850
+ async getRewardsBatch(addresses) {
851
+ if (addresses.length === 0) return [];
852
+ const params = new URLSearchParams({ addresses: addresses.map((a) => this.addr(a)).join(",") });
853
+ const res = await this.get(`/v1/rewards/batch?${params}`);
854
+ return res.data;
855
+ }
856
+ };
857
+
858
+ // src/starknet/abis/ipMarketplace.ts
859
+ var IPMarketplaceABI = [
860
+ {
861
+ "type": "impl",
862
+ "name": "Medialane721Impl",
863
+ "interface_name": "medialane_marketplace_erc721::core::interface::IMedialane"
864
+ },
865
+ {
866
+ "type": "struct",
867
+ "name": "medialane_marketplace_erc721::core::types::OfferItem",
868
+ "members": [
869
+ {
870
+ "name": "item_type",
871
+ "type": "core::felt252"
649
872
  },
650
873
  {
651
- "name": "royalty_amount",
652
- "type": "core::integer::u256",
653
- "kind": "data"
874
+ "name": "token",
875
+ "type": "core::starknet::contract_address::ContractAddress"
876
+ },
877
+ {
878
+ "name": "identifier_or_criteria",
879
+ "type": "core::felt252"
880
+ },
881
+ {
882
+ "name": "amount",
883
+ "type": "core::felt252"
654
884
  }
655
885
  ]
656
886
  },
657
887
  {
658
- "type": "event",
659
- "name": "medialane_marketplace_erc721::core::events::OrderCancelled",
660
- "kind": "struct",
888
+ "type": "struct",
889
+ "name": "medialane_marketplace_erc721::core::types::ConsiderationItem",
661
890
  "members": [
662
891
  {
663
- "name": "order_hash",
664
- "type": "core::felt252",
665
- "kind": "key"
892
+ "name": "item_type",
893
+ "type": "core::felt252"
666
894
  },
667
895
  {
668
- "name": "offerer",
669
- "type": "core::starknet::contract_address::ContractAddress",
670
- "kind": "key"
896
+ "name": "token",
897
+ "type": "core::starknet::contract_address::ContractAddress"
898
+ },
899
+ {
900
+ "name": "identifier_or_criteria",
901
+ "type": "core::felt252"
902
+ },
903
+ {
904
+ "name": "amount",
905
+ "type": "core::felt252"
906
+ },
907
+ {
908
+ "name": "recipient",
909
+ "type": "core::starknet::contract_address::ContractAddress"
671
910
  }
672
911
  ]
673
912
  },
674
913
  {
675
- "type": "event",
676
- "name": "medialane_marketplace_erc721::core::events::CounterIncremented",
677
- "kind": "struct",
914
+ "type": "struct",
915
+ "name": "medialane_marketplace_erc721::core::types::OrderParameters",
678
916
  "members": [
679
917
  {
680
918
  "name": "offerer",
681
- "type": "core::starknet::contract_address::ContractAddress",
682
- "kind": "key"
919
+ "type": "core::starknet::contract_address::ContractAddress"
683
920
  },
684
921
  {
685
- "name": "new_counter",
686
- "type": "core::felt252",
687
- "kind": "data"
688
- }
689
- ]
690
- },
691
- {
692
- "type": "event",
693
- "name": "medialane_marketplace_erc721::core::medialane::Medialane721::Event",
694
- "kind": "enum",
695
- "variants": [
922
+ "name": "marketplace",
923
+ "type": "core::starknet::contract_address::ContractAddress"
924
+ },
696
925
  {
697
- "name": "OrderCreated",
698
- "type": "medialane_marketplace_erc721::core::events::OrderCreated",
699
- "kind": "nested"
926
+ "name": "offer",
927
+ "type": "medialane_marketplace_erc721::core::types::OfferItem"
700
928
  },
701
929
  {
702
- "name": "OrderFulfilled",
703
- "type": "medialane_marketplace_erc721::core::events::OrderFulfilled",
704
- "kind": "nested"
930
+ "name": "consideration",
931
+ "type": "medialane_marketplace_erc721::core::types::ConsiderationItem"
705
932
  },
706
933
  {
707
- "name": "OrderCancelled",
708
- "type": "medialane_marketplace_erc721::core::events::OrderCancelled",
709
- "kind": "nested"
934
+ "name": "royalty_max_bps",
935
+ "type": "core::felt252"
710
936
  },
711
937
  {
712
- "name": "CounterIncremented",
713
- "type": "medialane_marketplace_erc721::core::events::CounterIncremented",
714
- "kind": "nested"
938
+ "name": "start_time",
939
+ "type": "core::felt252"
940
+ },
941
+ {
942
+ "name": "end_time",
943
+ "type": "core::felt252"
944
+ },
945
+ {
946
+ "name": "salt",
947
+ "type": "core::felt252"
948
+ },
949
+ {
950
+ "name": "counter",
951
+ "type": "core::felt252"
715
952
  }
716
953
  ]
717
- }
718
- ];
719
-
720
- // src/starknet/abis/popCollection.ts
721
- var POPCollectionABI = [
722
- {
723
- type: "struct",
724
- name: "core::byte_array::ByteArray",
725
- members: [
726
- { name: "data", type: "core::array::Array::<core::felt252>" },
727
- { name: "pending_word", type: "core::felt252" },
728
- { name: "pending_word_len", type: "core::integer::u32" }
729
- ]
730
- },
731
- {
732
- type: "function",
733
- name: "claim",
734
- inputs: [],
735
- outputs: [],
736
- state_mutability: "external"
737
- },
738
- {
739
- type: "function",
740
- name: "admin_mint",
741
- inputs: [
742
- { name: "recipient", type: "core::starknet::contract_address::ContractAddress" },
743
- { name: "custom_uri", type: "core::byte_array::ByteArray" }
744
- ],
745
- outputs: [],
746
- state_mutability: "external"
747
- },
748
- {
749
- type: "function",
750
- name: "add_to_allowlist",
751
- inputs: [{ name: "address", type: "core::starknet::contract_address::ContractAddress" }],
752
- outputs: [],
753
- state_mutability: "external"
754
- },
755
- {
756
- type: "function",
757
- name: "batch_add_to_allowlist",
758
- inputs: [{ name: "addresses", type: "core::array::Array::<core::starknet::contract_address::ContractAddress>" }],
759
- outputs: [],
760
- state_mutability: "external"
761
- },
762
- {
763
- type: "function",
764
- name: "remove_from_allowlist",
765
- inputs: [{ name: "address", type: "core::starknet::contract_address::ContractAddress" }],
766
- outputs: [],
767
- state_mutability: "external"
768
- },
769
- {
770
- type: "function",
771
- name: "set_token_uri",
772
- inputs: [
773
- { name: "token_id", type: "core::integer::u256" },
774
- { name: "uri", type: "core::byte_array::ByteArray" }
775
- ],
776
- outputs: [],
777
- state_mutability: "external"
778
- },
779
- {
780
- type: "function",
781
- name: "set_paused",
782
- inputs: [{ name: "paused", type: "core::bool" }],
783
- outputs: [],
784
- state_mutability: "external"
785
- },
786
- {
787
- type: "function",
788
- name: "is_eligible",
789
- inputs: [{ name: "address", type: "core::starknet::contract_address::ContractAddress" }],
790
- outputs: [{ type: "core::bool" }],
791
- state_mutability: "view"
792
- },
793
- {
794
- type: "function",
795
- name: "has_claimed",
796
- inputs: [{ name: "address", type: "core::starknet::contract_address::ContractAddress" }],
797
- outputs: [{ type: "core::bool" }],
798
- state_mutability: "view"
799
954
  },
800
955
  {
801
- type: "function",
802
- name: "total_minted",
803
- inputs: [],
804
- outputs: [{ type: "core::integer::u256" }],
805
- state_mutability: "view"
806
- }
807
- ];
808
-
809
- // src/starknet/abis/popFactory.ts
810
- var POPFactoryABI = [
811
- {
812
- type: "struct",
813
- name: "core::byte_array::ByteArray",
814
- members: [
815
- { name: "data", type: "core::array::Array::<core::felt252>" },
816
- { name: "pending_word", type: "core::felt252" },
817
- { name: "pending_word_len", type: "core::integer::u32" }
956
+ "type": "struct",
957
+ "name": "medialane_marketplace_erc721::core::types::Order",
958
+ "members": [
959
+ {
960
+ "name": "parameters",
961
+ "type": "medialane_marketplace_erc721::core::types::OrderParameters"
962
+ },
963
+ {
964
+ "name": "signature",
965
+ "type": "core::array::Array::<core::felt252>"
966
+ }
818
967
  ]
819
968
  },
820
969
  {
821
- type: "enum",
822
- name: "pop_protocol::types::EventType",
823
- variants: [
824
- { name: "Conference", type: "()" },
825
- { name: "Bootcamp", type: "()" },
826
- { name: "Workshop", type: "()" },
827
- { name: "Hackathon", type: "()" },
828
- { name: "Meetup", type: "()" },
829
- { name: "Course", type: "()" },
830
- { name: "Other", type: "()" }
970
+ "type": "struct",
971
+ "name": "medialane_marketplace_erc721::core::types::OrderCancellation",
972
+ "members": [
973
+ {
974
+ "name": "order_hash",
975
+ "type": "core::felt252"
976
+ },
977
+ {
978
+ "name": "offerer",
979
+ "type": "core::starknet::contract_address::ContractAddress"
980
+ }
831
981
  ]
832
982
  },
833
983
  {
834
- type: "function",
835
- name: "create_collection",
836
- inputs: [
837
- { name: "name", type: "core::byte_array::ByteArray" },
838
- { name: "symbol", type: "core::byte_array::ByteArray" },
839
- { name: "base_uri", type: "core::byte_array::ByteArray" },
840
- { name: "claim_end_time", type: "core::integer::u64" },
841
- { name: "event_type", type: "pop_protocol::types::EventType" }
842
- ],
843
- outputs: [{ type: "core::starknet::contract_address::ContractAddress" }],
844
- state_mutability: "external"
845
- },
846
- {
847
- type: "function",
848
- name: "register_provider",
849
- inputs: [
850
- { name: "provider", type: "core::starknet::contract_address::ContractAddress" },
851
- { name: "name", type: "core::byte_array::ByteArray" },
852
- { name: "website_url", type: "core::byte_array::ByteArray" }
853
- ],
854
- outputs: [],
855
- state_mutability: "external"
856
- },
857
- {
858
- type: "function",
859
- name: "set_pop_collection_class_hash",
860
- inputs: [{ name: "new_class_hash", type: "core::starknet::class_hash::ClassHash" }],
861
- outputs: [],
862
- state_mutability: "external"
863
- }
864
- ];
865
-
866
- // src/starknet/abis/dropCollection.ts
867
- var DropCollectionABI = [
868
- {
869
- type: "struct",
870
- name: "core::byte_array::ByteArray",
871
- members: [
872
- { name: "data", type: "core::array::Array::<core::felt252>" },
873
- { name: "pending_word", type: "core::felt252" },
874
- { name: "pending_word_len", type: "core::integer::u32" }
984
+ "type": "struct",
985
+ "name": "medialane_marketplace_erc721::core::types::CancelRequest",
986
+ "members": [
987
+ {
988
+ "name": "cancelation",
989
+ "type": "medialane_marketplace_erc721::core::types::OrderCancellation"
990
+ },
991
+ {
992
+ "name": "signature",
993
+ "type": "core::array::Array::<core::felt252>"
994
+ }
875
995
  ]
876
996
  },
877
997
  {
878
- type: "struct",
879
- name: "collection_drop::types::ClaimConditions",
880
- members: [
881
- { name: "start_time", type: "core::integer::u64" },
882
- { name: "end_time", type: "core::integer::u64" },
883
- { name: "price", type: "core::integer::u256" },
884
- { name: "payment_token", type: "core::starknet::contract_address::ContractAddress" },
885
- { name: "max_quantity_per_wallet", type: "core::integer::u256" }
998
+ "type": "enum",
999
+ "name": "medialane_marketplace_erc721::core::types::OrderStatus",
1000
+ "variants": [
1001
+ {
1002
+ "name": "None",
1003
+ "type": "()"
1004
+ },
1005
+ {
1006
+ "name": "Created",
1007
+ "type": "()"
1008
+ },
1009
+ {
1010
+ "name": "Filled",
1011
+ "type": "()"
1012
+ },
1013
+ {
1014
+ "name": "Cancelled",
1015
+ "type": "()"
1016
+ }
886
1017
  ]
887
1018
  },
888
1019
  {
889
- type: "function",
890
- name: "claim",
891
- inputs: [{ name: "quantity", type: "core::integer::u256" }],
892
- outputs: [],
893
- state_mutability: "external"
894
- },
895
- {
896
- type: "function",
897
- name: "admin_mint",
898
- inputs: [
899
- { name: "recipient", type: "core::starknet::contract_address::ContractAddress" },
900
- { name: "quantity", type: "core::integer::u256" },
901
- { name: "custom_uri", type: "core::byte_array::ByteArray" }
902
- ],
903
- outputs: [],
904
- state_mutability: "external"
905
- },
906
- {
907
- type: "function",
908
- name: "set_claim_conditions",
909
- inputs: [{ name: "conditions", type: "collection_drop::types::ClaimConditions" }],
910
- outputs: [],
911
- state_mutability: "external"
912
- },
913
- {
914
- type: "function",
915
- name: "get_claim_conditions",
916
- inputs: [],
917
- outputs: [{ type: "collection_drop::types::ClaimConditions" }],
918
- state_mutability: "view"
919
- },
920
- {
921
- type: "function",
922
- name: "set_allowlist_enabled",
923
- inputs: [{ name: "enabled", type: "core::bool" }],
924
- outputs: [],
925
- state_mutability: "external"
1020
+ "type": "struct",
1021
+ "name": "medialane_marketplace_erc721::core::types::OrderDetails",
1022
+ "members": [
1023
+ {
1024
+ "name": "offerer",
1025
+ "type": "core::starknet::contract_address::ContractAddress"
1026
+ },
1027
+ {
1028
+ "name": "offer",
1029
+ "type": "medialane_marketplace_erc721::core::types::OfferItem"
1030
+ },
1031
+ {
1032
+ "name": "consideration",
1033
+ "type": "medialane_marketplace_erc721::core::types::ConsiderationItem"
1034
+ },
1035
+ {
1036
+ "name": "royalty_max_bps",
1037
+ "type": "core::felt252"
1038
+ },
1039
+ {
1040
+ "name": "start_time",
1041
+ "type": "core::integer::u64"
1042
+ },
1043
+ {
1044
+ "name": "end_time",
1045
+ "type": "core::integer::u64"
1046
+ },
1047
+ {
1048
+ "name": "order_status",
1049
+ "type": "medialane_marketplace_erc721::core::types::OrderStatus"
1050
+ },
1051
+ {
1052
+ "name": "counter",
1053
+ "type": "core::felt252"
1054
+ }
1055
+ ]
926
1056
  },
927
1057
  {
928
- type: "function",
929
- name: "is_allowlist_enabled",
930
- inputs: [],
931
- outputs: [{ type: "core::bool" }],
932
- state_mutability: "view"
1058
+ "type": "interface",
1059
+ "name": "medialane_marketplace_erc721::core::interface::IMedialane",
1060
+ "items": [
1061
+ {
1062
+ "type": "function",
1063
+ "name": "register_order",
1064
+ "inputs": [
1065
+ {
1066
+ "name": "order",
1067
+ "type": "medialane_marketplace_erc721::core::types::Order"
1068
+ }
1069
+ ],
1070
+ "outputs": [],
1071
+ "state_mutability": "external"
1072
+ },
1073
+ {
1074
+ "type": "function",
1075
+ "name": "fulfill_order",
1076
+ "inputs": [
1077
+ {
1078
+ "name": "order_hash",
1079
+ "type": "core::felt252"
1080
+ }
1081
+ ],
1082
+ "outputs": [],
1083
+ "state_mutability": "external"
1084
+ },
1085
+ {
1086
+ "type": "function",
1087
+ "name": "cancel_order",
1088
+ "inputs": [
1089
+ {
1090
+ "name": "cancel_request",
1091
+ "type": "medialane_marketplace_erc721::core::types::CancelRequest"
1092
+ }
1093
+ ],
1094
+ "outputs": [],
1095
+ "state_mutability": "external"
1096
+ },
1097
+ {
1098
+ "type": "function",
1099
+ "name": "increment_counter",
1100
+ "inputs": [],
1101
+ "outputs": [],
1102
+ "state_mutability": "external"
1103
+ },
1104
+ {
1105
+ "type": "function",
1106
+ "name": "get_order_details",
1107
+ "inputs": [
1108
+ {
1109
+ "name": "order_hash",
1110
+ "type": "core::felt252"
1111
+ }
1112
+ ],
1113
+ "outputs": [
1114
+ {
1115
+ "type": "medialane_marketplace_erc721::core::types::OrderDetails"
1116
+ }
1117
+ ],
1118
+ "state_mutability": "view"
1119
+ },
1120
+ {
1121
+ "type": "function",
1122
+ "name": "get_order_hash",
1123
+ "inputs": [
1124
+ {
1125
+ "name": "parameters",
1126
+ "type": "medialane_marketplace_erc721::core::types::OrderParameters"
1127
+ },
1128
+ {
1129
+ "name": "signer",
1130
+ "type": "core::starknet::contract_address::ContractAddress"
1131
+ }
1132
+ ],
1133
+ "outputs": [
1134
+ {
1135
+ "type": "core::felt252"
1136
+ }
1137
+ ],
1138
+ "state_mutability": "view"
1139
+ },
1140
+ {
1141
+ "type": "function",
1142
+ "name": "get_cancellation_hash",
1143
+ "inputs": [
1144
+ {
1145
+ "name": "cancellation",
1146
+ "type": "medialane_marketplace_erc721::core::types::OrderCancellation"
1147
+ },
1148
+ {
1149
+ "name": "signer",
1150
+ "type": "core::starknet::contract_address::ContractAddress"
1151
+ }
1152
+ ],
1153
+ "outputs": [
1154
+ {
1155
+ "type": "core::felt252"
1156
+ }
1157
+ ],
1158
+ "state_mutability": "view"
1159
+ },
1160
+ {
1161
+ "type": "function",
1162
+ "name": "get_counter",
1163
+ "inputs": [
1164
+ {
1165
+ "name": "offerer",
1166
+ "type": "core::starknet::contract_address::ContractAddress"
1167
+ }
1168
+ ],
1169
+ "outputs": [
1170
+ {
1171
+ "type": "core::felt252"
1172
+ }
1173
+ ],
1174
+ "state_mutability": "view"
1175
+ },
1176
+ {
1177
+ "type": "function",
1178
+ "name": "get_native_token_address",
1179
+ "inputs": [],
1180
+ "outputs": [
1181
+ {
1182
+ "type": "core::starknet::contract_address::ContractAddress"
1183
+ }
1184
+ ],
1185
+ "state_mutability": "view"
1186
+ }
1187
+ ]
933
1188
  },
934
1189
  {
935
- type: "function",
936
- name: "add_to_allowlist",
937
- inputs: [{ name: "address", type: "core::starknet::contract_address::ContractAddress" }],
938
- outputs: [],
939
- state_mutability: "external"
1190
+ "type": "constructor",
1191
+ "name": "constructor",
1192
+ "inputs": [
1193
+ {
1194
+ "name": "native_token_address",
1195
+ "type": "core::starknet::contract_address::ContractAddress"
1196
+ }
1197
+ ]
940
1198
  },
941
1199
  {
942
- type: "function",
943
- name: "batch_add_to_allowlist",
944
- inputs: [{ name: "addresses", type: "core::array::Array::<core::starknet::contract_address::ContractAddress>" }],
945
- outputs: [],
946
- state_mutability: "external"
1200
+ "type": "event",
1201
+ "name": "medialane_marketplace_erc721::core::events::OrderCreated",
1202
+ "kind": "struct",
1203
+ "members": [
1204
+ {
1205
+ "name": "order_hash",
1206
+ "type": "core::felt252",
1207
+ "kind": "key"
1208
+ },
1209
+ {
1210
+ "name": "offerer",
1211
+ "type": "core::starknet::contract_address::ContractAddress",
1212
+ "kind": "key"
1213
+ }
1214
+ ]
947
1215
  },
948
1216
  {
949
- type: "function",
950
- name: "remove_from_allowlist",
951
- inputs: [{ name: "address", type: "core::starknet::contract_address::ContractAddress" }],
952
- outputs: [],
953
- state_mutability: "external"
1217
+ "type": "struct",
1218
+ "name": "core::integer::u256",
1219
+ "members": [
1220
+ {
1221
+ "name": "low",
1222
+ "type": "core::integer::u128"
1223
+ },
1224
+ {
1225
+ "name": "high",
1226
+ "type": "core::integer::u128"
1227
+ }
1228
+ ]
954
1229
  },
955
1230
  {
956
- type: "function",
957
- name: "is_allowlisted",
1231
+ "type": "event",
1232
+ "name": "medialane_marketplace_erc721::core::events::OrderFulfilled",
1233
+ "kind": "struct",
1234
+ "members": [
1235
+ {
1236
+ "name": "order_hash",
1237
+ "type": "core::felt252",
1238
+ "kind": "key"
1239
+ },
1240
+ {
1241
+ "name": "offerer",
1242
+ "type": "core::starknet::contract_address::ContractAddress",
1243
+ "kind": "key"
1244
+ },
1245
+ {
1246
+ "name": "fulfiller",
1247
+ "type": "core::starknet::contract_address::ContractAddress",
1248
+ "kind": "key"
1249
+ },
1250
+ {
1251
+ "name": "sale_amount",
1252
+ "type": "core::integer::u256",
1253
+ "kind": "data"
1254
+ },
1255
+ {
1256
+ "name": "royalty_receiver",
1257
+ "type": "core::starknet::contract_address::ContractAddress",
1258
+ "kind": "data"
1259
+ },
1260
+ {
1261
+ "name": "royalty_amount",
1262
+ "type": "core::integer::u256",
1263
+ "kind": "data"
1264
+ }
1265
+ ]
1266
+ },
1267
+ {
1268
+ "type": "event",
1269
+ "name": "medialane_marketplace_erc721::core::events::OrderCancelled",
1270
+ "kind": "struct",
1271
+ "members": [
1272
+ {
1273
+ "name": "order_hash",
1274
+ "type": "core::felt252",
1275
+ "kind": "key"
1276
+ },
1277
+ {
1278
+ "name": "offerer",
1279
+ "type": "core::starknet::contract_address::ContractAddress",
1280
+ "kind": "key"
1281
+ }
1282
+ ]
1283
+ },
1284
+ {
1285
+ "type": "event",
1286
+ "name": "medialane_marketplace_erc721::core::events::CounterIncremented",
1287
+ "kind": "struct",
1288
+ "members": [
1289
+ {
1290
+ "name": "offerer",
1291
+ "type": "core::starknet::contract_address::ContractAddress",
1292
+ "kind": "key"
1293
+ },
1294
+ {
1295
+ "name": "new_counter",
1296
+ "type": "core::felt252",
1297
+ "kind": "data"
1298
+ }
1299
+ ]
1300
+ },
1301
+ {
1302
+ "type": "event",
1303
+ "name": "medialane_marketplace_erc721::core::medialane::Medialane721::Event",
1304
+ "kind": "enum",
1305
+ "variants": [
1306
+ {
1307
+ "name": "OrderCreated",
1308
+ "type": "medialane_marketplace_erc721::core::events::OrderCreated",
1309
+ "kind": "nested"
1310
+ },
1311
+ {
1312
+ "name": "OrderFulfilled",
1313
+ "type": "medialane_marketplace_erc721::core::events::OrderFulfilled",
1314
+ "kind": "nested"
1315
+ },
1316
+ {
1317
+ "name": "OrderCancelled",
1318
+ "type": "medialane_marketplace_erc721::core::events::OrderCancelled",
1319
+ "kind": "nested"
1320
+ },
1321
+ {
1322
+ "name": "CounterIncremented",
1323
+ "type": "medialane_marketplace_erc721::core::events::CounterIncremented",
1324
+ "kind": "nested"
1325
+ }
1326
+ ]
1327
+ }
1328
+ ];
1329
+
1330
+ // src/starknet/abis/popCollection.ts
1331
+ var POPCollectionABI = [
1332
+ {
1333
+ type: "struct",
1334
+ name: "core::byte_array::ByteArray",
1335
+ members: [
1336
+ { name: "data", type: "core::array::Array::<core::felt252>" },
1337
+ { name: "pending_word", type: "core::felt252" },
1338
+ { name: "pending_word_len", type: "core::integer::u32" }
1339
+ ]
1340
+ },
1341
+ {
1342
+ type: "function",
1343
+ name: "claim",
1344
+ inputs: [],
1345
+ outputs: [],
1346
+ state_mutability: "external"
1347
+ },
1348
+ {
1349
+ type: "function",
1350
+ name: "admin_mint",
1351
+ inputs: [
1352
+ { name: "recipient", type: "core::starknet::contract_address::ContractAddress" },
1353
+ { name: "custom_uri", type: "core::byte_array::ByteArray" }
1354
+ ],
1355
+ outputs: [],
1356
+ state_mutability: "external"
1357
+ },
1358
+ {
1359
+ type: "function",
1360
+ name: "add_to_allowlist",
958
1361
  inputs: [{ name: "address", type: "core::starknet::contract_address::ContractAddress" }],
959
- outputs: [{ type: "core::bool" }],
960
- state_mutability: "view"
1362
+ outputs: [],
1363
+ state_mutability: "external"
961
1364
  },
962
1365
  {
963
1366
  type: "function",
964
- name: "set_base_uri",
965
- inputs: [{ name: "new_uri", type: "core::byte_array::ByteArray" }],
1367
+ name: "batch_add_to_allowlist",
1368
+ inputs: [{ name: "addresses", type: "core::array::Array::<core::starknet::contract_address::ContractAddress>" }],
1369
+ outputs: [],
1370
+ state_mutability: "external"
1371
+ },
1372
+ {
1373
+ type: "function",
1374
+ name: "remove_from_allowlist",
1375
+ inputs: [{ name: "address", type: "core::starknet::contract_address::ContractAddress" }],
966
1376
  outputs: [],
967
1377
  state_mutability: "external"
968
1378
  },
@@ -985,23 +1395,16 @@ var DropCollectionABI = [
985
1395
  },
986
1396
  {
987
1397
  type: "function",
988
- name: "withdraw_payments",
989
- inputs: [],
990
- outputs: [],
991
- state_mutability: "external"
992
- },
993
- {
994
- type: "function",
995
- name: "get_drop_id",
996
- inputs: [],
997
- outputs: [{ type: "core::integer::u256" }],
1398
+ name: "is_eligible",
1399
+ inputs: [{ name: "address", type: "core::starknet::contract_address::ContractAddress" }],
1400
+ outputs: [{ type: "core::bool" }],
998
1401
  state_mutability: "view"
999
1402
  },
1000
1403
  {
1001
1404
  type: "function",
1002
- name: "get_max_supply",
1003
- inputs: [],
1004
- outputs: [{ type: "core::integer::u256" }],
1405
+ name: "has_claimed",
1406
+ inputs: [{ name: "address", type: "core::starknet::contract_address::ContractAddress" }],
1407
+ outputs: [{ type: "core::bool" }],
1005
1408
  state_mutability: "view"
1006
1409
  },
1007
1410
  {
@@ -1010,32 +1413,68 @@ var DropCollectionABI = [
1010
1413
  inputs: [],
1011
1414
  outputs: [{ type: "core::integer::u256" }],
1012
1415
  state_mutability: "view"
1416
+ }
1417
+ ];
1418
+
1419
+ // src/starknet/abis/popFactory.ts
1420
+ var POPFactoryABI = [
1421
+ {
1422
+ type: "struct",
1423
+ name: "core::byte_array::ByteArray",
1424
+ members: [
1425
+ { name: "data", type: "core::array::Array::<core::felt252>" },
1426
+ { name: "pending_word", type: "core::felt252" },
1427
+ { name: "pending_word_len", type: "core::integer::u32" }
1428
+ ]
1429
+ },
1430
+ {
1431
+ type: "enum",
1432
+ name: "pop_protocol::types::EventType",
1433
+ variants: [
1434
+ { name: "Conference", type: "()" },
1435
+ { name: "Bootcamp", type: "()" },
1436
+ { name: "Workshop", type: "()" },
1437
+ { name: "Hackathon", type: "()" },
1438
+ { name: "Meetup", type: "()" },
1439
+ { name: "Course", type: "()" },
1440
+ { name: "Other", type: "()" }
1441
+ ]
1013
1442
  },
1014
1443
  {
1015
1444
  type: "function",
1016
- name: "remaining_supply",
1017
- inputs: [],
1018
- outputs: [{ type: "core::integer::u256" }],
1019
- state_mutability: "view"
1445
+ name: "create_collection",
1446
+ inputs: [
1447
+ { name: "name", type: "core::byte_array::ByteArray" },
1448
+ { name: "symbol", type: "core::byte_array::ByteArray" },
1449
+ { name: "base_uri", type: "core::byte_array::ByteArray" },
1450
+ { name: "claim_end_time", type: "core::integer::u64" },
1451
+ { name: "event_type", type: "pop_protocol::types::EventType" }
1452
+ ],
1453
+ outputs: [{ type: "core::starknet::contract_address::ContractAddress" }],
1454
+ state_mutability: "external"
1020
1455
  },
1021
1456
  {
1022
1457
  type: "function",
1023
- name: "minted_by_wallet",
1024
- inputs: [{ name: "wallet", type: "core::starknet::contract_address::ContractAddress" }],
1025
- outputs: [{ type: "core::integer::u256" }],
1026
- state_mutability: "view"
1458
+ name: "register_provider",
1459
+ inputs: [
1460
+ { name: "provider", type: "core::starknet::contract_address::ContractAddress" },
1461
+ { name: "name", type: "core::byte_array::ByteArray" },
1462
+ { name: "website_url", type: "core::byte_array::ByteArray" }
1463
+ ],
1464
+ outputs: [],
1465
+ state_mutability: "external"
1027
1466
  },
1028
1467
  {
1029
1468
  type: "function",
1030
- name: "is_paused",
1031
- inputs: [],
1032
- outputs: [{ type: "core::bool" }],
1033
- state_mutability: "view"
1469
+ name: "set_pop_collection_class_hash",
1470
+ inputs: [{ name: "new_class_hash", type: "core::starknet::class_hash::ClassHash" }],
1471
+ outputs: [],
1472
+ state_mutability: "external"
1034
1473
  }
1035
1474
  ];
1036
1475
 
1037
- // src/starknet/abis/dropFactory.ts
1038
- var DropFactoryABI = [
1476
+ // src/starknet/abis/dropCollection.ts
1477
+ var DropCollectionABI = [
1039
1478
  {
1040
1479
  type: "struct",
1041
1480
  name: "core::byte_array::ByteArray",
@@ -1058,33 +1497,204 @@ var DropFactoryABI = [
1058
1497
  },
1059
1498
  {
1060
1499
  type: "function",
1061
- name: "register_organizer",
1500
+ name: "claim",
1501
+ inputs: [{ name: "quantity", type: "core::integer::u256" }],
1502
+ outputs: [],
1503
+ state_mutability: "external"
1504
+ },
1505
+ {
1506
+ type: "function",
1507
+ name: "admin_mint",
1062
1508
  inputs: [
1063
- { name: "organizer", type: "core::starknet::contract_address::ContractAddress" },
1064
- { name: "name", type: "core::byte_array::ByteArray" }
1509
+ { name: "recipient", type: "core::starknet::contract_address::ContractAddress" },
1510
+ { name: "quantity", type: "core::integer::u256" },
1511
+ { name: "custom_uri", type: "core::byte_array::ByteArray" }
1065
1512
  ],
1066
1513
  outputs: [],
1067
1514
  state_mutability: "external"
1068
1515
  },
1069
1516
  {
1070
1517
  type: "function",
1071
- name: "revoke_organizer",
1072
- inputs: [{ name: "organizer", type: "core::starknet::contract_address::ContractAddress" }],
1518
+ name: "set_claim_conditions",
1519
+ inputs: [{ name: "conditions", type: "collection_drop::types::ClaimConditions" }],
1073
1520
  outputs: [],
1074
1521
  state_mutability: "external"
1075
1522
  },
1076
1523
  {
1077
1524
  type: "function",
1078
- name: "is_active_organizer",
1079
- inputs: [{ name: "organizer", type: "core::starknet::contract_address::ContractAddress" }],
1080
- outputs: [{ type: "core::bool" }],
1525
+ name: "get_claim_conditions",
1526
+ inputs: [],
1527
+ outputs: [{ type: "collection_drop::types::ClaimConditions" }],
1081
1528
  state_mutability: "view"
1082
1529
  },
1083
1530
  {
1084
1531
  type: "function",
1085
- name: "create_drop",
1086
- inputs: [
1087
- { name: "name", type: "core::byte_array::ByteArray" },
1532
+ name: "set_allowlist_enabled",
1533
+ inputs: [{ name: "enabled", type: "core::bool" }],
1534
+ outputs: [],
1535
+ state_mutability: "external"
1536
+ },
1537
+ {
1538
+ type: "function",
1539
+ name: "is_allowlist_enabled",
1540
+ inputs: [],
1541
+ outputs: [{ type: "core::bool" }],
1542
+ state_mutability: "view"
1543
+ },
1544
+ {
1545
+ type: "function",
1546
+ name: "add_to_allowlist",
1547
+ inputs: [{ name: "address", type: "core::starknet::contract_address::ContractAddress" }],
1548
+ outputs: [],
1549
+ state_mutability: "external"
1550
+ },
1551
+ {
1552
+ type: "function",
1553
+ name: "batch_add_to_allowlist",
1554
+ inputs: [{ name: "addresses", type: "core::array::Array::<core::starknet::contract_address::ContractAddress>" }],
1555
+ outputs: [],
1556
+ state_mutability: "external"
1557
+ },
1558
+ {
1559
+ type: "function",
1560
+ name: "remove_from_allowlist",
1561
+ inputs: [{ name: "address", type: "core::starknet::contract_address::ContractAddress" }],
1562
+ outputs: [],
1563
+ state_mutability: "external"
1564
+ },
1565
+ {
1566
+ type: "function",
1567
+ name: "is_allowlisted",
1568
+ inputs: [{ name: "address", type: "core::starknet::contract_address::ContractAddress" }],
1569
+ outputs: [{ type: "core::bool" }],
1570
+ state_mutability: "view"
1571
+ },
1572
+ {
1573
+ type: "function",
1574
+ name: "set_base_uri",
1575
+ inputs: [{ name: "new_uri", type: "core::byte_array::ByteArray" }],
1576
+ outputs: [],
1577
+ state_mutability: "external"
1578
+ },
1579
+ {
1580
+ type: "function",
1581
+ name: "set_token_uri",
1582
+ inputs: [
1583
+ { name: "token_id", type: "core::integer::u256" },
1584
+ { name: "uri", type: "core::byte_array::ByteArray" }
1585
+ ],
1586
+ outputs: [],
1587
+ state_mutability: "external"
1588
+ },
1589
+ {
1590
+ type: "function",
1591
+ name: "set_paused",
1592
+ inputs: [{ name: "paused", type: "core::bool" }],
1593
+ outputs: [],
1594
+ state_mutability: "external"
1595
+ },
1596
+ {
1597
+ type: "function",
1598
+ name: "withdraw_payments",
1599
+ inputs: [],
1600
+ outputs: [],
1601
+ state_mutability: "external"
1602
+ },
1603
+ {
1604
+ type: "function",
1605
+ name: "get_drop_id",
1606
+ inputs: [],
1607
+ outputs: [{ type: "core::integer::u256" }],
1608
+ state_mutability: "view"
1609
+ },
1610
+ {
1611
+ type: "function",
1612
+ name: "get_max_supply",
1613
+ inputs: [],
1614
+ outputs: [{ type: "core::integer::u256" }],
1615
+ state_mutability: "view"
1616
+ },
1617
+ {
1618
+ type: "function",
1619
+ name: "total_minted",
1620
+ inputs: [],
1621
+ outputs: [{ type: "core::integer::u256" }],
1622
+ state_mutability: "view"
1623
+ },
1624
+ {
1625
+ type: "function",
1626
+ name: "remaining_supply",
1627
+ inputs: [],
1628
+ outputs: [{ type: "core::integer::u256" }],
1629
+ state_mutability: "view"
1630
+ },
1631
+ {
1632
+ type: "function",
1633
+ name: "minted_by_wallet",
1634
+ inputs: [{ name: "wallet", type: "core::starknet::contract_address::ContractAddress" }],
1635
+ outputs: [{ type: "core::integer::u256" }],
1636
+ state_mutability: "view"
1637
+ },
1638
+ {
1639
+ type: "function",
1640
+ name: "is_paused",
1641
+ inputs: [],
1642
+ outputs: [{ type: "core::bool" }],
1643
+ state_mutability: "view"
1644
+ }
1645
+ ];
1646
+
1647
+ // src/starknet/abis/dropFactory.ts
1648
+ var DropFactoryABI = [
1649
+ {
1650
+ type: "struct",
1651
+ name: "core::byte_array::ByteArray",
1652
+ members: [
1653
+ { name: "data", type: "core::array::Array::<core::felt252>" },
1654
+ { name: "pending_word", type: "core::felt252" },
1655
+ { name: "pending_word_len", type: "core::integer::u32" }
1656
+ ]
1657
+ },
1658
+ {
1659
+ type: "struct",
1660
+ name: "collection_drop::types::ClaimConditions",
1661
+ members: [
1662
+ { name: "start_time", type: "core::integer::u64" },
1663
+ { name: "end_time", type: "core::integer::u64" },
1664
+ { name: "price", type: "core::integer::u256" },
1665
+ { name: "payment_token", type: "core::starknet::contract_address::ContractAddress" },
1666
+ { name: "max_quantity_per_wallet", type: "core::integer::u256" }
1667
+ ]
1668
+ },
1669
+ {
1670
+ type: "function",
1671
+ name: "register_organizer",
1672
+ inputs: [
1673
+ { name: "organizer", type: "core::starknet::contract_address::ContractAddress" },
1674
+ { name: "name", type: "core::byte_array::ByteArray" }
1675
+ ],
1676
+ outputs: [],
1677
+ state_mutability: "external"
1678
+ },
1679
+ {
1680
+ type: "function",
1681
+ name: "revoke_organizer",
1682
+ inputs: [{ name: "organizer", type: "core::starknet::contract_address::ContractAddress" }],
1683
+ outputs: [],
1684
+ state_mutability: "external"
1685
+ },
1686
+ {
1687
+ type: "function",
1688
+ name: "is_active_organizer",
1689
+ inputs: [{ name: "organizer", type: "core::starknet::contract_address::ContractAddress" }],
1690
+ outputs: [{ type: "core::bool" }],
1691
+ state_mutability: "view"
1692
+ },
1693
+ {
1694
+ type: "function",
1695
+ name: "create_drop",
1696
+ inputs: [
1697
+ { name: "name", type: "core::byte_array::ByteArray" },
1088
1698
  { name: "symbol", type: "core::byte_array::ByteArray" },
1089
1699
  { name: "base_uri", type: "core::byte_array::ByteArray" },
1090
1700
  { name: "max_supply", type: "core::integer::u256" },
@@ -10231,1715 +10841,50 @@ var IPGenesisABI = [
10231
10841
  {
10232
10842
  "name": "CounterIncremented",
10233
10843
  "type": "mip::mip::MIP::CounterComponent::CounterIncremented",
10234
- "kind": "nested"
10235
- },
10236
- {
10237
- "name": "CounterDecremented",
10238
- "type": "mip::mip::MIP::CounterComponent::CounterDecremented",
10239
- "kind": "nested"
10240
- }
10241
- ]
10242
- },
10243
- {
10244
- "type": "event",
10245
- "name": "mip::mip::MIP::Event",
10246
- "kind": "enum",
10247
- "variants": [
10248
- {
10249
- "name": "ERC721Event",
10250
- "type": "openzeppelin_token::erc721::erc721::ERC721Component::Event",
10251
- "kind": "flat"
10252
- },
10253
- {
10254
- "name": "OwnableEvent",
10255
- "type": "openzeppelin_access::ownable::ownable::OwnableComponent::Event",
10256
- "kind": "flat"
10257
- },
10258
- {
10259
- "name": "SRC5Event",
10260
- "type": "openzeppelin_introspection::src5::SRC5Component::Event",
10261
- "kind": "flat"
10262
- },
10263
- {
10264
- "name": "ERC721EnumerableEvent",
10265
- "type": "openzeppelin_token::erc721::extensions::erc721_enumerable::erc721_enumerable::ERC721EnumerableComponent::Event",
10266
- "kind": "flat"
10267
- },
10268
- {
10269
- "name": "CounterEvent",
10270
- "type": "mip::mip::MIP::CounterComponent::Event",
10271
- "kind": "flat"
10272
- }
10273
- ]
10274
- }
10275
- ];
10276
-
10277
- // src/constants.ts
10278
- var SN = getCoordinates("STARKNET");
10279
- SN.marketplace721;
10280
- SN.marketplace721ClassHash;
10281
- SN.marketplace721StartBlock;
10282
- SN.marketplace1155;
10283
- SN.marketplace1155ClassHash;
10284
- SN.marketplace1155StartBlock;
10285
- SN.collection721;
10286
- SN.collection721StartBlock;
10287
- SN.ipNftClassHash;
10288
- SN.ipCollectionClassHash;
10289
- SN.collection1155;
10290
- SN.collection1155FactoryClassHash;
10291
- SN.collection1155ClassHash;
10292
- SN.collection1155StartBlock;
10293
- SN.popFactory;
10294
- SN.popCollectionClassHash;
10295
- SN.dropFactory;
10296
- SN.dropCollectionClassHash;
10297
- SN.nftComments;
10298
- SN.ipTicketsFactory;
10299
- SN.ipTicketCollectionClassHash;
10300
- SN.ipClubRegistry;
10301
- SN.ipClubNftClassHash;
10302
- SN.ipClubFactory;
10303
- SN.ipClubCollectionClassHash;
10304
- SN.ipSponsorship;
10305
- SN.ipSponsorshipLicense;
10306
- SN.creatorCoinFactory;
10307
- SN.creatorCoinEkuboLauncher;
10308
- SN.creatorCoinClassHash;
10309
- SN.creatorCoinFactoryClassHash;
10310
- SN.creatorCoinStartBlock;
10311
- SN.ekuboCore;
10312
- var SUPPORTED_TOKENS = [
10313
- {
10314
- // Circle-native USDC on Starknet (canonical)
10315
- symbol: "USDC",
10316
- address: "0x033068f6539f8e6e6b131e6b2b814e6c34a5224bc66947c47dab9dfee93b35fb",
10317
- decimals: 6,
10318
- listable: true
10319
- },
10320
- {
10321
- symbol: "USDT",
10322
- address: "0x068f5c6a61780768455de69077e07e89787839bf8166decfbf92b645209c0fb8",
10323
- decimals: 6,
10324
- listable: true
10325
- },
10326
- {
10327
- symbol: "ETH",
10328
- address: "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7",
10329
- decimals: 18,
10330
- listable: true
10331
- },
10332
- {
10333
- symbol: "STRK",
10334
- address: "0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d",
10335
- decimals: 18,
10336
- listable: true
10337
- },
10338
- {
10339
- symbol: "WBTC",
10340
- address: "0x03fe2b97c1fd336e750087d68b9b867997fd64a2661ff3ca5a7c771641e8e7ac",
10341
- decimals: 8,
10342
- listable: true
10343
- }
10344
- ];
10345
- var DEFAULT_CURRENCY = "USDC";
10346
-
10347
- // src/utils/bigint.ts
10348
- function stringifyBigInts(obj) {
10349
- if (typeof obj === "bigint") {
10350
- return obj.toString();
10351
- }
10352
- if (Array.isArray(obj)) {
10353
- return obj.map(stringifyBigInts);
10354
- }
10355
- if (obj !== null && typeof obj === "object") {
10356
- return Object.fromEntries(
10357
- Object.entries(obj).map(([key, value]) => [
10358
- key,
10359
- stringifyBigInts(value)
10360
- ])
10361
- );
10362
- }
10363
- return obj;
10364
- }
10365
-
10366
- // src/utils/token.ts
10367
- function parseAmount(human, decimals) {
10368
- const [whole, frac = ""] = human.split(".");
10369
- const fracPadded = frac.padEnd(decimals, "0").slice(0, decimals);
10370
- return (BigInt(whole) * BigInt(10) ** BigInt(decimals) + BigInt(fracPadded)).toString();
10371
- }
10372
- function formatAmount(raw, decimals) {
10373
- const value = BigInt(raw);
10374
- const factor = BigInt(Math.pow(10, decimals));
10375
- const whole = value / factor;
10376
- const remainder = value % factor;
10377
- const fractional = remainder.toString().padStart(decimals, "0");
10378
- return `${whole}.${fractional}`;
10379
- }
10380
- function getTokenByAddress(address) {
10381
- const lower = address.toLowerCase();
10382
- return SUPPORTED_TOKENS.find((t) => t.address.toLowerCase() === lower);
10383
- }
10384
-
10385
- // src/starknet/marketplace/errors.ts
10386
- var MedialaneError = class extends Error {
10387
- constructor(message, code = "UNKNOWN", cause) {
10388
- super(message);
10389
- this.code = code;
10390
- this.cause = cause;
10391
- this.name = "MedialaneError";
10392
- }
10393
- };
10394
- function buildFeeCall(p, cfg) {
10395
- if (!cfg.enabled || !cfg.fundAddress) return null;
10396
- const bps = p.surface === "marketplace" ? cfg.marketplaceBps : cfg.launchpadBps;
10397
- if (bps <= 0) return null;
10398
- const fee = p.grossAmount * BigInt(bps) / 10000n;
10399
- if (fee <= 0n) return null;
10400
- const u = starknet.cairo.uint256(fee.toString());
10401
- return {
10402
- contractAddress: p.token,
10403
- entrypoint: "transfer",
10404
- calldata: [cfg.fundAddress, u.low.toString(), u.high.toString()]
10405
- };
10406
- }
10407
-
10408
- // src/utils/rpc.ts
10409
- var PUBLIC_RPC_FALLBACKS = [
10410
- "https://rpc.starknet.lava.build"
10411
- ];
10412
- var TRANSIENT_BODY_RE = /"code"\s*:\s*-32001|"code"\s*:\s*-32603|unable to complete|rate.?limit|too many|throttl|exceed.*quota|temporarily unavailable|service unavailable|overload|gateway.*time|upstream.*time|backend.*error/i;
10413
- function isTransientRpcError(input) {
10414
- const { status, body } = input;
10415
- if (typeof status === "number" && (status === 429 || status >= 500)) return true;
10416
- if (body == null) return false;
10417
- if (typeof body === "object") {
10418
- const err = body.error;
10419
- if (!err || typeof err !== "object") return false;
10420
- const code = err.code;
10421
- if (typeof code === "number") {
10422
- if (code === 429) return true;
10423
- if (code >= -32099 && code <= -32e3) return true;
10424
- if (code === -32603) return true;
10425
- }
10426
- const message = err.message;
10427
- return typeof message === "string" ? TRANSIENT_BODY_RE.test(message) : false;
10428
- }
10429
- return TRANSIENT_BODY_RE.test(String(body));
10430
- }
10431
- function createFailoverFetch(urls, options = {}) {
10432
- const endpoints = urls.filter((u) => Boolean(u));
10433
- if (endpoints.length === 0) {
10434
- throw new Error("createFailoverFetch: at least one RPC URL is required");
10435
- }
10436
- const doFetch = options.baseFetch ?? fetch;
10437
- const failover = async (_input, init) => {
10438
- let lastError;
10439
- for (let i = 0; i < endpoints.length; i++) {
10440
- const url = endpoints[i];
10441
- const isLast = i === endpoints.length - 1;
10442
- try {
10443
- const res = await doFetch(url, init);
10444
- const text = await res.text();
10445
- const rebuilt = () => new Response(text, { status: res.status, statusText: res.statusText, headers: res.headers });
10446
- if (isLast || !isTransientRpcError({ status: res.status, body: text })) {
10447
- return rebuilt();
10448
- }
10449
- options.onFailover?.({ url, status: res.status });
10450
- } catch (err) {
10451
- lastError = err;
10452
- if (isLast) throw err;
10453
- options.onFailover?.({ url, error: err });
10454
- }
10455
- }
10456
- throw lastError ?? new Error("createFailoverFetch: all endpoints failed");
10457
- };
10458
- return failover;
10459
- }
10460
-
10461
- // src/starknet/marketplace/utils.ts
10462
- var START_TIME_BUFFER_SECS = 30;
10463
- function generateSalt() {
10464
- const bytes = new Uint8Array(31);
10465
- crypto.getRandomValues(bytes);
10466
- const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
10467
- return starknet.num.toHex(BigInt("0x" + hex));
10468
- }
10469
- async function resolveRoyaltyMaxBps(provider, nft, tokenId, override) {
10470
- if (override !== void 0) return override;
10471
- try {
10472
- const id = starknet.cairo.uint256(tokenId);
10473
- const res = await provider.callContract({
10474
- contractAddress: nft,
10475
- entrypoint: "royalty_info",
10476
- calldata: [id.low.toString(), id.high.toString(), "10000", "0"]
10477
- });
10478
- return BigInt(res[1] ?? "0").toString();
10479
- } catch {
10480
- return "0";
10481
- }
10482
- }
10483
- function toSignatureArray(sig) {
10484
- if (Array.isArray(sig)) return sig;
10485
- const s = sig;
10486
- return [s.r.toString(), s.s.toString()];
10487
- }
10488
- function getChainId(config) {
10489
- if (config.chain !== "STARKNET") {
10490
- throw new Error(`SNIP-12 signing is Starknet-only; got chain "${config.chain}"`);
10491
- }
10492
- return starknet.constants.StarknetChainId.SN_MAIN;
10493
- }
10494
- function resolveToken(currency) {
10495
- const token = SUPPORTED_TOKENS.find(
10496
- (t) => t.symbol === currency.toUpperCase() || t.address.toLowerCase() === currency.toLowerCase()
10497
- );
10498
- if (!token) throw new MedialaneError(`Unsupported currency: ${currency}`, "INVALID_PARAMS");
10499
- return token;
10500
- }
10501
- var _providerCache = /* @__PURE__ */ new WeakMap();
10502
- function getProvider(config) {
10503
- let p = _providerCache.get(config);
10504
- if (!p) {
10505
- const urls = Array.from(/* @__PURE__ */ new Set([config.rpcUrl, ...PUBLIC_RPC_FALLBACKS]));
10506
- p = new starknet.RpcProvider({ nodeUrl: urls[0], baseFetch: createFailoverFetch(urls) });
10507
- _providerCache.set(config, p);
10508
- }
10509
- return p;
10510
- }
10511
-
10512
- // src/starknet/marketplace/orders.ts
10513
- var _contractCache = /* @__PURE__ */ new WeakMap();
10514
- function makeContract(config) {
10515
- const cached = _contractCache.get(config);
10516
- const provider = getProvider(config);
10517
- if (cached) return { ...cached, provider };
10518
- const contract = new starknet.Contract(
10519
- IPMarketplaceABI,
10520
- config.marketplaceContract,
10521
- provider
10522
- );
10523
- _contractCache.set(config, { contract });
10524
- return { contract, provider };
10525
- }
10526
- async function createListing(account, params, config) {
10527
- const { nftContract, tokenId, price, currency = DEFAULT_CURRENCY, durationSeconds } = params;
10528
- const { contract, provider } = makeContract(config);
10529
- const token = resolveToken(currency);
10530
- const priceWei = parseAmount(price, token.decimals);
10531
- const now = Math.floor(Date.now() / 1e3);
10532
- const startTime = now + START_TIME_BUFFER_SECS;
10533
- const endTime = now + durationSeconds;
10534
- const counter = (await contract.get_counter(account.address)).toString();
10535
- const royaltyMaxBps = await resolveRoyaltyMaxBps(provider, nftContract, tokenId, params.royaltyMaxBps);
10536
- const orderParams = {
10537
- offerer: account.address,
10538
- marketplace: config.marketplaceContract,
10539
- offer: {
10540
- item_type: "ERC721",
10541
- token: nftContract,
10542
- identifier_or_criteria: tokenId,
10543
- amount: "1"
10544
- },
10545
- consideration: {
10546
- item_type: "ERC20",
10547
- token: token.address,
10548
- identifier_or_criteria: "0",
10549
- amount: priceWei,
10550
- recipient: account.address
10551
- },
10552
- royalty_max_bps: royaltyMaxBps,
10553
- start_time: startTime.toString(),
10554
- end_time: endTime.toString(),
10555
- salt: generateSalt(),
10556
- counter
10557
- };
10558
- const chainId = getChainId(config);
10559
- const typedData = stringifyBigInts(buildOrderTypedData(orderParams, chainId));
10560
- const signature = await account.signMessage(typedData);
10561
- const signatureArray = toSignatureArray(signature);
10562
- const registerPayload = stringifyBigInts({
10563
- parameters: {
10564
- ...orderParams,
10565
- offer: {
10566
- ...orderParams.offer,
10567
- item_type: starknet.shortString.encodeShortString(orderParams.offer.item_type)
10568
- },
10569
- consideration: {
10570
- ...orderParams.consideration,
10571
- item_type: starknet.shortString.encodeShortString(orderParams.consideration.item_type)
10572
- }
10573
- },
10574
- signature: signatureArray
10575
- });
10576
- const tokenIdUint256 = starknet.cairo.uint256(tokenId);
10577
- let isAlreadyApproved = false;
10578
- try {
10579
- const result = await provider.callContract({
10580
- contractAddress: nftContract,
10581
- entrypoint: "get_approved",
10582
- calldata: [tokenIdUint256.low.toString(), tokenIdUint256.high.toString()]
10583
- });
10584
- isAlreadyApproved = BigInt(result[0]).toString() === BigInt(config.marketplaceContract).toString();
10585
- } catch {
10586
- }
10587
- const registerCall = contract.populate("register_order", [registerPayload]);
10588
- const calls = isAlreadyApproved ? [registerCall] : [
10589
- {
10590
- contractAddress: nftContract,
10591
- entrypoint: "approve",
10592
- calldata: [
10593
- config.marketplaceContract,
10594
- tokenIdUint256.low.toString(),
10595
- tokenIdUint256.high.toString()
10596
- ]
10597
- },
10598
- registerCall
10599
- ];
10600
- try {
10601
- const tx = await account.execute(calls);
10602
- await provider.waitForTransaction(tx.transaction_hash);
10603
- return { txHash: tx.transaction_hash };
10604
- } catch (err) {
10605
- throw new MedialaneError("Failed to create listing", "TRANSACTION_FAILED", err);
10606
- }
10607
- }
10608
- async function makeOffer(account, params, config) {
10609
- const { nftContract, tokenId, price, currency = DEFAULT_CURRENCY, durationSeconds } = params;
10610
- const { contract, provider } = makeContract(config);
10611
- const token = resolveToken(currency);
10612
- const priceWei = parseAmount(price, token.decimals);
10613
- const now = Math.floor(Date.now() / 1e3);
10614
- const startTime = now + START_TIME_BUFFER_SECS;
10615
- const endTime = now + durationSeconds;
10616
- const counter = (await contract.get_counter(account.address)).toString();
10617
- const royaltyMaxBps = await resolveRoyaltyMaxBps(provider, nftContract, tokenId, params.royaltyMaxBps);
10618
- const orderParams = {
10619
- offerer: account.address,
10620
- marketplace: config.marketplaceContract,
10621
- offer: {
10622
- item_type: "ERC20",
10623
- token: token.address,
10624
- identifier_or_criteria: "0",
10625
- amount: priceWei
10626
- },
10627
- consideration: {
10628
- item_type: "ERC721",
10629
- token: nftContract,
10630
- identifier_or_criteria: tokenId,
10631
- amount: "1",
10632
- recipient: account.address
10633
- },
10634
- royalty_max_bps: royaltyMaxBps,
10635
- start_time: startTime.toString(),
10636
- end_time: endTime.toString(),
10637
- salt: generateSalt(),
10638
- counter
10639
- };
10640
- const chainId = getChainId(config);
10641
- const typedData = stringifyBigInts(buildOrderTypedData(orderParams, chainId));
10642
- const signature = await account.signMessage(typedData);
10643
- const signatureArray = toSignatureArray(signature);
10644
- const registerPayload = stringifyBigInts({
10645
- parameters: {
10646
- ...orderParams,
10647
- offer: {
10648
- ...orderParams.offer,
10649
- item_type: starknet.shortString.encodeShortString(orderParams.offer.item_type)
10650
- },
10651
- consideration: {
10652
- ...orderParams.consideration,
10653
- item_type: starknet.shortString.encodeShortString(orderParams.consideration.item_type)
10654
- }
10655
- },
10656
- signature: signatureArray
10657
- });
10658
- const amountUint256 = starknet.cairo.uint256(priceWei);
10659
- const approveCall = {
10660
- contractAddress: token.address,
10661
- entrypoint: "approve",
10662
- calldata: [
10663
- config.marketplaceContract,
10664
- amountUint256.low.toString(),
10665
- amountUint256.high.toString()
10666
- ]
10667
- };
10668
- const registerCall = contract.populate("register_order", [registerPayload]);
10669
- try {
10670
- const tx = await account.execute([approveCall, registerCall]);
10671
- await provider.waitForTransaction(tx.transaction_hash);
10672
- return { txHash: tx.transaction_hash };
10673
- } catch (err) {
10674
- throw new MedialaneError("Failed to make offer", "TRANSACTION_FAILED", err);
10675
- }
10676
- }
10677
- async function fulfillOrder(account, params, config) {
10678
- const { orderHash, paymentToken, totalPrice } = params;
10679
- const { contract, provider } = makeContract(config);
10680
- const totalPriceU256 = starknet.cairo.uint256(totalPrice);
10681
- const approveCall = {
10682
- contractAddress: paymentToken,
10683
- entrypoint: "approve",
10684
- calldata: [
10685
- config.marketplaceContract,
10686
- totalPriceU256.low.toString(),
10687
- totalPriceU256.high.toString()
10688
- ]
10689
- };
10690
- const fulfillCall = contract.populate("fulfill_order", [orderHash]);
10691
- const feeCall = buildFeeCall(
10692
- { surface: "marketplace", token: paymentToken, grossAmount: BigInt(totalPrice) },
10693
- config.feeConfig
10694
- );
10695
- const calls = feeCall ? [approveCall, fulfillCall, feeCall] : [approveCall, fulfillCall];
10696
- try {
10697
- const tx = await account.execute(calls);
10698
- await provider.waitForTransaction(tx.transaction_hash);
10699
- return { txHash: tx.transaction_hash };
10700
- } catch (err) {
10701
- throw new MedialaneError("Failed to fulfill order", "TRANSACTION_FAILED", err);
10702
- }
10703
- }
10704
- async function cancelOrder(account, params, config) {
10705
- const { orderHash } = params;
10706
- const { contract, provider } = makeContract(config);
10707
- const chainId = getChainId(config);
10708
- const cancelParams = {
10709
- order_hash: orderHash,
10710
- offerer: account.address
10711
- };
10712
- const typedData = stringifyBigInts(
10713
- buildCancellationTypedData(cancelParams, chainId)
10714
- );
10715
- const signature = await account.signMessage(typedData);
10716
- const signatureArray = toSignatureArray(signature);
10717
- const cancelRequest = stringifyBigInts({
10718
- cancelation: cancelParams,
10719
- signature: signatureArray
10720
- });
10721
- const call = contract.populate("cancel_order", [cancelRequest]);
10722
- try {
10723
- const tx = await account.execute(call);
10724
- await provider.waitForTransaction(tx.transaction_hash);
10725
- return { txHash: tx.transaction_hash };
10726
- } catch (err) {
10727
- throw new MedialaneError("Failed to cancel order", "TRANSACTION_FAILED", err);
10728
- }
10729
- }
10730
- async function mint(account, params, config) {
10731
- const { collectionId, recipient, tokenUri, royaltyBps, collectionContract } = params;
10732
- const provider = getProvider(config);
10733
- const contractAddress = collectionContract ?? config.collectionContract;
10734
- const id = starknet.cairo.uint256(collectionId);
10735
- const calldata = [
10736
- id.low.toString(),
10737
- id.high.toString(),
10738
- recipient,
10739
- ...encodeByteArray(tokenUri),
10740
- royaltyBps.toString()
10741
- ];
10742
- try {
10743
- const tx = await account.execute([{ contractAddress, entrypoint: "mint", calldata }]);
10744
- await provider.waitForTransaction(tx.transaction_hash);
10745
- return { txHash: tx.transaction_hash };
10746
- } catch (err) {
10747
- throw new MedialaneError("Failed to mint NFT", "TRANSACTION_FAILED", err);
10748
- }
10749
- }
10750
- async function createCollection(account, params, config) {
10751
- const { name, symbol, baseUri, collectionContract } = params;
10752
- const provider = getProvider(config);
10753
- const contractAddress = collectionContract ?? config.collectionContract;
10754
- const calldata = [
10755
- ...encodeByteArray(name),
10756
- ...encodeByteArray(symbol),
10757
- ...encodeByteArray(baseUri)
10758
- ];
10759
- try {
10760
- const tx = await account.execute([{ contractAddress, entrypoint: "create_collection", calldata }]);
10761
- await provider.waitForTransaction(tx.transaction_hash);
10762
- return { txHash: tx.transaction_hash };
10763
- } catch (err) {
10764
- throw new MedialaneError("Failed to create collection", "TRANSACTION_FAILED", err);
10765
- }
10766
- }
10767
- async function checkoutCart(account, items, config) {
10768
- if (items.length === 0) throw new MedialaneError("Cart is empty", "INVALID_PARAMS");
10769
- const { contract, provider } = makeContract(config);
10770
- const tokenTotals = /* @__PURE__ */ new Map();
10771
- for (const item of items) {
10772
- const prev = tokenTotals.get(item.considerationToken) ?? 0n;
10773
- tokenTotals.set(item.considerationToken, prev + BigInt(item.considerationAmount));
10774
- }
10775
- const approveCalls = Array.from(tokenTotals.entries()).map(([tokenAddr, totalWei]) => {
10776
- const amount = starknet.cairo.uint256(totalWei.toString());
10777
- return {
10778
- contractAddress: tokenAddr,
10779
- entrypoint: "approve",
10780
- calldata: [
10781
- config.marketplaceContract,
10782
- amount.low.toString(),
10783
- amount.high.toString()
10784
- ]
10785
- };
10786
- });
10787
- const fulfillCalls = items.map(
10788
- (item) => contract.populate("fulfill_order", [item.orderHash])
10789
- );
10790
- const feeCalls = Array.from(tokenTotals.entries()).map(
10791
- ([tokenAddr, totalWei]) => buildFeeCall(
10792
- { surface: "marketplace", token: tokenAddr, grossAmount: totalWei },
10793
- config.feeConfig
10794
- )
10795
- ).filter((c) => c !== null);
10796
- try {
10797
- const tx = await account.execute([...approveCalls, ...fulfillCalls, ...feeCalls]);
10798
- await provider.waitForTransaction(tx.transaction_hash);
10799
- return { txHash: tx.transaction_hash };
10800
- } catch (err) {
10801
- throw new MedialaneError("Cart checkout failed", "TRANSACTION_FAILED", err);
10802
- }
10803
- }
10804
- async function getOrderDetails(orderHash, config) {
10805
- const { contract } = makeContract(config);
10806
- return contract.get_order_details(orderHash);
10807
- }
10808
- async function getCounter(address, config) {
10809
- const { contract } = makeContract(config);
10810
- return BigInt((await contract.get_counter(address)).toString());
10811
- }
10812
- async function incrementCounter(account, config) {
10813
- const { contract, provider } = makeContract(config);
10814
- const call = contract.populate("increment_counter", []);
10815
- try {
10816
- const tx = await account.execute(call);
10817
- await provider.waitForTransaction(tx.transaction_hash);
10818
- return { txHash: tx.transaction_hash };
10819
- } catch (err) {
10820
- throw new MedialaneError("Failed to increment counter", "TRANSACTION_FAILED", err);
10821
- }
10822
- }
10823
-
10824
- // src/starknet/marketplace/index.ts
10825
- var MarketplaceModule = class {
10826
- constructor(config) {
10827
- this.config = config;
10828
- }
10829
- // ─── Writes ───────────────────────────────────────────────────────────────
10830
- createListing(account, params) {
10831
- return createListing(account, params, this.config);
10832
- }
10833
- makeOffer(account, params) {
10834
- return makeOffer(account, params, this.config);
10835
- }
10836
- fulfillOrder(account, params) {
10837
- return fulfillOrder(account, params, this.config);
10838
- }
10839
- cancelOrder(account, params) {
10840
- return cancelOrder(account, params, this.config);
10841
- }
10842
- checkoutCart(account, items) {
10843
- return checkoutCart(account, items, this.config);
10844
- }
10845
- mint(account, params) {
10846
- return mint(account, params, this.config);
10847
- }
10848
- createCollection(account, params) {
10849
- return createCollection(account, params, this.config);
10850
- }
10851
- /** Bulk-cancel: bump the caller's counter, invalidating all their open orders. */
10852
- incrementCounter(account) {
10853
- return incrementCounter(account, this.config);
10854
- }
10855
- // ─── View calls ───────────────────────────────────────────────────────────
10856
- getOrderDetails(orderHash) {
10857
- return getOrderDetails(orderHash, this.config);
10858
- }
10859
- getCounter(address) {
10860
- return getCounter(address, this.config);
10861
- }
10862
- // ─── Typed data builders (for ChipiPay / custom signing flows) ───────────
10863
- buildListingTypedData(params, chainId) {
10864
- return buildOrderTypedData(params, chainId);
10865
- }
10866
- buildCancellationTypedData(params, chainId) {
10867
- return buildCancellationTypedData(params, chainId);
10868
- }
10869
- };
10870
- var _contractCache2 = /* @__PURE__ */ new WeakMap();
10871
- function getContract(config) {
10872
- let c = _contractCache2.get(config);
10873
- if (!c) {
10874
- const provider = getProvider(config);
10875
- c = new starknet.Contract(
10876
- Medialane1155ABI,
10877
- config.marketplace1155Contract,
10878
- provider
10879
- );
10880
- _contractCache2.set(config, c);
10881
- }
10882
- return c;
10883
- }
10884
- async function createListing1155(account, params, config) {
10885
- const {
10886
- nftContract,
10887
- tokenId,
10888
- amount,
10889
- pricePerUnit,
10890
- currency = DEFAULT_CURRENCY,
10891
- durationSeconds
10892
- } = params;
10893
- const contract = getContract(config);
10894
- const provider = getProvider(config);
10895
- const token = resolveToken(currency);
10896
- const priceWei = parseAmount(pricePerUnit, token.decimals);
10897
- const now = Math.floor(Date.now() / 1e3);
10898
- const endTime = now + durationSeconds;
10899
- const chainId = getChainId(config);
10900
- const counter = (await contract.get_counter(account.address)).toString();
10901
- const royaltyMaxBps = await resolveRoyaltyMaxBps(provider, nftContract, tokenId, params.royaltyMaxBps);
10902
- const orderParams = {
10903
- offerer: account.address,
10904
- marketplace: config.marketplace1155Contract,
10905
- offer: {
10906
- item_type: "ERC1155",
10907
- token: nftContract,
10908
- identifier_or_criteria: tokenId,
10909
- amount
10910
- // ERC-1155 leg amount = unit quantity
10911
- },
10912
- consideration: {
10913
- item_type: "ERC20",
10914
- token: token.address,
10915
- identifier_or_criteria: "0",
10916
- amount: priceWei,
10917
- // payment leg amount = price PER UNIT
10918
- recipient: account.address
10919
- },
10920
- royalty_max_bps: royaltyMaxBps,
10921
- start_time: (now + START_TIME_BUFFER_SECS).toString(),
10922
- end_time: endTime.toString(),
10923
- salt: generateSalt(),
10924
- counter
10925
- };
10926
- const typedData = stringifyBigInts(
10927
- build1155OrderTypedData(orderParams, chainId)
10928
- );
10929
- const signature = await account.signMessage(typedData);
10930
- const signatureArray = toSignatureArray(signature);
10931
- const orderPayload = stringifyBigInts({
10932
- parameters: {
10933
- ...orderParams,
10934
- offer: {
10935
- ...orderParams.offer,
10936
- item_type: starknet.shortString.encodeShortString(orderParams.offer.item_type)
10937
- },
10938
- consideration: {
10939
- ...orderParams.consideration,
10940
- item_type: starknet.shortString.encodeShortString(orderParams.consideration.item_type)
10941
- }
10942
- },
10943
- signature: signatureArray
10944
- });
10945
- let isApproved = false;
10946
- try {
10947
- const result = await provider.callContract({
10948
- contractAddress: nftContract,
10949
- entrypoint: "is_approved_for_all",
10950
- calldata: [account.address, config.marketplace1155Contract]
10951
- });
10952
- isApproved = BigInt(result[0]) === 1n;
10953
- } catch {
10954
- }
10955
- const registerCall = contract.populate("register_order", [orderPayload]);
10956
- const calls = isApproved ? [registerCall] : [
10957
- {
10958
- contractAddress: nftContract,
10959
- entrypoint: "set_approval_for_all",
10960
- calldata: [config.marketplace1155Contract, "1"]
10961
- },
10962
- registerCall
10963
- ];
10964
- try {
10965
- const tx = await account.execute(calls);
10966
- await provider.waitForTransaction(tx.transaction_hash);
10967
- return { txHash: tx.transaction_hash };
10968
- } catch (err) {
10969
- throw new MedialaneError("Failed to create ERC-1155 listing", "TRANSACTION_FAILED", err);
10970
- }
10971
- }
10972
- async function fulfillOrder1155(account, params, config) {
10973
- const { orderHash, paymentToken, totalPrice, quantity = "1" } = params;
10974
- const contract = getContract(config);
10975
- const provider = getProvider(config);
10976
- const totalPriceU256 = starknet.cairo.uint256(totalPrice);
10977
- const approveCall = {
10978
- contractAddress: paymentToken,
10979
- entrypoint: "approve",
10980
- calldata: [
10981
- config.marketplace1155Contract,
10982
- totalPriceU256.low.toString(),
10983
- totalPriceU256.high.toString()
10984
- ]
10985
- };
10986
- const fulfillCall = contract.populate("fulfill_order", [orderHash, quantity]);
10987
- try {
10988
- const tx = await account.execute([approveCall, fulfillCall]);
10989
- await provider.waitForTransaction(tx.transaction_hash);
10990
- return { txHash: tx.transaction_hash };
10991
- } catch (err) {
10992
- throw new MedialaneError("Failed to fulfill ERC-1155 order", "TRANSACTION_FAILED", err);
10993
- }
10994
- }
10995
- async function cancelOrder1155(account, params, config) {
10996
- const { orderHash } = params;
10997
- const contract = getContract(config);
10998
- const provider = getProvider(config);
10999
- const chainId = getChainId(config);
11000
- const cancelParams = {
11001
- order_hash: orderHash,
11002
- offerer: account.address
11003
- };
11004
- const typedData = stringifyBigInts(
11005
- build1155CancellationTypedData(cancelParams, chainId)
11006
- );
11007
- const signature = await account.signMessage(typedData);
11008
- const signatureArray = toSignatureArray(signature);
11009
- const cancelPayload = stringifyBigInts({
11010
- cancelation: cancelParams,
11011
- signature: signatureArray
11012
- });
11013
- const cancelCall = contract.populate("cancel_order", [cancelPayload]);
11014
- try {
11015
- const tx = await account.execute(cancelCall);
11016
- await provider.waitForTransaction(tx.transaction_hash);
11017
- return { txHash: tx.transaction_hash };
11018
- } catch (err) {
11019
- throw new MedialaneError("Failed to cancel ERC-1155 order", "TRANSACTION_FAILED", err);
11020
- }
11021
- }
11022
- async function makeOffer1155(account, params, config) {
11023
- const {
11024
- nftContract,
11025
- tokenId,
11026
- amount,
11027
- price,
11028
- currency = DEFAULT_CURRENCY,
11029
- durationSeconds
11030
- } = params;
11031
- const contract = getContract(config);
11032
- const provider = getProvider(config);
11033
- const chainId = getChainId(config);
11034
- const token = resolveToken(currency);
11035
- const priceWei = parseAmount(price, token.decimals);
11036
- const now = Math.floor(Date.now() / 1e3);
11037
- const endTime = now + durationSeconds;
11038
- const counter = (await contract.get_counter(account.address)).toString();
11039
- const royaltyMaxBps = await resolveRoyaltyMaxBps(provider, nftContract, tokenId, params.royaltyMaxBps);
11040
- const orderParams = {
11041
- offerer: account.address,
11042
- marketplace: config.marketplace1155Contract,
11043
- offer: {
11044
- item_type: "ERC20",
11045
- token: token.address,
11046
- identifier_or_criteria: "0",
11047
- amount: priceWei
11048
- // price PER UNIT
11049
- },
11050
- consideration: {
11051
- item_type: "ERC1155",
11052
- token: nftContract,
11053
- identifier_or_criteria: tokenId,
11054
- amount,
11055
- // unit quantity
11056
- recipient: account.address
11057
- },
11058
- royalty_max_bps: royaltyMaxBps,
11059
- start_time: (now + START_TIME_BUFFER_SECS).toString(),
11060
- end_time: endTime.toString(),
11061
- salt: generateSalt(),
11062
- counter
11063
- };
11064
- const typedData = stringifyBigInts(
11065
- build1155OrderTypedData(orderParams, chainId)
11066
- );
11067
- const signature = await account.signMessage(typedData);
11068
- const signatureArray = toSignatureArray(signature);
11069
- const registerPayload = stringifyBigInts({
11070
- parameters: {
11071
- ...orderParams,
11072
- offer: {
11073
- ...orderParams.offer,
11074
- item_type: starknet.shortString.encodeShortString(orderParams.offer.item_type)
11075
- },
11076
- consideration: {
11077
- ...orderParams.consideration,
11078
- item_type: starknet.shortString.encodeShortString(orderParams.consideration.item_type)
11079
- }
11080
- },
11081
- signature: signatureArray
11082
- });
11083
- const totalWei = BigInt(priceWei) * BigInt(amount);
11084
- const amountU256 = starknet.cairo.uint256(totalWei.toString());
11085
- const approveCall = {
11086
- contractAddress: token.address,
11087
- entrypoint: "approve",
11088
- calldata: [
11089
- config.marketplace1155Contract,
11090
- amountU256.low.toString(),
11091
- amountU256.high.toString()
11092
- ]
11093
- };
11094
- const registerCall = contract.populate("register_order", [registerPayload]);
11095
- try {
11096
- const tx = await account.execute([approveCall, registerCall]);
11097
- await provider.waitForTransaction(tx.transaction_hash);
11098
- return { txHash: tx.transaction_hash };
11099
- } catch (err) {
11100
- throw new MedialaneError("Failed to make ERC-1155 offer", "TRANSACTION_FAILED", err);
11101
- }
11102
- }
11103
- async function checkoutCart1155(account, items, config) {
11104
- if (items.length === 0) throw new MedialaneError("Cart is empty", "INVALID_PARAMS");
11105
- const contract = getContract(config);
11106
- const provider = getProvider(config);
11107
- const tokenTotals = /* @__PURE__ */ new Map();
11108
- for (const item of items) {
11109
- const prev = tokenTotals.get(item.considerationToken) ?? 0n;
11110
- tokenTotals.set(item.considerationToken, prev + BigInt(item.considerationAmount));
11111
- }
11112
- const approveCalls = Array.from(tokenTotals.entries()).map(([tokenAddr, totalWei]) => {
11113
- const amount = starknet.cairo.uint256(totalWei.toString());
11114
- return {
11115
- contractAddress: tokenAddr,
11116
- entrypoint: "approve",
11117
- calldata: [
11118
- config.marketplace1155Contract,
11119
- amount.low.toString(),
11120
- amount.high.toString()
11121
- ]
11122
- };
11123
- });
11124
- const fulfillCalls = items.map(
11125
- (item) => contract.populate("fulfill_order", [item.orderHash, item.quantity ?? "1"])
11126
- );
11127
- try {
11128
- const tx = await account.execute([...approveCalls, ...fulfillCalls]);
11129
- await provider.waitForTransaction(tx.transaction_hash);
11130
- return { txHash: tx.transaction_hash };
11131
- } catch (err) {
11132
- throw new MedialaneError("ERC-1155 cart checkout failed", "TRANSACTION_FAILED", err);
11133
- }
11134
- }
11135
- async function getOrderDetails1155(orderHash, config) {
11136
- const contract = getContract(config);
11137
- return contract.get_order_details(orderHash);
11138
- }
11139
- async function getCounter1155(address, config) {
11140
- const contract = getContract(config);
11141
- return BigInt((await contract.get_counter(address)).toString());
11142
- }
11143
- async function incrementCounter1155(account, config) {
11144
- const contract = getContract(config);
11145
- const provider = getProvider(config);
11146
- const call = contract.populate("increment_counter", []);
11147
- try {
11148
- const tx = await account.execute(call);
11149
- await provider.waitForTransaction(tx.transaction_hash);
11150
- return { txHash: tx.transaction_hash };
11151
- } catch (err) {
11152
- throw new MedialaneError("Failed to increment counter (1155)", "TRANSACTION_FAILED", err);
11153
- }
11154
- }
11155
-
11156
- // src/starknet/marketplace1155/index.ts
11157
- var Medialane1155Module = class {
11158
- constructor(config) {
11159
- this.config = config;
11160
- }
11161
- // ─── Writes ───────────────────────────────────────────────────────────────
11162
- /**
11163
- * Create an ERC-1155 sell listing.
11164
- * Optionally grants `set_approval_for_all` if not already approved.
11165
- */
11166
- createListing(account, params) {
11167
- return createListing1155(account, params, this.config);
11168
- }
11169
- /**
11170
- * Make an offer (bid) on an ERC-1155 token.
11171
- * Approves the ERC-20 spend then calls `register_order` atomically.
11172
- */
11173
- makeOffer(account, params) {
11174
- return makeOffer1155(account, params, this.config);
11175
- }
11176
- /**
11177
- * Fulfill (buy) an ERC-1155 listing.
11178
- * Approves the payment token then calls `fulfill_order` atomically.
11179
- */
11180
- fulfillOrder(account, params) {
11181
- return fulfillOrder1155(account, params, this.config);
11182
- }
11183
- /**
11184
- * Cancel an ERC-1155 listing (offerer only).
11185
- */
11186
- cancelOrder(account, params) {
11187
- return cancelOrder1155(account, params, this.config);
11188
- }
11189
- /**
11190
- * Checkout a cart of ERC-1155 orders atomically.
11191
- * Signs one fulfillment per item (with quantity), sums ERC-20 approvals by token.
11192
- */
11193
- checkoutCart(account, items) {
11194
- return checkoutCart1155(account, items, this.config);
11195
- }
11196
- /** Bulk-cancel on the 1155 venue: bump the caller's counter. */
11197
- incrementCounter(account) {
11198
- return incrementCounter1155(account, this.config);
11199
- }
11200
- // ─── View calls ───────────────────────────────────────────────────────────
11201
- getOrderDetails(orderHash) {
11202
- return getOrderDetails1155(orderHash, this.config);
11203
- }
11204
- getCounter(address) {
11205
- return getCounter1155(address, this.config);
11206
- }
11207
- // ─── Typed data builders (for ChipiPay / custom signing flows) ───────────
11208
- buildListingTypedData(params, chainId) {
11209
- return build1155OrderTypedData(params, chainId);
11210
- }
11211
- buildCancellationTypedData(params, chainId) {
11212
- return build1155CancellationTypedData(params, chainId);
11213
- }
11214
- };
11215
- function normalizeAddress(chain, address) {
11216
- switch (chain) {
11217
- case "STARKNET":
11218
- return normalizeStarknet(address);
11219
- case "ETHEREUM":
11220
- case "BASE":
11221
- return normalizeEvm(address);
11222
- case "SOLANA":
11223
- return normalizeSolana(address);
11224
- case "STELLAR":
11225
- return normalizeStellar(address);
11226
- case "BITCOIN":
11227
- throw new Error("BITCOIN address normalization not implemented");
11228
- }
11229
- }
11230
- function normalizeStarknet(address) {
11231
- try {
11232
- const hex = BigInt(address).toString(16);
11233
- return "0x" + hex.padStart(64, "0").toLowerCase();
11234
- } catch {
11235
- throw new Error(`Invalid STARKNET address: "${address}"`);
11236
- }
11237
- }
11238
- function normalizeEvm(address) {
11239
- const m = /^0x([0-9a-fA-F]{40})$/.exec(address);
11240
- if (!m) throw new Error(`Invalid ETHEREUM/BASE address: "${address}"`);
11241
- const lower = m[1].toLowerCase();
11242
- const hash5 = sha3_js.keccak_256(new TextEncoder().encode(lower));
11243
- let out = "0x";
11244
- for (let i = 0; i < 40; i++) {
11245
- const nibble = hash5[i >> 1] >> (i % 2 === 0 ? 4 : 0) & 15;
11246
- out += nibble >= 8 ? lower[i].toUpperCase() : lower[i];
11247
- }
11248
- return out;
11249
- }
11250
- function normalizeSolana(address) {
11251
- try {
11252
- const bytes = base.base58.decode(address);
11253
- if (bytes.length !== 32) throw new Error("not a 32-byte key");
11254
- return address;
11255
- } catch {
11256
- throw new Error(`Invalid SOLANA address: "${address}"`);
11257
- }
11258
- }
11259
- var STELLAR_VERSION_BYTES = /* @__PURE__ */ new Set([6 << 3, 2 << 3]);
11260
- function normalizeStellar(address) {
11261
- const upper = address.toUpperCase();
11262
- if (!/^[GC][A-Z2-7]{55}$/.test(upper)) {
11263
- throw new Error(`Invalid STELLAR address: "${address}"`);
11264
- }
11265
- let decoded;
11266
- try {
11267
- decoded = base.base32.decode(upper);
11268
- } catch {
11269
- throw new Error(`Invalid STELLAR address: "${address}"`);
11270
- }
11271
- if (decoded.length !== 35 || !STELLAR_VERSION_BYTES.has(decoded[0])) {
11272
- throw new Error(`Invalid STELLAR address: "${address}"`);
11273
- }
11274
- const payload = decoded.subarray(0, 33);
11275
- const checksum = decoded[33] | decoded[34] << 8;
11276
- if (crc16xmodem(payload) !== checksum) {
11277
- throw new Error(`Invalid STELLAR address: "${address}"`);
11278
- }
11279
- return upper;
11280
- }
11281
- function crc16xmodem(bytes) {
11282
- let crc = 0;
11283
- for (const byte of bytes) {
11284
- crc ^= byte << 8;
11285
- for (let i = 0; i < 8; i++) {
11286
- crc = crc & 32768 ? (crc << 1 ^ 4129) & 65535 : crc << 1 & 65535;
11287
- }
11288
- }
11289
- return crc;
11290
- }
11291
-
11292
- // src/utils/retry.ts
11293
- var DEFAULT_MAX_ATTEMPTS = 3;
11294
- var DEFAULT_BASE_DELAY_MS = 300;
11295
- var DEFAULT_MAX_DELAY_MS = 5e3;
11296
- function sleep(ms) {
11297
- return new Promise((resolve) => setTimeout(resolve, ms));
11298
- }
11299
- async function withRetry(fn, opts) {
11300
- const maxAttempts = opts?.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
11301
- const baseDelayMs = opts?.baseDelayMs ?? DEFAULT_BASE_DELAY_MS;
11302
- const maxDelayMs = opts?.maxDelayMs ?? DEFAULT_MAX_DELAY_MS;
11303
- let lastError;
11304
- for (let attempt = 0; attempt < maxAttempts; attempt++) {
11305
- try {
11306
- return await fn();
11307
- } catch (err) {
11308
- lastError = err;
11309
- if (err instanceof MedialaneApiError && err.status < 500) {
11310
- throw err;
11311
- }
11312
- const isRetryable = err instanceof MedialaneApiError && err.status >= 500 || err instanceof TypeError;
11313
- if (!isRetryable || attempt === maxAttempts - 1) {
11314
- throw err;
11315
- }
11316
- const jitter = Math.random() * baseDelayMs;
11317
- const delay = Math.min(baseDelayMs * Math.pow(2, attempt) + jitter, maxDelayMs);
11318
- await sleep(delay);
11319
- }
11320
- }
11321
- throw lastError;
11322
- }
11323
-
11324
- // src/api/client.ts
11325
- function deriveErrorCode(status) {
11326
- if (status === 404) return "TOKEN_NOT_FOUND";
11327
- if (status === 429) return "RATE_LIMITED";
11328
- if (status === 410) return "INTENT_EXPIRED";
11329
- if (status === 401 || status === 403) return "UNAUTHORIZED";
11330
- if (status === 400) return "INVALID_PARAMS";
11331
- return "UNKNOWN";
11332
- }
11333
- var MedialaneApiError = class extends Error {
11334
- constructor(status, message) {
11335
- super(message);
11336
- this.status = status;
11337
- this.name = "MedialaneApiError";
11338
- this.code = deriveErrorCode(status);
11339
- }
11340
- };
11341
- var ApiClient = class {
11342
- constructor(baseUrl, apiKey, retryOptions, chain = "STARKNET") {
11343
- this.baseUrl = baseUrl;
11344
- this.chain = chain;
11345
- this.baseHeaders = apiKey ? { "x-api-key": apiKey } : {};
11346
- this.retryOptions = retryOptions;
11347
- }
11348
- /** Normalize an address for this client's chain (chain-scoped — Decision B). */
11349
- addr(a) {
11350
- return normalizeAddress(this.chain, a);
11351
- }
11352
- async request(path, init) {
11353
- const url = `${this.baseUrl.replace(/\/$/, "")}${path}`;
11354
- const headers = { ...this.baseHeaders };
11355
- if (!(init?.body instanceof FormData)) {
11356
- headers["Content-Type"] = "application/json";
11357
- }
11358
- const res = await withRetry(async () => {
11359
- const response = await fetch(url, {
11360
- ...init,
11361
- headers: { ...headers, ...init?.headers }
11362
- });
11363
- if (!response.ok) {
11364
- const text = await response.text().catch(() => response.statusText);
11365
- let message = text;
11366
- try {
11367
- const body = JSON.parse(text);
11368
- if (body.error) message = body.error;
11369
- } catch {
11370
- }
11371
- throw new MedialaneApiError(response.status, message);
11372
- }
11373
- return response;
11374
- }, this.retryOptions);
11375
- return res.json();
11376
- }
11377
- get(path) {
11378
- return this.request(path, { method: "GET" });
11379
- }
11380
- post(path, body) {
11381
- return this.request(path, { method: "POST", body: JSON.stringify(body) });
11382
- }
11383
- patch(path, body) {
11384
- return this.request(path, { method: "PATCH", body: JSON.stringify(body) });
11385
- }
11386
- del(path) {
11387
- return this.request(path, { method: "DELETE" });
11388
- }
11389
- async checkResponse(res, options) {
11390
- if (options?.allow404 && res.status === 404) return null;
11391
- if (options?.allow403 && res.status === 403) return null;
11392
- if (!res.ok) {
11393
- const text = await res.text().catch(() => res.statusText);
11394
- let message = text;
11395
- try {
11396
- const body = JSON.parse(text);
11397
- if (body.error) message = body.error;
11398
- } catch {
11399
- }
11400
- throw new MedialaneApiError(res.status, message);
11401
- }
11402
- return res.json();
11403
- }
11404
- // ─── Orders ────────────────────────────────────────────────────────────────
11405
- getOrders(query = {}) {
11406
- const params = new URLSearchParams();
11407
- if (query.status) params.set("status", query.status);
11408
- if (query.collection) params.set("collection", query.collection);
11409
- if (query.currency) params.set("currency", query.currency);
11410
- if (query.sort) params.set("sort", query.sort);
11411
- if (query.page !== void 0) params.set("page", String(query.page));
11412
- if (query.limit !== void 0) params.set("limit", String(query.limit));
11413
- if (query.offerer) params.set("offerer", this.addr(query.offerer));
11414
- if (query.minPrice) params.set("minPrice", query.minPrice);
11415
- if (query.maxPrice) params.set("maxPrice", query.maxPrice);
11416
- if (query.chain) params.set("chain", query.chain);
11417
- const qs = params.toString();
11418
- return this.get(`/v1/orders${qs ? `?${qs}` : ""}`);
11419
- }
11420
- getOrder(orderHash) {
11421
- return this.get(`/v1/orders/${orderHash}`);
11422
- }
11423
- getActiveOrdersForToken(contract, tokenId) {
11424
- return this.get(`/v1/orders/token/${this.addr(contract)}/${tokenId}`);
11425
- }
11426
- getOrdersByUser(address, page = 1, limit = 20) {
11427
- return this.get(
11428
- `/v1/orders/user/${this.addr(address)}?page=${page}&limit=${limit}`
11429
- );
11430
- }
11431
- // ─── Tokens ────────────────────────────────────────────────────────────────
11432
- getToken(contract, tokenId, wait = false) {
11433
- return this.get(
11434
- `/v1/tokens/${contract}/${tokenId}${wait ? "?wait=true" : ""}`
11435
- );
11436
- }
11437
- getTokensByOwner(address, page = 1, limit = 20) {
11438
- return this.get(
11439
- `/v1/tokens/owned/${this.addr(address)}?page=${page}&limit=${limit}`
11440
- );
11441
- }
11442
- getTokenHistory(contract, tokenId, page = 1, limit = 20) {
11443
- return this.get(
11444
- `/v1/tokens/${contract}/${tokenId}/history?page=${page}&limit=${limit}`
11445
- );
11446
- }
11447
- // ─── Collections ───────────────────────────────────────────────────────────
11448
- getCollections(page = 1, limit = 20, isKnown, sort, service, chain) {
11449
- const params = new URLSearchParams({ page: String(page), limit: String(limit) });
11450
- if (isKnown !== void 0) params.set("isKnown", String(isKnown));
11451
- if (sort) params.set("sort", sort);
11452
- if (service) params.set("service", service);
11453
- if (chain) params.set("chain", chain);
11454
- return this.get(`/v1/collections?${params}`);
11455
- }
11456
- getCollectionsByOwner(owner, page = 1, limit = 50) {
11457
- const params = new URLSearchParams({ owner: this.addr(owner), page: String(page), limit: String(limit) });
11458
- return this.get(`/v1/collections?${params}`);
11459
- }
11460
- getCollection(contract) {
11461
- return this.get(`/v1/collections/${this.addr(contract)}`);
11462
- }
11463
- getCollectionTokens(contract, page = 1, limit = 20, sort = "recent") {
11464
- return this.get(
11465
- `/v1/collections/${this.addr(contract)}/tokens?page=${page}&limit=${limit}&sort=${sort}`
11466
- );
11467
- }
11468
- // ─── Activities ────────────────────────────────────────────────────────────
11469
- getActivities(query = {}) {
11470
- const params = new URLSearchParams();
11471
- if (query.type) params.set("type", query.type);
11472
- if (query.page !== void 0) params.set("page", String(query.page));
11473
- if (query.limit !== void 0) params.set("limit", String(query.limit));
11474
- if (query.chain) params.set("chain", query.chain);
11475
- const qs = params.toString();
11476
- return this.get(`/v1/activities${qs ? `?${qs}` : ""}`);
11477
- }
11478
- getActivitiesByAddress(address, page = 1, limit = 20) {
11479
- return this.get(
11480
- `/v1/activities/${this.addr(address)}?page=${page}&limit=${limit}`
11481
- );
11482
- }
11483
- // ─── Comments ──────────────────────────────────────────────────────────────
11484
- getTokenComments(contract, tokenId, opts = {}) {
11485
- const params = new URLSearchParams();
11486
- if (opts.page !== void 0) params.set("page", String(opts.page));
11487
- if (opts.limit !== void 0) params.set("limit", String(opts.limit));
11488
- const qs = params.toString();
11489
- return this.get(
11490
- `/v1/tokens/${this.addr(contract)}/${tokenId}/comments${qs ? `?${qs}` : ""}`
11491
- );
11492
- }
11493
- // ─── Search ────────────────────────────────────────────────────────────────
11494
- search(q, limit = 10, chain) {
11495
- const params = new URLSearchParams({ q, limit: String(limit) });
11496
- if (chain) params.set("chain", chain);
11497
- return this.get(
11498
- `/v1/search?${params.toString()}`
11499
- );
11500
- }
11501
- // ─── Intents ───────────────────────────────────────────────────────────────
11502
- createListingIntent(params) {
11503
- return this.post("/v1/intents/listing", params);
11504
- }
11505
- createOfferIntent(params) {
11506
- return this.post("/v1/intents/offer", params);
11507
- }
11508
- createFulfillIntent(params) {
11509
- return this.post("/v1/intents/fulfill", params);
11510
- }
11511
- createCancelIntent(params) {
11512
- return this.post("/v1/intents/cancel", params);
11513
- }
11514
- getIntent(id) {
11515
- return this.get(`/v1/intents/${id}`);
11516
- }
11517
- submitIntentSignature(id, signature) {
11518
- return this.patch(`/v1/intents/${id}/signature`, { signature });
11519
- }
11520
- confirmIntent(id, txHash) {
11521
- return this.patch(`/v1/intents/${id}/confirm`, { txHash });
11522
- }
11523
- createMintIntent(params) {
11524
- return this.post("/v1/intents/mint", params);
11525
- }
11526
- createCollectionIntent(params) {
11527
- return this.post("/v1/intents/create-collection", params);
11528
- }
11529
- /**
11530
- * Create a counter-offer intent. The seller proposes a new price in response
11531
- * to a buyer's active bid. clerkToken is optional — the endpoint authenticates
11532
- * via the tenant API key; pass a Clerk JWT only if your backend requires it.
11533
- */
11534
- createCounterOfferIntent(params, clerkToken) {
11535
- const extraHeaders = clerkToken ? { "Authorization": `Bearer ${clerkToken}` } : {};
11536
- return this.request("/v1/intents/counter-offer", {
11537
- method: "POST",
11538
- body: JSON.stringify(params),
11539
- headers: extraHeaders
11540
- });
11541
- }
11542
- /**
11543
- * Fetch counter-offers. Pass `originalOrderHash` (buyer view) or
11544
- * `sellerAddress` (seller view) — at least one is required.
11545
- */
11546
- getCounterOffers(query) {
11547
- const params = new URLSearchParams();
11548
- if (query.originalOrderHash) params.set("originalOrderHash", query.originalOrderHash);
11549
- if (query.sellerAddress) params.set("sellerAddress", query.sellerAddress);
11550
- if (query.page !== void 0) params.set("page", String(query.page));
11551
- if (query.limit !== void 0) params.set("limit", String(query.limit));
11552
- return this.get(`/v1/orders/counter-offers?${params}`);
11553
- }
11554
- // ─── Metadata ──────────────────────────────────────────────────────────────
11555
- getMetadataSignedUrl() {
11556
- return this.get("/v1/metadata/signed-url");
11557
- }
11558
- uploadMetadata(metadata) {
11559
- return this.post("/v1/metadata/upload", metadata);
11560
- }
11561
- resolveMetadata(uri) {
11562
- const params = new URLSearchParams({ uri });
11563
- return this.get(`/v1/metadata/resolve?${params.toString()}`);
11564
- }
11565
- uploadFile(file) {
11566
- const formData = new FormData();
11567
- formData.append("file", file);
11568
- return this.request("/v1/metadata/upload-file", {
11569
- method: "POST",
11570
- body: formData
11571
- });
11572
- }
11573
- // ─── Portal (tenant self-service) ──────────────────────────────────────────
11574
- getMe() {
11575
- return this.get("/v1/portal/me");
11576
- }
11577
- getApiKeys() {
11578
- return this.get("/v1/portal/keys");
11579
- }
11580
- createApiKey(label) {
11581
- return this.post("/v1/portal/keys", label ? { label } : {});
11582
- }
11583
- deleteApiKey(id) {
11584
- return this.del(`/v1/portal/keys/${id}`);
11585
- }
11586
- getUsage() {
11587
- return this.get("/v1/portal/usage");
11588
- }
11589
- getWebhooks() {
11590
- return this.get("/v1/portal/webhooks");
11591
- }
11592
- createWebhook(params) {
11593
- return this.post("/v1/portal/webhooks", params);
11594
- }
11595
- deleteWebhook(id) {
11596
- return this.del(
11597
- `/v1/portal/webhooks/${id}`
11598
- );
11599
- }
11600
- // ─── Collection Claims ──────────────────────────────────────────────────────
11601
- /**
11602
- * Path 1: On-chain auto claim. Sends both x-api-key (tenant auth) and
11603
- * Authorization: Bearer (Clerk JWT) simultaneously.
11604
- */
11605
- async claimCollection(contractAddress, walletAddress, clerkToken) {
11606
- const url = `${this.baseUrl.replace(/\/$/, "")}/v1/collections/claim`;
11607
- const res = await fetch(url, {
11608
- method: "POST",
11609
- headers: {
11610
- "x-api-key": this.baseHeaders["x-api-key"] ?? "",
11611
- "Content-Type": "application/json",
11612
- "Authorization": `Bearer ${clerkToken}`
11613
- },
11614
- body: JSON.stringify({ contractAddress, walletAddress })
11615
- });
11616
- return this.checkResponse(res);
11617
- }
11618
- /**
11619
- * Path 3: Manual off-chain claim request (email-based).
11620
- */
11621
- requestCollectionClaim(params) {
11622
- return this.request("/v1/collections/claim/request", {
11623
- method: "POST",
11624
- body: JSON.stringify(params)
11625
- });
11626
- }
11627
- // ─── Collection Profiles ────────────────────────────────────────────────────
11628
- async getCollectionProfile(contractAddress) {
11629
- const url = `${this.baseUrl.replace(/\/$/, "")}/v1/collections/${this.addr(contractAddress)}/profile`;
11630
- const res = await fetch(url, { headers: this.baseHeaders });
11631
- return this.checkResponse(res, { allow404: true });
11632
- }
11633
- /**
11634
- * Update collection profile. Requires Clerk JWT for ownership check.
11635
- */
11636
- async updateCollectionProfile(contractAddress, data, clerkToken) {
11637
- const url = `${this.baseUrl.replace(/\/$/, "")}/v1/collections/${this.addr(contractAddress)}/profile`;
11638
- const res = await fetch(url, {
11639
- method: "PATCH",
11640
- headers: {
11641
- "x-api-key": this.baseHeaders["x-api-key"] ?? "",
11642
- "Content-Type": "application/json",
11643
- "Authorization": `Bearer ${clerkToken}`
11644
- },
11645
- body: JSON.stringify(data)
11646
- });
11647
- return this.checkResponse(res);
11648
- }
11649
- async getGatedContent(contractAddress, clerkToken) {
11650
- const url = `${this.baseUrl.replace(/\/$/, "")}/v1/collections/${this.addr(contractAddress)}/gated-content`;
11651
- const res = await fetch(url, {
11652
- headers: { ...this.baseHeaders, "Authorization": `Bearer ${clerkToken}` }
11653
- });
11654
- return this.checkResponse(res, { allow404: true, allow403: true });
11655
- }
11656
- // ─── Creator Profiles ───────────────────────────────────────────────────────
11657
- /** List all creators with an approved username. */
11658
- async getCreators(opts = {}) {
11659
- const params = new URLSearchParams();
11660
- if (opts.search) params.set("search", opts.search);
11661
- if (opts.page) params.set("page", String(opts.page));
11662
- if (opts.limit) params.set("limit", String(opts.limit));
11663
- const url = `${this.baseUrl.replace(/\/$/, "")}/v1/creators?${params}`;
11664
- const res = await fetch(url, { headers: this.baseHeaders });
11665
- return this.checkResponse(res);
11666
- }
11667
- async getCreatorProfile(walletAddress) {
11668
- const url = `${this.baseUrl.replace(/\/$/, "")}/v1/creators/${this.addr(walletAddress)}/profile`;
11669
- const res = await fetch(url, { headers: this.baseHeaders });
11670
- return this.checkResponse(res, { allow404: true });
11671
- }
11672
- /** Resolve a username slug to a creator profile (public). */
11673
- async getCreatorByUsername(username) {
11674
- const url = `${this.baseUrl.replace(/\/$/, "")}/v1/creators/by-username/${encodeURIComponent(username.toLowerCase().trim())}`;
11675
- const res = await fetch(url, { headers: this.baseHeaders });
11676
- return this.checkResponse(res, { allow404: true });
11677
- }
11678
- /**
11679
- * Update creator profile. Requires Clerk JWT; wallet must match authenticated user.
11680
- */
11681
- async updateCreatorProfile(walletAddress, data, clerkToken) {
11682
- const url = `${this.baseUrl.replace(/\/$/, "")}/v1/creators/${this.addr(walletAddress)}/profile`;
11683
- const res = await fetch(url, {
11684
- method: "PATCH",
11685
- headers: {
11686
- "x-api-key": this.baseHeaders["x-api-key"] ?? "",
11687
- "Content-Type": "application/json",
11688
- "Authorization": `Bearer ${clerkToken}`
11689
- },
11690
- body: JSON.stringify(data)
11691
- });
11692
- return this.checkResponse(res);
11693
- }
11694
- // ─── Collection Slug Claims ───────────────────────────────────────────────────
11695
- /** Check if a collection slug is available (public, no auth). */
11696
- async checkCollectionSlugAvailability(slug) {
11697
- const url = `${this.baseUrl.replace(/\/$/, "")}/v1/collection-slug-claims/check/${encodeURIComponent(slug.toLowerCase().trim())}`;
11698
- const res = await fetch(url, { headers: this.baseHeaders });
11699
- return this.checkResponse(res);
11700
- }
11701
- /** Submit a slug claim for a collection. Requires Clerk JWT — caller must be the collection owner. */
11702
- async submitCollectionSlugClaim(contractAddress, slug, clerkToken, notifyEmail) {
11703
- const url = `${this.baseUrl.replace(/\/$/, "")}/v1/collection-slug-claims`;
11704
- const res = await fetch(url, {
11705
- method: "POST",
11706
- headers: {
11707
- "x-api-key": this.baseHeaders["x-api-key"] ?? "",
11708
- "Content-Type": "application/json",
11709
- Authorization: `Bearer ${clerkToken}`
11710
- },
11711
- body: JSON.stringify({ contractAddress, slug, notifyEmail })
11712
- });
11713
- return this.checkResponse(res);
11714
- }
11715
- /** Returns all slug claims submitted by the authenticated wallet. Requires Clerk JWT. */
11716
- async getMyCollectionSlugClaims(clerkToken) {
11717
- const url = `${this.baseUrl.replace(/\/$/, "")}/v1/collection-slug-claims/me`;
11718
- const res = await fetch(url, {
11719
- headers: { ...this.baseHeaders, Authorization: `Bearer ${clerkToken}` }
11720
- });
11721
- return this.checkResponse(res);
11722
- }
11723
- /** Resolve a collection slug to a full collection. Returns null if not found. */
11724
- async getCollectionBySlug(slug) {
11725
- const url = `${this.baseUrl.replace(/\/$/, "")}/v1/collections/by-slug/${encodeURIComponent(slug.toLowerCase().trim())}`;
11726
- const res = await fetch(url, { headers: this.baseHeaders });
11727
- return this.checkResponse(res, { allow404: true });
11728
- }
11729
- // ─── User Wallet ─────────────────────────────────────────────────────────────
11730
- /**
11731
- * Upsert the authenticated user's wallet address in the backend DB.
11732
- * Call after onboarding when ChipiPay confirms the wallet address.
11733
- * Requires Clerk JWT; no tenant API key needed.
11734
- */
11735
- /**
11736
- * Frictionless wallet registration. Tenant API key only (no Clerk JWT required).
11737
- * Idempotent — backend's ensureAccountForWallet upserts and upgrades existing
11738
- * UNKNOWN walletType rows when a more specific value is supplied.
11739
- */
11740
- async registerUser(params) {
11741
- return this.post("/v1/users/register", params);
11742
- }
11743
- async upsertMyWallet(clerkToken, options = {}) {
11744
- const url = `${this.baseUrl.replace(/\/$/, "")}/v1/users/me`;
11745
- const body = {
11746
- walletType: options.walletType ?? "UNKNOWN",
11747
- appSource: options.appSource ?? "MEDIALANE_SDK"
11748
- };
11749
- if (options.chain) body.chain = options.chain;
11750
- const res = await fetch(url, {
11751
- method: "POST",
11752
- headers: {
11753
- "Content-Type": "application/json",
11754
- "Authorization": `Bearer ${clerkToken}`
11755
- },
11756
- body: JSON.stringify(body)
11757
- });
11758
- return this.checkResponse(res);
11759
- }
11760
- /**
11761
- * Get the authenticated user's stored wallet address from the backend DB.
11762
- * Returns null if the user has not completed onboarding yet.
11763
- * Requires Clerk JWT; no tenant API key needed.
11764
- */
11765
- async getMyWallet(clerkToken) {
11766
- const url = `${this.baseUrl.replace(/\/$/, "")}/v1/users/me`;
11767
- const res = await fetch(url, {
11768
- headers: { "Authorization": `Bearer ${clerkToken}` }
11769
- });
11770
- return this.checkResponse(res, { allow404: true });
11771
- }
11772
- // ─── Remix Licensing ─────────────────────────────────────────────────────────
11773
- /**
11774
- * Get public remixes of a token (open to everyone).
11775
- */
11776
- getTokenRemixes(contract, tokenId, opts = {}) {
11777
- const params = new URLSearchParams();
11778
- if (opts.page !== void 0) params.set("page", String(opts.page));
11779
- if (opts.limit !== void 0) params.set("limit", String(opts.limit));
11780
- const qs = params.toString();
11781
- return this.get(
11782
- `/v1/tokens/${this.addr(contract)}/${tokenId}/remixes${qs ? `?${qs}` : ""}`
11783
- );
11784
- }
11785
- /**
11786
- * Submit a custom remix offer for a token. Requires Clerk JWT.
11787
- */
11788
- submitRemixOffer(params, clerkToken) {
11789
- return this.request("/v1/remix-offers", {
11790
- method: "POST",
11791
- body: JSON.stringify(params),
11792
- headers: { "Authorization": `Bearer ${clerkToken}` }
11793
- });
11794
- }
11795
- /**
11796
- * Submit an auto remix offer for a token with an open license. Requires Clerk JWT.
11797
- */
11798
- submitAutoRemixOffer(params, clerkToken) {
11799
- return this.request("/v1/remix-offers/auto", {
11800
- method: "POST",
11801
- body: JSON.stringify(params),
11802
- headers: { "Authorization": `Bearer ${clerkToken}` }
11803
- });
11804
- }
11805
- /**
11806
- * Record a self-remix (owner remixing their own token). Requires Clerk JWT.
11807
- */
11808
- confirmSelfRemix(params, clerkToken) {
11809
- return this.request("/v1/remix-offers/self/confirm", {
11810
- method: "POST",
11811
- body: JSON.stringify(params),
11812
- headers: { "Authorization": `Bearer ${clerkToken}` }
11813
- });
11814
- }
11815
- /**
11816
- * List remix offers by role. Requires Clerk JWT.
11817
- * role="creator" — offers where you are the original creator.
11818
- * role="requester" — offers you made.
11819
- */
11820
- async getRemixOffers(query, clerkToken) {
11821
- const params = new URLSearchParams({ role: query.role });
11822
- if (query.page !== void 0) params.set("page", String(query.page));
11823
- if (query.limit !== void 0) params.set("limit", String(query.limit));
11824
- const url = `${this.baseUrl.replace(/\/$/, "")}/v1/remix-offers?${params}`;
11825
- const res = await fetch(url, {
11826
- headers: { ...this.baseHeaders, "Authorization": `Bearer ${clerkToken}` }
11827
- });
11828
- return this.checkResponse(res);
11829
- }
11830
- /**
11831
- * Get a single remix offer. Clerk JWT optional (price/currency hidden for non-participants).
11832
- */
11833
- async getRemixOffer(id, clerkToken) {
11834
- const url = `${this.baseUrl.replace(/\/$/, "")}/v1/remix-offers/${id}`;
11835
- const headers = { ...this.baseHeaders };
11836
- if (clerkToken) headers["Authorization"] = `Bearer ${clerkToken}`;
11837
- const res = await fetch(url, { headers });
11838
- return this.checkResponse(res);
11839
- }
11840
- /**
11841
- * Creator approves a remix offer (authorises the requester to mint). Requires Clerk JWT.
11842
- */
11843
- confirmRemixOffer(id, params, clerkToken) {
11844
- return this.request(`/v1/remix-offers/${id}/confirm`, {
11845
- method: "POST",
11846
- body: JSON.stringify(params),
11847
- headers: { "Authorization": `Bearer ${clerkToken}` }
11848
- });
11849
- }
11850
- /**
11851
- * Creator rejects a remix offer. Requires Clerk JWT.
11852
- */
11853
- rejectRemixOffer(id, clerkToken) {
11854
- return this.request(`/v1/remix-offers/${id}/reject`, {
11855
- method: "POST",
11856
- body: JSON.stringify({}),
11857
- headers: { "Authorization": `Bearer ${clerkToken}` }
11858
- });
11859
- }
11860
- /**
11861
- * Requester extends the expiry of a pending remix offer by 1–30 days.
11862
- * Requires Clerk JWT.
11863
- */
11864
- extendRemixOffer(id, days, clerkToken) {
11865
- return this.request(`/v1/remix-offers/${id}/extend`, {
11866
- method: "POST",
11867
- body: JSON.stringify({ days }),
11868
- headers: { "Authorization": `Bearer ${clerkToken}` }
11869
- });
11870
- }
11871
- // ─── POP Protocol ──────────────────────────────────────────────────────────
11872
- getPopCollections(opts = {}) {
11873
- return this.getCollections(opts.page ?? 1, opts.limit ?? 20, void 0, opts.sort, "POP_PROTOCOL");
11874
- }
11875
- async getPopEligibility(collection, wallet) {
11876
- const res = await this.get(
11877
- `/v1/pop/eligibility/${this.addr(collection)}/${this.addr(wallet)}`
11878
- );
11879
- return res.data;
11880
- }
11881
- async getPopEligibilityBatch(collection, wallets) {
11882
- const params = new URLSearchParams({ wallets: wallets.map((w) => this.addr(w)).join(",") });
11883
- const res = await this.get(
11884
- `/v1/pop/eligibility/${this.addr(collection)}?${params}`
11885
- );
11886
- return res.data;
11887
- }
11888
- // ─── Coins (fungible — ERC-20 etc.) ───────────────────────────────────────────
11889
- // Coins are a separate model from Collections (spec 2026-06-14). Price/liquidity
11890
- // is read live from Ekubo (CreatorCoinService.getPrice), never from these.
11891
- getCoins(opts = {}) {
11892
- const params = new URLSearchParams();
11893
- if (opts.page) params.set("page", String(opts.page));
11894
- if (opts.limit) params.set("limit", String(opts.limit));
11895
- if (opts.service) params.set("service", opts.service);
11896
- if (opts.chain) params.set("chain", opts.chain);
11897
- const qs = params.toString();
11898
- return this.get(`/v1/coins${qs ? `?${qs}` : ""}`);
11899
- }
11900
- getCoin(contract) {
11901
- return this.get(`/v1/coins/${this.addr(contract)}`);
11902
- }
11903
- // ─── Collection Drop ────────────────────────────────────────────────────────
11904
- getDropCollections(opts = {}) {
11905
- return this.getCollections(opts.page ?? 1, opts.limit ?? 20, void 0, opts.sort, "COLLECTION_DROP");
11906
- }
11907
- async getDropMintStatus(collection, wallet) {
11908
- const res = await this.get(
11909
- `/v1/drop/mint-status/${this.addr(collection)}/${this.addr(wallet)}`
11910
- );
11911
- return res.data;
11912
- }
11913
- // ─── Rewards (v0.49.0) ─────────────────────────────────────────────────────
11914
- // Scores are recomputed on a schedule by the backend (~15 min) — reads only.
11915
- /** Score + level + progress + badges for one address (zeroed for unknown). */
11916
- async getRewards(address) {
11917
- const res = await this.get(`/v1/rewards/${this.addr(address)}`);
11918
- return res.data;
11919
- }
11920
- /** Paginated XP leaderboard. */
11921
- getRewardsLeaderboard(page = 1, limit = 50) {
11922
- return this.get(`/v1/rewards?page=${page}&limit=${limit}`);
11923
- }
11924
- /** Point-event history for an address. */
11925
- getRewardsEvents(address, page = 1, limit = 20) {
11926
- return this.get(
11927
- `/v1/rewards/${this.addr(address)}/events?page=${page}&limit=${limit}`
11928
- );
11929
- }
11930
- /** Reward configuration: level ladder, enabled action XP values, badge catalog. */
11931
- async getRewardsConfig() {
11932
- const res = await this.get(`/v1/rewards/config`);
11933
- return res.data;
11934
- }
11935
- /** Minimal level info for up to 50 addresses — one call per list page. */
11936
- async getRewardsBatch(addresses) {
11937
- if (addresses.length === 0) return [];
11938
- const params = new URLSearchParams({ addresses: addresses.map((a) => this.addr(a)).join(",") });
11939
- const res = await this.get(`/v1/rewards/batch?${params}`);
11940
- return res.data;
10844
+ "kind": "nested"
10845
+ },
10846
+ {
10847
+ "name": "CounterDecremented",
10848
+ "type": "mip::mip::MIP::CounterComponent::CounterDecremented",
10849
+ "kind": "nested"
10850
+ }
10851
+ ]
10852
+ },
10853
+ {
10854
+ "type": "event",
10855
+ "name": "mip::mip::MIP::Event",
10856
+ "kind": "enum",
10857
+ "variants": [
10858
+ {
10859
+ "name": "ERC721Event",
10860
+ "type": "openzeppelin_token::erc721::erc721::ERC721Component::Event",
10861
+ "kind": "flat"
10862
+ },
10863
+ {
10864
+ "name": "OwnableEvent",
10865
+ "type": "openzeppelin_access::ownable::ownable::OwnableComponent::Event",
10866
+ "kind": "flat"
10867
+ },
10868
+ {
10869
+ "name": "SRC5Event",
10870
+ "type": "openzeppelin_introspection::src5::SRC5Component::Event",
10871
+ "kind": "flat"
10872
+ },
10873
+ {
10874
+ "name": "ERC721EnumerableEvent",
10875
+ "type": "openzeppelin_token::erc721::extensions::erc721_enumerable::erc721_enumerable::ERC721EnumerableComponent::Event",
10876
+ "kind": "flat"
10877
+ },
10878
+ {
10879
+ "name": "CounterEvent",
10880
+ "type": "mip::mip::MIP::CounterComponent::Event",
10881
+ "kind": "flat"
10882
+ }
10883
+ ]
11941
10884
  }
11942
- };
10885
+ ];
10886
+
10887
+ // src/starknet/services/pop.ts
11943
10888
  var PopService = class {
11944
10889
  constructor(config) {
11945
10890
  this.factoryAddress = getStarknetCoordinates(config.chain).popFactory;
@@ -12006,6 +10951,21 @@ var PopService = class {
12006
10951
  return { txHash: res.transaction_hash };
12007
10952
  }
12008
10953
  };
10954
+ function buildFeeCall(p, cfg) {
10955
+ if (!cfg.enabled || !cfg.fundAddress) return null;
10956
+ const bps = p.surface === "marketplace" ? cfg.marketplaceBps : cfg.launchpadBps;
10957
+ if (bps <= 0) return null;
10958
+ const fee = p.grossAmount * BigInt(bps) / 10000n;
10959
+ if (fee <= 0n) return null;
10960
+ const u = starknet.cairo.uint256(fee.toString());
10961
+ return {
10962
+ contractAddress: p.token,
10963
+ entrypoint: "transfer",
10964
+ calldata: [cfg.fundAddress, u.low.toString(), u.high.toString()]
10965
+ };
10966
+ }
10967
+
10968
+ // src/starknet/services/drop.ts
12009
10969
  function toContractConditions(c) {
12010
10970
  return {
12011
10971
  start_time: c.startTime,
@@ -12217,6 +11177,83 @@ var ERC1155CollectionService = class {
12217
11177
  return { txHash: res.transaction_hash };
12218
11178
  }
12219
11179
  };
11180
+
11181
+ // src/constants.ts
11182
+ var SN = getCoordinates("STARKNET");
11183
+ SN.marketplace721;
11184
+ SN.marketplace721ClassHash;
11185
+ SN.marketplace721StartBlock;
11186
+ SN.marketplace1155;
11187
+ SN.marketplace1155ClassHash;
11188
+ SN.marketplace1155StartBlock;
11189
+ SN.collection721;
11190
+ SN.collection721StartBlock;
11191
+ SN.ipNftClassHash;
11192
+ SN.ipCollectionClassHash;
11193
+ SN.collection1155;
11194
+ SN.collection1155FactoryClassHash;
11195
+ SN.collection1155ClassHash;
11196
+ SN.collection1155StartBlock;
11197
+ SN.popFactory;
11198
+ SN.popCollectionClassHash;
11199
+ SN.dropFactory;
11200
+ SN.dropCollectionClassHash;
11201
+ SN.nftComments;
11202
+ SN.ipTicketsFactory;
11203
+ SN.ipTicketCollectionClassHash;
11204
+ SN.ipClubRegistry;
11205
+ SN.ipClubNftClassHash;
11206
+ SN.ipClubFactory;
11207
+ SN.ipClubCollectionClassHash;
11208
+ SN.ipSponsorship;
11209
+ SN.ipSponsorshipLicense;
11210
+ SN.creatorCoinFactory;
11211
+ SN.creatorCoinEkuboLauncher;
11212
+ SN.creatorCoinClassHash;
11213
+ SN.creatorCoinFactoryClassHash;
11214
+ SN.creatorCoinStartBlock;
11215
+ SN.ekuboCore;
11216
+ var SUPPORTED_TOKENS = [
11217
+ {
11218
+ // Circle-native USDC on Starknet (canonical)
11219
+ symbol: "USDC",
11220
+ address: "0x033068f6539f8e6e6b131e6b2b814e6c34a5224bc66947c47dab9dfee93b35fb",
11221
+ decimals: 6,
11222
+ listable: true
11223
+ },
11224
+ {
11225
+ symbol: "USDT",
11226
+ address: "0x068f5c6a61780768455de69077e07e89787839bf8166decfbf92b645209c0fb8",
11227
+ decimals: 6,
11228
+ listable: true
11229
+ },
11230
+ {
11231
+ symbol: "ETH",
11232
+ address: "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7",
11233
+ decimals: 18,
11234
+ listable: true
11235
+ },
11236
+ {
11237
+ symbol: "STRK",
11238
+ address: "0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d",
11239
+ decimals: 18,
11240
+ listable: true
11241
+ },
11242
+ {
11243
+ symbol: "WBTC",
11244
+ address: "0x03fe2b97c1fd336e750087d68b9b867997fd64a2661ff3ca5a7c771641e8e7ac",
11245
+ decimals: 8,
11246
+ listable: true
11247
+ }
11248
+ ];
11249
+
11250
+ // src/utils/token.ts
11251
+ function getTokenByAddress(address) {
11252
+ const lower = address.toLowerCase();
11253
+ return SUPPORTED_TOKENS.find((t) => t.address.toLowerCase() === lower);
11254
+ }
11255
+
11256
+ // src/starknet/services/creatorCoin.ts
12220
11257
  var VALIDATED_EKUBO_PARAMS = {
12221
11258
  fee: "0xc49ba5e353f7d00000000000000000",
12222
11259
  tickSpacing: 5982n,
@@ -12662,8 +11699,6 @@ var SponsorshipService = class {
12662
11699
  var MedialaneClient = class {
12663
11700
  constructor(rawConfig = {}) {
12664
11701
  this.config = resolveConfig(rawConfig);
12665
- this.marketplace = new MarketplaceModule(this.config);
12666
- this.marketplace1155 = new Medialane1155Module(this.config);
12667
11702
  this.services = {
12668
11703
  pop: new PopService(this.config),
12669
11704
  drop: new DropService(this.config),
@@ -12696,109 +11731,596 @@ var MedialaneClient = class {
12696
11731
  this.api = new ApiClient(this.config.backendUrl, this.config.apiKey, this.config.retryOptions, this.config.chain);
12697
11732
  }
12698
11733
  }
12699
- get chain() {
12700
- return this.config.chain;
11734
+ get chain() {
11735
+ return this.config.chain;
11736
+ }
11737
+ get rpcUrl() {
11738
+ return this.config.rpcUrl;
11739
+ }
11740
+ get marketplaceContract() {
11741
+ return this.config.marketplaceContract;
11742
+ }
11743
+ };
11744
+
11745
+ // src/starknet/marketplace/errors.ts
11746
+ var MedialaneError = class extends Error {
11747
+ constructor(message, code = "UNKNOWN", cause) {
11748
+ super(message);
11749
+ this.code = code;
11750
+ this.cause = cause;
11751
+ this.name = "MedialaneError";
11752
+ }
11753
+ };
11754
+
11755
+ // src/utils/rpc.ts
11756
+ var PUBLIC_RPC_FALLBACKS = [
11757
+ "https://rpc.starknet.lava.build"
11758
+ ];
11759
+ var TRANSIENT_BODY_RE = /"code"\s*:\s*-32001|"code"\s*:\s*-32603|unable to complete|rate.?limit|too many|throttl|exceed.*quota|temporarily unavailable|service unavailable|overload|gateway.*time|upstream.*time|backend.*error/i;
11760
+ function isTransientRpcError(input) {
11761
+ const { status, body } = input;
11762
+ if (typeof status === "number" && (status === 429 || status >= 500)) return true;
11763
+ if (body == null) return false;
11764
+ if (typeof body === "object") {
11765
+ const err = body.error;
11766
+ if (!err || typeof err !== "object") return false;
11767
+ const code = err.code;
11768
+ if (typeof code === "number") {
11769
+ if (code === 429) return true;
11770
+ if (code >= -32099 && code <= -32e3) return true;
11771
+ if (code === -32603) return true;
11772
+ }
11773
+ const message = err.message;
11774
+ return typeof message === "string" ? TRANSIENT_BODY_RE.test(message) : false;
11775
+ }
11776
+ return TRANSIENT_BODY_RE.test(String(body));
11777
+ }
11778
+ function createFailoverFetch(urls, options = {}) {
11779
+ const endpoints = urls.filter((u) => Boolean(u));
11780
+ if (endpoints.length === 0) {
11781
+ throw new Error("createFailoverFetch: at least one RPC URL is required");
11782
+ }
11783
+ const doFetch = options.baseFetch ?? fetch;
11784
+ const failover = async (_input, init) => {
11785
+ let lastError;
11786
+ for (let i = 0; i < endpoints.length; i++) {
11787
+ const url = endpoints[i];
11788
+ const isLast = i === endpoints.length - 1;
11789
+ try {
11790
+ const res = await doFetch(url, init);
11791
+ const text = await res.text();
11792
+ const rebuilt = () => new Response(text, { status: res.status, statusText: res.statusText, headers: res.headers });
11793
+ if (isLast || !isTransientRpcError({ status: res.status, body: text })) {
11794
+ return rebuilt();
11795
+ }
11796
+ options.onFailover?.({ url, status: res.status });
11797
+ } catch (err) {
11798
+ lastError = err;
11799
+ if (isLast) throw err;
11800
+ options.onFailover?.({ url, error: err });
11801
+ }
11802
+ }
11803
+ throw lastError ?? new Error("createFailoverFetch: all endpoints failed");
11804
+ };
11805
+ return failover;
11806
+ }
11807
+
11808
+ // src/starknet/marketplace/utils.ts
11809
+ var START_TIME_BUFFER_SECS = 30;
11810
+ function newContract(abi, address, providerOrAccount) {
11811
+ const C = starknet.Contract;
11812
+ return C.length === 1 ? new starknet.Contract({ abi, address, providerOrAccount }) : new starknet.Contract(
11813
+ abi,
11814
+ address,
11815
+ providerOrAccount
11816
+ );
11817
+ }
11818
+ function getChainId(config) {
11819
+ if (config.chain !== "STARKNET") {
11820
+ throw new Error(`SNIP-12 signing is Starknet-only; got chain "${config.chain}"`);
11821
+ }
11822
+ return starknet.constants.StarknetChainId.SN_MAIN;
11823
+ }
11824
+ function resolveToken(currency) {
11825
+ const token = SUPPORTED_TOKENS.find(
11826
+ (t) => t.symbol === currency.toUpperCase() || t.address.toLowerCase() === currency.toLowerCase()
11827
+ );
11828
+ if (!token) throw new MedialaneError(`Unsupported currency: ${currency}`, "INVALID_PARAMS");
11829
+ return token;
11830
+ }
11831
+ var _providerCache = /* @__PURE__ */ new WeakMap();
11832
+ function getProvider(config) {
11833
+ let p = _providerCache.get(config);
11834
+ if (!p) {
11835
+ const urls = Array.from(/* @__PURE__ */ new Set([config.rpcUrl, ...PUBLIC_RPC_FALLBACKS]));
11836
+ p = new starknet.RpcProvider({ nodeUrl: urls[0], baseFetch: createFailoverFetch(urls) });
11837
+ _providerCache.set(config, p);
11838
+ }
11839
+ return p;
11840
+ }
11841
+
11842
+ // src/starknet/marketplace/orders.ts
11843
+ var _contractCache = /* @__PURE__ */ new WeakMap();
11844
+ function makeContract(config) {
11845
+ const cached = _contractCache.get(config);
11846
+ if (cached) return cached;
11847
+ const contract = newContract(
11848
+ IPMarketplaceABI,
11849
+ config.marketplaceContract,
11850
+ getProvider(config)
11851
+ );
11852
+ const entry = { contract };
11853
+ _contractCache.set(config, entry);
11854
+ return entry;
11855
+ }
11856
+ async function getOrderDetails(orderHash, config) {
11857
+ const { contract } = makeContract(config);
11858
+ return contract.get_order_details(orderHash);
11859
+ }
11860
+ async function getCounter(address, config) {
11861
+ const { contract } = makeContract(config);
11862
+ return BigInt((await contract.get_counter(address)).toString());
11863
+ }
11864
+
11865
+ // src/starknet/marketplace1155/orders.ts
11866
+ var _contractCache2 = /* @__PURE__ */ new WeakMap();
11867
+ function getContract(config) {
11868
+ let c = _contractCache2.get(config);
11869
+ if (!c) {
11870
+ c = newContract(Medialane1155ABI, config.marketplace1155Contract, getProvider(config));
11871
+ _contractCache2.set(config, c);
11872
+ }
11873
+ return c;
11874
+ }
11875
+ async function getOrderDetails1155(orderHash, config) {
11876
+ const contract = getContract(config);
11877
+ return contract.get_order_details(orderHash);
11878
+ }
11879
+ async function getCounter1155(address, config) {
11880
+ const contract = getContract(config);
11881
+ return BigInt((await contract.get_counter(address)).toString());
11882
+ }
11883
+
11884
+ // src/utils/bigint.ts
11885
+ function stringifyBigInts(obj) {
11886
+ if (typeof obj === "bigint") {
11887
+ return obj.toString();
12701
11888
  }
12702
- get rpcUrl() {
12703
- return this.config.rpcUrl;
11889
+ if (Array.isArray(obj)) {
11890
+ return obj.map(stringifyBigInts);
12704
11891
  }
12705
- get marketplaceContract() {
12706
- return this.config.marketplaceContract;
11892
+ if (obj !== null && typeof obj === "object") {
11893
+ return Object.fromEntries(
11894
+ Object.entries(obj).map(([key, value]) => [
11895
+ key,
11896
+ stringifyBigInts(value)
11897
+ ])
11898
+ );
12707
11899
  }
11900
+ return obj;
11901
+ }
11902
+ var STARKNET_DOMAIN = [
11903
+ { name: "name", type: "shortstring" },
11904
+ { name: "version", type: "shortstring" },
11905
+ { name: "chainId", type: "shortstring" },
11906
+ { name: "revision", type: "shortstring" }
11907
+ ];
11908
+ var OFFER_ITEM = [
11909
+ { name: "item_type", type: "shortstring" },
11910
+ { name: "token", type: "ContractAddress" },
11911
+ { name: "identifier_or_criteria", type: "felt" },
11912
+ { name: "amount", type: "felt" }
11913
+ ];
11914
+ var CONSIDERATION_ITEM = [
11915
+ { name: "item_type", type: "shortstring" },
11916
+ { name: "token", type: "ContractAddress" },
11917
+ { name: "identifier_or_criteria", type: "felt" },
11918
+ { name: "amount", type: "felt" },
11919
+ { name: "recipient", type: "ContractAddress" }
11920
+ ];
11921
+ var ORDER_PARAMETERS = [
11922
+ { name: "offerer", type: "ContractAddress" },
11923
+ { name: "marketplace", type: "ContractAddress" },
11924
+ { name: "offer", type: "OfferItem" },
11925
+ { name: "consideration", type: "ConsiderationItem" },
11926
+ { name: "royalty_max_bps", type: "felt" },
11927
+ { name: "start_time", type: "felt" },
11928
+ { name: "end_time", type: "felt" },
11929
+ { name: "salt", type: "felt" },
11930
+ { name: "counter", type: "felt" }
11931
+ ];
11932
+ var ORDER_CANCELLATION = [
11933
+ { name: "order_hash", type: "felt" },
11934
+ { name: "offerer", type: "ContractAddress" }
11935
+ ];
11936
+ var DOMAIN_VERSION = {
11937
+ erc721: "5",
11938
+ erc1155: "4"
12708
11939
  };
11940
+ function buildDomain(standard, chainId) {
11941
+ return {
11942
+ name: "Medialane",
11943
+ version: DOMAIN_VERSION[standard],
11944
+ chainId,
11945
+ revision: starknet.TypedDataRevision.ACTIVE
11946
+ };
11947
+ }
11948
+ function buildOrderTypedData(message, chainId) {
11949
+ return {
11950
+ domain: buildDomain("erc721", chainId),
11951
+ primaryType: "OrderParameters",
11952
+ types: {
11953
+ StarknetDomain: STARKNET_DOMAIN,
11954
+ OrderParameters: ORDER_PARAMETERS,
11955
+ OfferItem: OFFER_ITEM,
11956
+ ConsiderationItem: CONSIDERATION_ITEM
11957
+ },
11958
+ message
11959
+ };
11960
+ }
11961
+ function build1155OrderTypedData(message, chainId) {
11962
+ return {
11963
+ domain: buildDomain("erc1155", chainId),
11964
+ primaryType: "OrderParameters",
11965
+ types: {
11966
+ StarknetDomain: STARKNET_DOMAIN,
11967
+ OrderParameters: ORDER_PARAMETERS,
11968
+ OfferItem: OFFER_ITEM,
11969
+ ConsiderationItem: CONSIDERATION_ITEM
11970
+ },
11971
+ message
11972
+ };
11973
+ }
11974
+ function buildCancellationTypedData(message, chainId) {
11975
+ return {
11976
+ domain: buildDomain("erc721", chainId),
11977
+ primaryType: "OrderCancellation",
11978
+ types: {
11979
+ StarknetDomain: STARKNET_DOMAIN,
11980
+ OrderCancellation: ORDER_CANCELLATION
11981
+ },
11982
+ message
11983
+ };
11984
+ }
11985
+ function build1155CancellationTypedData(message, chainId) {
11986
+ return {
11987
+ domain: buildDomain("erc1155", chainId),
11988
+ primaryType: "OrderCancellation",
11989
+ types: {
11990
+ StarknetDomain: STARKNET_DOMAIN,
11991
+ OrderCancellation: ORDER_CANCELLATION
11992
+ },
11993
+ message
11994
+ };
11995
+ }
11996
+
11997
+ // src/starknet/marketplace/build.ts
11998
+ function contractFor(cfg) {
11999
+ return newContract(IPMarketplaceABI, cfg.marketplaceContract, getProvider(cfg));
12000
+ }
12001
+ function buildListingOrder(i, cfg) {
12002
+ const orderParams = {
12003
+ offerer: i.offerer,
12004
+ marketplace: cfg.marketplaceContract,
12005
+ offer: { item_type: "ERC721", token: i.nftContract, identifier_or_criteria: i.tokenId, amount: "1" },
12006
+ consideration: {
12007
+ item_type: "ERC20",
12008
+ token: i.paymentTokenAddress,
12009
+ identifier_or_criteria: "0",
12010
+ amount: i.priceWei,
12011
+ recipient: i.offerer
12012
+ },
12013
+ royalty_max_bps: i.royaltyMaxBps,
12014
+ start_time: String(i.startTime),
12015
+ end_time: String(i.endTime),
12016
+ salt: i.salt,
12017
+ counter: i.counter
12018
+ };
12019
+ const typedData = stringifyBigInts(buildOrderTypedData(orderParams, getChainId(cfg)));
12020
+ return { orderParams, typedData };
12021
+ }
12022
+ function buildOfferOrder(i, cfg) {
12023
+ const orderParams = {
12024
+ offerer: i.offerer,
12025
+ marketplace: cfg.marketplaceContract,
12026
+ offer: { item_type: "ERC20", token: i.paymentTokenAddress, identifier_or_criteria: "0", amount: i.priceWei },
12027
+ consideration: {
12028
+ item_type: "ERC721",
12029
+ token: i.nftContract,
12030
+ identifier_or_criteria: i.tokenId,
12031
+ amount: "1",
12032
+ recipient: i.offerer
12033
+ },
12034
+ royalty_max_bps: i.royaltyMaxBps,
12035
+ start_time: String(i.startTime),
12036
+ end_time: String(i.endTime),
12037
+ salt: i.salt,
12038
+ counter: i.counter
12039
+ };
12040
+ const typedData = stringifyBigInts(buildOrderTypedData(orderParams, getChainId(cfg)));
12041
+ return { orderParams, typedData };
12042
+ }
12043
+ function registerPayload(orderParams, signature) {
12044
+ return stringifyBigInts({
12045
+ parameters: {
12046
+ ...orderParams,
12047
+ offer: { ...orderParams.offer, item_type: starknet.shortString.encodeShortString(orderParams.offer.item_type) },
12048
+ consideration: {
12049
+ ...orderParams.consideration,
12050
+ item_type: starknet.shortString.encodeShortString(orderParams.consideration.item_type)
12051
+ }
12052
+ },
12053
+ signature
12054
+ });
12055
+ }
12056
+ function buildRegisterCalls(a, cfg) {
12057
+ const registerCall = contractFor(cfg).populate("register_order", [registerPayload(a.orderParams, a.signature)]);
12058
+ return a.approvalNeeded ? [a.approve, registerCall] : [registerCall];
12059
+ }
12060
+ function buildFulfillCalls(a, cfg) {
12061
+ const u = starknet.cairo.uint256(a.totalPrice);
12062
+ const approve = {
12063
+ contractAddress: a.paymentToken,
12064
+ entrypoint: "approve",
12065
+ calldata: [cfg.marketplaceContract, u.low.toString(), u.high.toString()]
12066
+ };
12067
+ const fulfill = contractFor(cfg).populate("fulfill_order", [a.orderHash]);
12068
+ const fee = buildFeeCall(
12069
+ { surface: "marketplace", token: a.paymentToken, grossAmount: BigInt(a.totalPrice) },
12070
+ cfg.feeConfig
12071
+ );
12072
+ return fee ? [approve, fulfill, fee] : [approve, fulfill];
12073
+ }
12074
+ function buildCancelCalls(a, cfg) {
12075
+ const cancelRequest = stringifyBigInts({
12076
+ cancelation: { order_hash: a.orderHash, offerer: a.offerer },
12077
+ signature: a.signature
12078
+ });
12079
+ return [contractFor(cfg).populate("cancel_order", [cancelRequest])];
12080
+ }
12081
+ function buildCancelTypedData(orderHash, offerer, cfg) {
12082
+ return stringifyBigInts(buildCancellationTypedData({ order_hash: orderHash, offerer }, getChainId(cfg)));
12083
+ }
12084
+ function contractFor2(cfg) {
12085
+ return newContract(Medialane1155ABI, cfg.marketplace1155Contract, getProvider(cfg));
12086
+ }
12087
+ function buildListing1155Order(i, cfg) {
12088
+ const orderParams = {
12089
+ offerer: i.offerer,
12090
+ marketplace: cfg.marketplace1155Contract,
12091
+ offer: { item_type: "ERC1155", token: i.nftContract, identifier_or_criteria: i.tokenId, amount: i.quantity },
12092
+ consideration: {
12093
+ item_type: "ERC20",
12094
+ token: i.paymentTokenAddress,
12095
+ identifier_or_criteria: "0",
12096
+ amount: i.priceWeiPerUnit,
12097
+ recipient: i.offerer
12098
+ },
12099
+ royalty_max_bps: i.royaltyMaxBps,
12100
+ start_time: String(i.startTime),
12101
+ end_time: String(i.endTime),
12102
+ salt: i.salt,
12103
+ counter: i.counter
12104
+ };
12105
+ const typedData = stringifyBigInts(
12106
+ build1155OrderTypedData(orderParams, getChainId(cfg))
12107
+ );
12108
+ return { orderParams, typedData };
12109
+ }
12110
+ function buildOffer1155Order(i, cfg) {
12111
+ const orderParams = {
12112
+ offerer: i.offerer,
12113
+ marketplace: cfg.marketplace1155Contract,
12114
+ offer: { item_type: "ERC20", token: i.paymentTokenAddress, identifier_or_criteria: "0", amount: i.priceWeiPerUnit },
12115
+ consideration: {
12116
+ item_type: "ERC1155",
12117
+ token: i.nftContract,
12118
+ identifier_or_criteria: i.tokenId,
12119
+ amount: i.quantity,
12120
+ recipient: i.offerer
12121
+ },
12122
+ royalty_max_bps: i.royaltyMaxBps,
12123
+ start_time: String(i.startTime),
12124
+ end_time: String(i.endTime),
12125
+ salt: i.salt,
12126
+ counter: i.counter
12127
+ };
12128
+ const typedData = stringifyBigInts(
12129
+ build1155OrderTypedData(orderParams, getChainId(cfg))
12130
+ );
12131
+ return { orderParams, typedData };
12132
+ }
12133
+ function registerPayload2(orderParams, signature) {
12134
+ return stringifyBigInts({
12135
+ parameters: {
12136
+ ...orderParams,
12137
+ offer: { ...orderParams.offer, item_type: starknet.shortString.encodeShortString(orderParams.offer.item_type) },
12138
+ consideration: {
12139
+ ...orderParams.consideration,
12140
+ item_type: starknet.shortString.encodeShortString(orderParams.consideration.item_type)
12141
+ }
12142
+ },
12143
+ signature
12144
+ });
12145
+ }
12146
+ function buildRegister1155Calls(a, cfg) {
12147
+ const registerCall = contractFor2(cfg).populate("register_order", [registerPayload2(a.orderParams, a.signature)]);
12148
+ return a.approvalNeeded ? [a.approve, registerCall] : [registerCall];
12149
+ }
12150
+ function buildFulfill1155Calls(a, cfg) {
12151
+ const u = starknet.cairo.uint256(a.totalPrice);
12152
+ const approve = {
12153
+ contractAddress: a.paymentToken,
12154
+ entrypoint: "approve",
12155
+ calldata: [cfg.marketplace1155Contract, u.low.toString(), u.high.toString()]
12156
+ };
12157
+ const fulfill = contractFor2(cfg).populate("fulfill_order", [a.orderHash, a.quantity]);
12158
+ const fee = buildFeeCall(
12159
+ { surface: "marketplace", token: a.paymentToken, grossAmount: BigInt(a.totalPrice) },
12160
+ cfg.feeConfig
12161
+ );
12162
+ return fee ? [approve, fulfill, fee] : [approve, fulfill];
12163
+ }
12164
+ function buildCancel1155Calls(a, cfg) {
12165
+ const cancelPayload = stringifyBigInts({
12166
+ cancelation: { order_hash: a.orderHash, offerer: a.offerer },
12167
+ signature: a.signature
12168
+ });
12169
+ return [contractFor2(cfg).populate("cancel_order", [cancelPayload])];
12170
+ }
12171
+ function buildCancel1155TypedData(orderHash, offerer, cfg) {
12172
+ return stringifyBigInts(
12173
+ build1155CancellationTypedData({ order_hash: orderHash, offerer }, getChainId(cfg))
12174
+ );
12175
+ }
12176
+
12177
+ // src/starknet/venue.ts
12709
12178
  var NO_EXPIRY_SECONDS = 100 * 365 * 24 * 3600;
12710
12179
  var StarknetVenue = class {
12711
12180
  constructor(deps) {
12712
12181
  this.deps = deps;
12713
12182
  this.chain = "STARKNET";
12714
- this.m721 = new MarketplaceModule(deps.config);
12715
- this.m1155 = new Medialane1155Module(deps.config);
12716
12183
  }
12717
- incrementCounter(signer) {
12718
- return this.m721.incrementCounter(signer);
12184
+ async incrementCounter(signer) {
12185
+ return signer.execute([
12186
+ { contractAddress: this.deps.config.marketplaceContract, entrypoint: "increment_counter", calldata: [] }
12187
+ ]);
12719
12188
  }
12720
12189
  getOrderDetails(orderRef) {
12721
- return this.m721.getOrderDetails(orderRef);
12190
+ return getOrderDetails(orderRef, this.deps.config);
12722
12191
  }
12723
- getCounter(address) {
12724
- return this.m721.getCounter(address);
12192
+ async getCounter(address) {
12193
+ return this.readCounter(this.deps.config.marketplaceContract, address);
12725
12194
  }
12726
12195
  async fulfillOrder(signer, orderRef, opts) {
12727
12196
  const o = await this.deps.resolveOrder(orderRef);
12728
12197
  const quantity = opts?.quantity ?? "1";
12729
12198
  const totalPrice = (BigInt(o.unitPrice) * BigInt(quantity)).toString();
12730
- if (o.standard === "ERC1155") {
12731
- return this.m1155.fulfillOrder(signer, {
12732
- orderHash: orderRef,
12733
- paymentToken: o.paymentToken,
12734
- totalPrice,
12735
- quantity
12736
- });
12737
- }
12738
- return this.m721.fulfillOrder(signer, {
12739
- orderHash: orderRef,
12740
- paymentToken: o.paymentToken,
12741
- totalPrice
12742
- });
12199
+ const calls = o.standard === "ERC1155" ? buildFulfill1155Calls({ orderHash: orderRef, paymentToken: o.paymentToken, totalPrice, quantity }, this.deps.config) : buildFulfillCalls({ orderHash: orderRef, paymentToken: o.paymentToken, totalPrice }, this.deps.config);
12200
+ return signer.execute(calls);
12743
12201
  }
12744
12202
  async cancelOrder(signer, orderRef) {
12745
12203
  const o = await this.deps.resolveOrder(orderRef);
12746
- if (o.standard === "ERC1155") {
12747
- return this.m1155.cancelOrder(signer, { orderHash: orderRef });
12748
- }
12749
- return this.m721.cancelOrder(signer, { orderHash: orderRef });
12204
+ const typedData = o.standard === "ERC1155" ? buildCancel1155TypedData(orderRef, signer.address, this.deps.config) : buildCancelTypedData(orderRef, signer.address, this.deps.config);
12205
+ const signature = await signer.signTypedData(typedData);
12206
+ const calls = o.standard === "ERC1155" ? buildCancel1155Calls({ orderHash: orderRef, offerer: signer.address, signature }, this.deps.config) : buildCancelCalls({ orderHash: orderRef, offerer: signer.address, signature }, this.deps.config);
12207
+ return signer.execute(calls);
12750
12208
  }
12751
12209
  async registerOrder(signer, p) {
12752
12210
  const standard = await this.deps.resolveStandard(p.asset.contract);
12753
- const token = resolveToken(p.paymentToken);
12754
- const humanPrice = formatAmount(p.amount, token.decimals);
12755
- const durationSeconds = this.durationSeconds(p.endTime);
12211
+ const paymentTokenAddress = resolveToken(p.paymentToken).address;
12756
12212
  const royaltyMaxBps = String(p.royaltyMaxBps);
12213
+ const startTime = Math.floor(Date.now() / 1e3) + START_TIME_BUFFER_SECS;
12214
+ const endTime = p.endTime && p.endTime > 0 ? p.endTime : startTime + NO_EXPIRY_SECONDS;
12757
12215
  const quantity = p.quantity ?? "1";
12758
- let txHash;
12216
+ const marketplace = standard === "ERC1155" ? this.deps.config.marketplace1155Contract : this.deps.config.marketplaceContract;
12217
+ const counter = String(await this.readCounter(marketplace, signer.address));
12218
+ let typedData;
12219
+ let buildCalls;
12759
12220
  if (standard === "ERC1155") {
12760
- if (p.side === "listing") {
12761
- const res = await this.m1155.createListing(signer, {
12221
+ const built = (p.side === "listing" ? buildListing1155Order : buildOffer1155Order)(
12222
+ {
12223
+ offerer: signer.address,
12762
12224
  nftContract: p.asset.contract,
12763
12225
  tokenId: p.asset.tokenId,
12764
- amount: quantity,
12765
- pricePerUnit: humanPrice,
12766
- currency: p.paymentToken,
12767
- durationSeconds,
12768
- royaltyMaxBps
12769
- });
12770
- txHash = res.txHash;
12771
- } else {
12772
- const totalHuman = formatAmount((BigInt(p.amount) * BigInt(quantity)).toString(), token.decimals);
12773
- const res = await this.m1155.makeOffer(signer, {
12226
+ quantity,
12227
+ priceWeiPerUnit: p.amount,
12228
+ paymentTokenAddress,
12229
+ royaltyMaxBps,
12230
+ startTime,
12231
+ endTime,
12232
+ salt: p.salt,
12233
+ counter
12234
+ },
12235
+ this.deps.config
12236
+ );
12237
+ typedData = built.typedData;
12238
+ const approval = p.side === "listing" ? await this.approval1155ForListing(signer.address, p.asset.contract) : this.approvalForErc20(paymentTokenAddress, (BigInt(p.amount) * BigInt(quantity)).toString(), marketplace);
12239
+ buildCalls = (sig) => buildRegister1155Calls({ orderParams: built.orderParams, signature: sig, ...approval }, this.deps.config);
12240
+ } else {
12241
+ const built = (p.side === "listing" ? buildListingOrder : buildOfferOrder)(
12242
+ {
12243
+ offerer: signer.address,
12774
12244
  nftContract: p.asset.contract,
12775
12245
  tokenId: p.asset.tokenId,
12776
- amount: quantity,
12777
- price: totalHuman,
12778
- currency: p.paymentToken,
12779
- durationSeconds,
12780
- royaltyMaxBps
12781
- });
12782
- txHash = res.txHash;
12783
- }
12784
- } else {
12785
- const params = {
12786
- nftContract: p.asset.contract,
12787
- tokenId: p.asset.tokenId,
12788
- price: humanPrice,
12789
- currency: p.paymentToken,
12790
- durationSeconds,
12791
- royaltyMaxBps
12792
- };
12793
- const res = p.side === "listing" ? await this.m721.createListing(signer, params) : await this.m721.makeOffer(signer, params);
12794
- txHash = res.txHash;
12246
+ priceWei: p.amount,
12247
+ paymentTokenAddress,
12248
+ royaltyMaxBps,
12249
+ startTime,
12250
+ endTime,
12251
+ salt: p.salt,
12252
+ counter
12253
+ },
12254
+ this.deps.config
12255
+ );
12256
+ typedData = built.typedData;
12257
+ const approval = p.side === "listing" ? await this.approval721ForListing(signer.address, p.asset.contract, p.asset.tokenId) : this.approvalForErc20(paymentTokenAddress, p.amount, marketplace);
12258
+ buildCalls = (sig) => buildRegisterCalls({ orderParams: built.orderParams, signature: sig, ...approval }, this.deps.config);
12795
12259
  }
12260
+ const signature = await signer.signTypedData(typedData);
12261
+ const { txHash } = await signer.execute(buildCalls(signature));
12796
12262
  const orderRef = await this.orderRefFromReceipt(txHash);
12797
12263
  return { txHash, orderRef };
12798
12264
  }
12799
- durationSeconds(endTime) {
12800
- if (!endTime) return NO_EXPIRY_SECONDS;
12801
- return Math.max(1, endTime - Math.floor(Date.now() / 1e3));
12265
+ // ─── reads (all on deps.provider) ─────────────────────────────────────────
12266
+ async readCounter(marketplace, address) {
12267
+ const res = await this.deps.provider.callContract({
12268
+ contractAddress: marketplace,
12269
+ entrypoint: "get_counter",
12270
+ calldata: [address]
12271
+ });
12272
+ return BigInt(res[0] ?? "0");
12273
+ }
12274
+ /** 721 listing approval: `get_approved(tokenId) == marketplace` ⇒ no approve. */
12275
+ async approval721ForListing(_owner, nftContract, tokenId) {
12276
+ const id = starknet.cairo.uint256(tokenId);
12277
+ const approve = {
12278
+ contractAddress: nftContract,
12279
+ entrypoint: "approve",
12280
+ calldata: [this.deps.config.marketplaceContract, id.low.toString(), id.high.toString()]
12281
+ };
12282
+ let approved = false;
12283
+ try {
12284
+ const res = await this.deps.provider.callContract({
12285
+ contractAddress: nftContract,
12286
+ entrypoint: "get_approved",
12287
+ calldata: [id.low.toString(), id.high.toString()]
12288
+ });
12289
+ approved = BigInt(res[0]).toString() === BigInt(this.deps.config.marketplaceContract).toString();
12290
+ } catch {
12291
+ }
12292
+ return { approvalNeeded: !approved, approve };
12293
+ }
12294
+ /** 1155 listing approval: `is_approved_for_all(owner, marketplace)`. */
12295
+ async approval1155ForListing(owner, nftContract) {
12296
+ const approve = {
12297
+ contractAddress: nftContract,
12298
+ entrypoint: "set_approval_for_all",
12299
+ calldata: [this.deps.config.marketplace1155Contract, "1"]
12300
+ };
12301
+ let approved = false;
12302
+ try {
12303
+ const res = await this.deps.provider.callContract({
12304
+ contractAddress: nftContract,
12305
+ entrypoint: "is_approved_for_all",
12306
+ calldata: [owner, this.deps.config.marketplace1155Contract]
12307
+ });
12308
+ approved = BigInt(res[0]) === 1n;
12309
+ } catch {
12310
+ }
12311
+ return { approvalNeeded: !approved, approve };
12312
+ }
12313
+ /** Offers always approve the ERC-20 spend (no read). */
12314
+ approvalForErc20(token, amountWei, marketplace) {
12315
+ const u = starknet.cairo.uint256(amountWei);
12316
+ return {
12317
+ approvalNeeded: true,
12318
+ approve: {
12319
+ contractAddress: token,
12320
+ entrypoint: "approve",
12321
+ calldata: [marketplace, u.low.toString(), u.high.toString()]
12322
+ }
12323
+ };
12802
12324
  }
12803
12325
  /** The canonical Starknet order id = the contract-emitted `OrderCreated`
12804
12326
  * hash (`keys[1]`), which is exactly what the indexer stores. */
@@ -13071,6 +12593,30 @@ function buybackQuoteRaw(teamCoinsRawValue, quoteDecimals) {
13071
12593
  function fdvHuman(supplyHuman) {
13072
12594
  return supplyHuman * LAUNCH_PRICE_QUOTE_PER_COIN;
13073
12595
  }
12596
+ function encodeByteArray(str) {
12597
+ const bytes = new TextEncoder().encode(str);
12598
+ const fullChunks = [];
12599
+ let i = 0;
12600
+ while (i + 31 <= bytes.length) {
12601
+ let val = 0n;
12602
+ for (const b of bytes.slice(i, i + 31)) {
12603
+ val = val << 8n | BigInt(b);
12604
+ }
12605
+ fullChunks.push(starknet.num.toHex(val));
12606
+ i += 31;
12607
+ }
12608
+ const remaining = bytes.slice(i);
12609
+ let pendingVal = 0n;
12610
+ for (const b of remaining) {
12611
+ pendingVal = pendingVal << 8n | BigInt(b);
12612
+ }
12613
+ return [
12614
+ fullChunks.length.toString(),
12615
+ ...fullChunks,
12616
+ starknet.num.toHex(pendingVal),
12617
+ remaining.length.toString()
12618
+ ];
12619
+ }
13074
12620
 
13075
12621
  exports.ADMIN_HEADERS = ADMIN_HEADERS;
13076
12622
  exports.ADMIN_SCOPE = ADMIN_SCOPE;
@@ -13098,9 +12644,7 @@ exports.IPSponsorshipLicenseABI = IPSponsorshipLicenseABI;
13098
12644
  exports.IPTicketCollectionABI = IPTicketCollectionABI;
13099
12645
  exports.IPTicketCollectionFactoryABI = IPTicketCollectionFactoryABI;
13100
12646
  exports.LAUNCH_PRICE_QUOTE_PER_COIN = LAUNCH_PRICE_QUOTE_PER_COIN;
13101
- exports.MarketplaceModule = MarketplaceModule;
13102
12647
  exports.Medialane1155ABI = Medialane1155ABI;
13103
- exports.Medialane1155Module = Medialane1155Module;
13104
12648
  exports.MedialaneClient = MedialaneClient;
13105
12649
  exports.MedialaneError = MedialaneError;
13106
12650
  exports.POPCollectionABI = POPCollectionABI;
@@ -13125,7 +12669,11 @@ exports.createAdminSessionGrant = createAdminSessionGrant;
13125
12669
  exports.encodeAdminHeaders = encodeAdminHeaders;
13126
12670
  exports.encodeByteArray = encodeByteArray;
13127
12671
  exports.fdvHuman = fdvHuman;
12672
+ exports.getCounter = getCounter;
12673
+ exports.getCounter1155 = getCounter1155;
13128
12674
  exports.getCreatorCoinPrice = getCreatorCoinPrice;
12675
+ exports.getOrderDetails = getOrderDetails;
12676
+ exports.getOrderDetails1155 = getOrderDetails1155;
13129
12677
  exports.getSiwsStorageKey = getSiwsStorageKey;
13130
12678
  exports.getStoredSiwsToken = getStoredSiwsToken;
13131
12679
  exports.isSiwsTokenValid = isSiwsTokenValid;