@medialane/sdk 0.3.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -0
- package/dist/index.cjs +208 -141
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +49 -6
- package/dist/index.d.ts +49 -6
- package/dist/index.js +208 -141
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -29,6 +29,12 @@ declare const SUPPORTED_NETWORKS: readonly ["mainnet", "sepolia"];
|
|
|
29
29
|
type Network = (typeof SUPPORTED_NETWORKS)[number];
|
|
30
30
|
declare const DEFAULT_RPC_URLS: Record<Network, string>;
|
|
31
31
|
|
|
32
|
+
interface RetryOptions {
|
|
33
|
+
maxAttempts?: number;
|
|
34
|
+
baseDelayMs?: number;
|
|
35
|
+
maxDelayMs?: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
32
38
|
declare const MedialaneConfigSchema: z.ZodObject<{
|
|
33
39
|
network: z.ZodDefault<z.ZodEnum<["mainnet", "sepolia"]>>;
|
|
34
40
|
rpcUrl: z.ZodOptional<z.ZodString>;
|
|
@@ -37,20 +43,43 @@ declare const MedialaneConfigSchema: z.ZodObject<{
|
|
|
37
43
|
apiKey: z.ZodOptional<z.ZodString>;
|
|
38
44
|
marketplaceContract: z.ZodOptional<z.ZodString>;
|
|
39
45
|
collectionContract: z.ZodOptional<z.ZodString>;
|
|
46
|
+
retryOptions: z.ZodOptional<z.ZodObject<{
|
|
47
|
+
maxAttempts: z.ZodOptional<z.ZodNumber>;
|
|
48
|
+
baseDelayMs: z.ZodOptional<z.ZodNumber>;
|
|
49
|
+
maxDelayMs: z.ZodOptional<z.ZodNumber>;
|
|
50
|
+
}, "strip", z.ZodTypeAny, {
|
|
51
|
+
maxAttempts?: number | undefined;
|
|
52
|
+
baseDelayMs?: number | undefined;
|
|
53
|
+
maxDelayMs?: number | undefined;
|
|
54
|
+
}, {
|
|
55
|
+
maxAttempts?: number | undefined;
|
|
56
|
+
baseDelayMs?: number | undefined;
|
|
57
|
+
maxDelayMs?: number | undefined;
|
|
58
|
+
}>>;
|
|
40
59
|
}, "strip", z.ZodTypeAny, {
|
|
41
60
|
network: "mainnet" | "sepolia";
|
|
61
|
+
collectionContract?: string | undefined;
|
|
42
62
|
rpcUrl?: string | undefined;
|
|
43
63
|
backendUrl?: string | undefined;
|
|
44
64
|
apiKey?: string | undefined;
|
|
45
65
|
marketplaceContract?: string | undefined;
|
|
46
|
-
|
|
66
|
+
retryOptions?: {
|
|
67
|
+
maxAttempts?: number | undefined;
|
|
68
|
+
baseDelayMs?: number | undefined;
|
|
69
|
+
maxDelayMs?: number | undefined;
|
|
70
|
+
} | undefined;
|
|
47
71
|
}, {
|
|
72
|
+
collectionContract?: string | undefined;
|
|
48
73
|
network?: "mainnet" | "sepolia" | undefined;
|
|
49
74
|
rpcUrl?: string | undefined;
|
|
50
75
|
backendUrl?: string | undefined;
|
|
51
76
|
apiKey?: string | undefined;
|
|
52
77
|
marketplaceContract?: string | undefined;
|
|
53
|
-
|
|
78
|
+
retryOptions?: {
|
|
79
|
+
maxAttempts?: number | undefined;
|
|
80
|
+
baseDelayMs?: number | undefined;
|
|
81
|
+
maxDelayMs?: number | undefined;
|
|
82
|
+
} | undefined;
|
|
54
83
|
}>;
|
|
55
84
|
type MedialaneConfig = z.input<typeof MedialaneConfigSchema>;
|
|
56
85
|
interface ResolvedConfig {
|
|
@@ -60,6 +89,7 @@ interface ResolvedConfig {
|
|
|
60
89
|
apiKey: string | undefined;
|
|
61
90
|
marketplaceContract: string;
|
|
62
91
|
collectionContract: string;
|
|
92
|
+
retryOptions?: RetryOptions;
|
|
63
93
|
}
|
|
64
94
|
declare function resolveConfig(raw: MedialaneConfig): ResolvedConfig;
|
|
65
95
|
|
|
@@ -145,9 +175,12 @@ interface TxResult {
|
|
|
145
175
|
txHash: string;
|
|
146
176
|
}
|
|
147
177
|
|
|
178
|
+
type MedialaneErrorCode = "TOKEN_NOT_FOUND" | "COLLECTION_NOT_FOUND" | "ORDER_NOT_FOUND" | "INTENT_NOT_FOUND" | "INTENT_EXPIRED" | "RATE_LIMITED" | "NETWORK_NOT_SUPPORTED" | "APPROVAL_FAILED" | "TRANSACTION_FAILED" | "INVALID_PARAMS" | "UNAUTHORIZED" | "UNKNOWN";
|
|
179
|
+
|
|
148
180
|
declare class MedialaneError extends Error {
|
|
181
|
+
readonly code: MedialaneErrorCode;
|
|
149
182
|
readonly cause?: unknown | undefined;
|
|
150
|
-
constructor(message: string, cause?: unknown | undefined);
|
|
183
|
+
constructor(message: string, code?: MedialaneErrorCode, cause?: unknown | undefined);
|
|
151
184
|
}
|
|
152
185
|
|
|
153
186
|
declare class MarketplaceModule {
|
|
@@ -165,6 +198,14 @@ declare class MarketplaceModule {
|
|
|
165
198
|
buildCancellationTypedData(params: Record<string, unknown>, chainId: constants.StarknetChainId): TypedData;
|
|
166
199
|
}
|
|
167
200
|
|
|
201
|
+
type CollectionSort = "recent" | "supply" | "floor" | "volume" | "name";
|
|
202
|
+
interface ApiCollectionsQuery {
|
|
203
|
+
page?: number;
|
|
204
|
+
limit?: number;
|
|
205
|
+
isKnown?: boolean;
|
|
206
|
+
sort?: CollectionSort;
|
|
207
|
+
owner?: string;
|
|
208
|
+
}
|
|
168
209
|
type OrderStatus = "ACTIVE" | "FULFILLED" | "CANCELLED" | "EXPIRED";
|
|
169
210
|
type SortOrder = "price_asc" | "price_desc" | "recent";
|
|
170
211
|
type ActivityType = "transfer" | "sale" | "listing" | "offer" | "cancelled";
|
|
@@ -494,12 +535,14 @@ interface CreateWebhookParams {
|
|
|
494
535
|
|
|
495
536
|
declare class MedialaneApiError extends Error {
|
|
496
537
|
readonly status: number;
|
|
538
|
+
readonly code: MedialaneErrorCode;
|
|
497
539
|
constructor(status: number, message: string);
|
|
498
540
|
}
|
|
499
541
|
declare class ApiClient {
|
|
500
542
|
private readonly baseUrl;
|
|
501
543
|
private readonly baseHeaders;
|
|
502
|
-
|
|
544
|
+
private readonly retryOptions;
|
|
545
|
+
constructor(baseUrl: string, apiKey?: string, retryOptions?: RetryOptions);
|
|
503
546
|
private request;
|
|
504
547
|
private get;
|
|
505
548
|
private post;
|
|
@@ -512,7 +555,7 @@ declare class ApiClient {
|
|
|
512
555
|
getToken(contract: string, tokenId: string, wait?: boolean): Promise<ApiResponse<ApiToken>>;
|
|
513
556
|
getTokensByOwner(address: string, page?: number, limit?: number): Promise<ApiResponse<ApiToken[]>>;
|
|
514
557
|
getTokenHistory(contract: string, tokenId: string, page?: number, limit?: number): Promise<ApiResponse<ApiActivity[]>>;
|
|
515
|
-
getCollections(page?: number, limit?: number, isKnown?: boolean): Promise<ApiResponse<ApiCollection[]>>;
|
|
558
|
+
getCollections(page?: number, limit?: number, isKnown?: boolean, sort?: CollectionSort): Promise<ApiResponse<ApiCollection[]>>;
|
|
516
559
|
getCollectionsByOwner(owner: string, page?: number, limit?: number): Promise<ApiResponse<ApiCollection[]>>;
|
|
517
560
|
getCollection(contract: string): Promise<ApiResponse<ApiCollection>>;
|
|
518
561
|
getCollectionTokens(contract: string, page?: number, limit?: number): Promise<ApiResponse<ApiToken[]>>;
|
|
@@ -989,4 +1032,4 @@ declare function buildFulfillmentTypedData(message: Record<string, unknown>, cha
|
|
|
989
1032
|
*/
|
|
990
1033
|
declare function buildCancellationTypedData(message: Record<string, unknown>, chainId: constants.StarknetChainId): TypedData;
|
|
991
1034
|
|
|
992
|
-
export { type ActivityType, type ApiActivitiesQuery, type ApiActivity, type ApiActivityPrice, ApiClient, type ApiCollection, type ApiIntent, type ApiIntentCreated, type ApiKeyStatus, type ApiMeta, type ApiMetadataSignedUrl, type ApiMetadataUpload, type ApiOrder, type ApiOrderConsideration, type ApiOrderOffer, type ApiOrderPrice, type ApiOrderTokenMeta, type ApiOrderTxHash, type ApiOrdersQuery, type ApiPortalKey, type ApiPortalKeyCreated, type ApiPortalMe, type ApiResponse, type ApiSearchCollectionResult, type ApiSearchResult, type ApiSearchTokenResult, type ApiToken, type ApiTokenMetadata, type ApiUsageDay, type ApiWebhookCreated, type ApiWebhookEndpoint, COLLECTION_CONTRACT_MAINNET, type CancelOrderIntentParams, type CancelOrderParams, type Cancelation, type CartItem, type ConsiderationItem, type CreateCollectionIntentParams, type CreateCollectionParams, type CreateListingIntentParams, type CreateListingParams, type CreateMintIntentParams, type CreateWebhookParams, DEFAULT_RPC_URLS, type FulfillOrderIntentParams, type FulfillOrderParams, type Fulfillment, IPMarketplaceABI, type IntentStatus, type IntentType, type IpAttribute, type IpNftMetadata, MARKETPLACE_CONTRACT_MAINNET, type MakeOfferIntentParams, type MakeOfferParams, MarketplaceModule, MedialaneApiError, MedialaneClient, type MedialaneConfig, MedialaneError, type MintParams, type Network, type OfferItem, type Order, type OrderParameters, type OrderStatus, type ResolvedConfig, SUPPORTED_NETWORKS, SUPPORTED_TOKENS, type SortOrder, type SupportedTokenSymbol, type TenantPlan, type TxResult, type WebhookEventType, type WebhookStatus, buildCancellationTypedData, buildFulfillmentTypedData, buildOrderTypedData, formatAmount, getTokenByAddress, getTokenBySymbol, normalizeAddress, parseAmount, resolveConfig, shortenAddress, stringifyBigInts, u256ToBigInt };
|
|
1035
|
+
export { type ActivityType, type ApiActivitiesQuery, type ApiActivity, type ApiActivityPrice, ApiClient, type ApiCollection, type ApiCollectionsQuery, type ApiIntent, type ApiIntentCreated, type ApiKeyStatus, type ApiMeta, type ApiMetadataSignedUrl, type ApiMetadataUpload, type ApiOrder, type ApiOrderConsideration, type ApiOrderOffer, type ApiOrderPrice, type ApiOrderTokenMeta, type ApiOrderTxHash, type ApiOrdersQuery, type ApiPortalKey, type ApiPortalKeyCreated, type ApiPortalMe, type ApiResponse, type ApiSearchCollectionResult, type ApiSearchResult, type ApiSearchTokenResult, type ApiToken, type ApiTokenMetadata, type ApiUsageDay, type ApiWebhookCreated, type ApiWebhookEndpoint, COLLECTION_CONTRACT_MAINNET, type CancelOrderIntentParams, type CancelOrderParams, type Cancelation, type CartItem, type CollectionSort, type ConsiderationItem, type CreateCollectionIntentParams, type CreateCollectionParams, type CreateListingIntentParams, type CreateListingParams, type CreateMintIntentParams, type CreateWebhookParams, DEFAULT_RPC_URLS, type FulfillOrderIntentParams, type FulfillOrderParams, type Fulfillment, IPMarketplaceABI, type IntentStatus, type IntentType, type IpAttribute, type IpNftMetadata, MARKETPLACE_CONTRACT_MAINNET, type MakeOfferIntentParams, type MakeOfferParams, MarketplaceModule, MedialaneApiError, MedialaneClient, type MedialaneConfig, MedialaneError, type MedialaneErrorCode, type MintParams, type Network, type OfferItem, type Order, type OrderParameters, type OrderStatus, type ResolvedConfig, type RetryOptions, SUPPORTED_NETWORKS, SUPPORTED_TOKENS, type SortOrder, type SupportedTokenSymbol, type TenantPlan, type TxResult, type WebhookEventType, type WebhookStatus, buildCancellationTypedData, buildFulfillmentTypedData, buildOrderTypedData, formatAmount, getTokenByAddress, getTokenBySymbol, normalizeAddress, parseAmount, resolveConfig, shortenAddress, stringifyBigInts, u256ToBigInt };
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,8 @@ import { TypedDataRevision, shortString, cairo, Contract, constants, RpcProvider
|
|
|
6
6
|
// src/constants.ts
|
|
7
7
|
var MARKETPLACE_CONTRACT_MAINNET = "0x04299b51289aa700de4ce19cc77bcea8430bfd1aef04193efab09d60a3a7ee0f";
|
|
8
8
|
var COLLECTION_CONTRACT_MAINNET = "0x05e73b7be06d82beeb390a0e0d655f2c9e7cf519658e04f05d9c690ccc41da03";
|
|
9
|
+
var MARKETPLACE_CONTRACT_SEPOLIA = "";
|
|
10
|
+
var COLLECTION_CONTRACT_SEPOLIA = "";
|
|
9
11
|
var SUPPORTED_TOKENS = [
|
|
10
12
|
{
|
|
11
13
|
// Circle-native USDC on Starknet (canonical — preferred)
|
|
@@ -42,122 +44,6 @@ var DEFAULT_RPC_URLS = {
|
|
|
42
44
|
sepolia: "https://rpc.starknet-sepolia.lava.build"
|
|
43
45
|
};
|
|
44
46
|
|
|
45
|
-
// src/config.ts
|
|
46
|
-
var MedialaneConfigSchema = z.object({
|
|
47
|
-
network: z.enum(SUPPORTED_NETWORKS).default("mainnet"),
|
|
48
|
-
rpcUrl: z.string().url().optional(),
|
|
49
|
-
backendUrl: z.string().url().optional(),
|
|
50
|
-
/** API key for authenticated /v1/* backend endpoints */
|
|
51
|
-
apiKey: z.string().optional(),
|
|
52
|
-
marketplaceContract: z.string().optional(),
|
|
53
|
-
collectionContract: z.string().optional()
|
|
54
|
-
});
|
|
55
|
-
function resolveConfig(raw) {
|
|
56
|
-
const parsed = MedialaneConfigSchema.parse(raw);
|
|
57
|
-
return {
|
|
58
|
-
network: parsed.network,
|
|
59
|
-
rpcUrl: parsed.rpcUrl ?? DEFAULT_RPC_URLS[parsed.network],
|
|
60
|
-
backendUrl: parsed.backendUrl,
|
|
61
|
-
apiKey: parsed.apiKey,
|
|
62
|
-
marketplaceContract: parsed.marketplaceContract ?? MARKETPLACE_CONTRACT_MAINNET,
|
|
63
|
-
collectionContract: parsed.collectionContract ?? COLLECTION_CONTRACT_MAINNET
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
function buildOrderTypedData(message, chainId) {
|
|
67
|
-
return {
|
|
68
|
-
domain: {
|
|
69
|
-
name: "Medialane",
|
|
70
|
-
version: "1",
|
|
71
|
-
chainId,
|
|
72
|
-
revision: TypedDataRevision.ACTIVE
|
|
73
|
-
},
|
|
74
|
-
primaryType: "OrderParameters",
|
|
75
|
-
types: {
|
|
76
|
-
StarknetDomain: [
|
|
77
|
-
{ name: "name", type: "shortstring" },
|
|
78
|
-
{ name: "version", type: "shortstring" },
|
|
79
|
-
{ name: "chainId", type: "shortstring" },
|
|
80
|
-
{ name: "revision", type: "shortstring" }
|
|
81
|
-
],
|
|
82
|
-
OrderParameters: [
|
|
83
|
-
{ name: "offerer", type: "ContractAddress" },
|
|
84
|
-
{ name: "offer", type: "OfferItem" },
|
|
85
|
-
{ name: "consideration", type: "ConsiderationItem" },
|
|
86
|
-
{ name: "start_time", type: "felt" },
|
|
87
|
-
{ name: "end_time", type: "felt" },
|
|
88
|
-
{ name: "salt", type: "felt" },
|
|
89
|
-
{ name: "nonce", type: "felt" }
|
|
90
|
-
],
|
|
91
|
-
OfferItem: [
|
|
92
|
-
{ name: "item_type", type: "shortstring" },
|
|
93
|
-
{ name: "token", type: "ContractAddress" },
|
|
94
|
-
{ name: "identifier_or_criteria", type: "felt" },
|
|
95
|
-
{ name: "start_amount", type: "felt" },
|
|
96
|
-
{ name: "end_amount", type: "felt" }
|
|
97
|
-
],
|
|
98
|
-
ConsiderationItem: [
|
|
99
|
-
{ name: "item_type", type: "shortstring" },
|
|
100
|
-
{ name: "token", type: "ContractAddress" },
|
|
101
|
-
{ name: "identifier_or_criteria", type: "felt" },
|
|
102
|
-
{ name: "start_amount", type: "felt" },
|
|
103
|
-
{ name: "end_amount", type: "felt" },
|
|
104
|
-
{ name: "recipient", type: "ContractAddress" }
|
|
105
|
-
]
|
|
106
|
-
},
|
|
107
|
-
message
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
function buildFulfillmentTypedData(message, chainId) {
|
|
111
|
-
return {
|
|
112
|
-
domain: {
|
|
113
|
-
name: "Medialane",
|
|
114
|
-
version: "1",
|
|
115
|
-
chainId,
|
|
116
|
-
revision: TypedDataRevision.ACTIVE
|
|
117
|
-
},
|
|
118
|
-
primaryType: "OrderFulfillment",
|
|
119
|
-
types: {
|
|
120
|
-
StarknetDomain: [
|
|
121
|
-
{ name: "name", type: "shortstring" },
|
|
122
|
-
{ name: "version", type: "shortstring" },
|
|
123
|
-
{ name: "chainId", type: "shortstring" },
|
|
124
|
-
{ name: "revision", type: "shortstring" }
|
|
125
|
-
],
|
|
126
|
-
OrderFulfillment: [
|
|
127
|
-
{ name: "order_hash", type: "felt" },
|
|
128
|
-
{ name: "fulfiller", type: "ContractAddress" },
|
|
129
|
-
{ name: "nonce", type: "felt" }
|
|
130
|
-
]
|
|
131
|
-
},
|
|
132
|
-
message
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
function buildCancellationTypedData(message, chainId) {
|
|
136
|
-
return {
|
|
137
|
-
domain: {
|
|
138
|
-
name: "Medialane",
|
|
139
|
-
version: "1",
|
|
140
|
-
chainId,
|
|
141
|
-
revision: TypedDataRevision.ACTIVE
|
|
142
|
-
},
|
|
143
|
-
primaryType: "OrderCancellation",
|
|
144
|
-
types: {
|
|
145
|
-
StarknetDomain: [
|
|
146
|
-
{ name: "name", type: "shortstring" },
|
|
147
|
-
{ name: "version", type: "shortstring" },
|
|
148
|
-
{ name: "chainId", type: "shortstring" },
|
|
149
|
-
{ name: "revision", type: "shortstring" }
|
|
150
|
-
],
|
|
151
|
-
OrderCancellation: [
|
|
152
|
-
{ name: "order_hash", type: "felt" },
|
|
153
|
-
{ name: "offerer", type: "ContractAddress" },
|
|
154
|
-
{ name: "nonce", type: "felt" }
|
|
155
|
-
]
|
|
156
|
-
},
|
|
157
|
-
message
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
|
|
161
47
|
// src/abis.ts
|
|
162
48
|
var IPMarketplaceABI = [
|
|
163
49
|
{
|
|
@@ -510,11 +396,106 @@ function getTokenBySymbol(symbol) {
|
|
|
510
396
|
const upper = symbol.toUpperCase();
|
|
511
397
|
return SUPPORTED_TOKENS.find((t) => t.symbol === upper);
|
|
512
398
|
}
|
|
399
|
+
function buildOrderTypedData(message, chainId) {
|
|
400
|
+
return {
|
|
401
|
+
domain: {
|
|
402
|
+
name: "Medialane",
|
|
403
|
+
version: "1",
|
|
404
|
+
chainId,
|
|
405
|
+
revision: TypedDataRevision.ACTIVE
|
|
406
|
+
},
|
|
407
|
+
primaryType: "OrderParameters",
|
|
408
|
+
types: {
|
|
409
|
+
StarknetDomain: [
|
|
410
|
+
{ name: "name", type: "shortstring" },
|
|
411
|
+
{ name: "version", type: "shortstring" },
|
|
412
|
+
{ name: "chainId", type: "shortstring" },
|
|
413
|
+
{ name: "revision", type: "shortstring" }
|
|
414
|
+
],
|
|
415
|
+
OrderParameters: [
|
|
416
|
+
{ name: "offerer", type: "ContractAddress" },
|
|
417
|
+
{ name: "offer", type: "OfferItem" },
|
|
418
|
+
{ name: "consideration", type: "ConsiderationItem" },
|
|
419
|
+
{ name: "start_time", type: "felt" },
|
|
420
|
+
{ name: "end_time", type: "felt" },
|
|
421
|
+
{ name: "salt", type: "felt" },
|
|
422
|
+
{ name: "nonce", type: "felt" }
|
|
423
|
+
],
|
|
424
|
+
OfferItem: [
|
|
425
|
+
{ name: "item_type", type: "shortstring" },
|
|
426
|
+
{ name: "token", type: "ContractAddress" },
|
|
427
|
+
{ name: "identifier_or_criteria", type: "felt" },
|
|
428
|
+
{ name: "start_amount", type: "felt" },
|
|
429
|
+
{ name: "end_amount", type: "felt" }
|
|
430
|
+
],
|
|
431
|
+
ConsiderationItem: [
|
|
432
|
+
{ name: "item_type", type: "shortstring" },
|
|
433
|
+
{ name: "token", type: "ContractAddress" },
|
|
434
|
+
{ name: "identifier_or_criteria", type: "felt" },
|
|
435
|
+
{ name: "start_amount", type: "felt" },
|
|
436
|
+
{ name: "end_amount", type: "felt" },
|
|
437
|
+
{ name: "recipient", type: "ContractAddress" }
|
|
438
|
+
]
|
|
439
|
+
},
|
|
440
|
+
message
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
function buildFulfillmentTypedData(message, chainId) {
|
|
444
|
+
return {
|
|
445
|
+
domain: {
|
|
446
|
+
name: "Medialane",
|
|
447
|
+
version: "1",
|
|
448
|
+
chainId,
|
|
449
|
+
revision: TypedDataRevision.ACTIVE
|
|
450
|
+
},
|
|
451
|
+
primaryType: "OrderFulfillment",
|
|
452
|
+
types: {
|
|
453
|
+
StarknetDomain: [
|
|
454
|
+
{ name: "name", type: "shortstring" },
|
|
455
|
+
{ name: "version", type: "shortstring" },
|
|
456
|
+
{ name: "chainId", type: "shortstring" },
|
|
457
|
+
{ name: "revision", type: "shortstring" }
|
|
458
|
+
],
|
|
459
|
+
OrderFulfillment: [
|
|
460
|
+
{ name: "order_hash", type: "felt" },
|
|
461
|
+
{ name: "fulfiller", type: "ContractAddress" },
|
|
462
|
+
{ name: "nonce", type: "felt" }
|
|
463
|
+
]
|
|
464
|
+
},
|
|
465
|
+
message
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
function buildCancellationTypedData(message, chainId) {
|
|
469
|
+
return {
|
|
470
|
+
domain: {
|
|
471
|
+
name: "Medialane",
|
|
472
|
+
version: "1",
|
|
473
|
+
chainId,
|
|
474
|
+
revision: TypedDataRevision.ACTIVE
|
|
475
|
+
},
|
|
476
|
+
primaryType: "OrderCancellation",
|
|
477
|
+
types: {
|
|
478
|
+
StarknetDomain: [
|
|
479
|
+
{ name: "name", type: "shortstring" },
|
|
480
|
+
{ name: "version", type: "shortstring" },
|
|
481
|
+
{ name: "chainId", type: "shortstring" },
|
|
482
|
+
{ name: "revision", type: "shortstring" }
|
|
483
|
+
],
|
|
484
|
+
OrderCancellation: [
|
|
485
|
+
{ name: "order_hash", type: "felt" },
|
|
486
|
+
{ name: "offerer", type: "ContractAddress" },
|
|
487
|
+
{ name: "nonce", type: "felt" }
|
|
488
|
+
]
|
|
489
|
+
},
|
|
490
|
+
message
|
|
491
|
+
};
|
|
492
|
+
}
|
|
513
493
|
|
|
514
494
|
// src/marketplace/orders.ts
|
|
515
495
|
var MedialaneError = class extends Error {
|
|
516
|
-
constructor(message, cause) {
|
|
496
|
+
constructor(message, code = "UNKNOWN", cause) {
|
|
517
497
|
super(message);
|
|
498
|
+
this.code = code;
|
|
518
499
|
this.cause = cause;
|
|
519
500
|
this.name = "MedialaneError";
|
|
520
501
|
}
|
|
@@ -554,7 +535,7 @@ function resolveToken(currency) {
|
|
|
554
535
|
const token = SUPPORTED_TOKENS.find(
|
|
555
536
|
(t) => t.symbol === currency.toUpperCase() || t.address.toLowerCase() === currency.toLowerCase()
|
|
556
537
|
);
|
|
557
|
-
if (!token) throw new MedialaneError(`Unsupported currency: ${currency}
|
|
538
|
+
if (!token) throw new MedialaneError(`Unsupported currency: ${currency}`, "INVALID_PARAMS");
|
|
558
539
|
return token;
|
|
559
540
|
}
|
|
560
541
|
async function createListing(account, params, config) {
|
|
@@ -638,7 +619,7 @@ async function createListing(account, params, config) {
|
|
|
638
619
|
await provider.waitForTransaction(tx.transaction_hash);
|
|
639
620
|
return { txHash: tx.transaction_hash };
|
|
640
621
|
} catch (err) {
|
|
641
|
-
throw new MedialaneError("Failed to create listing", err);
|
|
622
|
+
throw new MedialaneError("Failed to create listing", "TRANSACTION_FAILED", err);
|
|
642
623
|
}
|
|
643
624
|
}
|
|
644
625
|
async function makeOffer(account, params, config) {
|
|
@@ -709,7 +690,7 @@ async function makeOffer(account, params, config) {
|
|
|
709
690
|
await provider.waitForTransaction(tx.transaction_hash);
|
|
710
691
|
return { txHash: tx.transaction_hash };
|
|
711
692
|
} catch (err) {
|
|
712
|
-
throw new MedialaneError("Failed to make offer", err);
|
|
693
|
+
throw new MedialaneError("Failed to make offer", "TRANSACTION_FAILED", err);
|
|
713
694
|
}
|
|
714
695
|
}
|
|
715
696
|
async function fulfillOrder(account, params, config) {
|
|
@@ -737,7 +718,7 @@ async function fulfillOrder(account, params, config) {
|
|
|
737
718
|
await provider.waitForTransaction(tx.transaction_hash);
|
|
738
719
|
return { txHash: tx.transaction_hash };
|
|
739
720
|
} catch (err) {
|
|
740
|
-
throw new MedialaneError("Failed to fulfill order", err);
|
|
721
|
+
throw new MedialaneError("Failed to fulfill order", "TRANSACTION_FAILED", err);
|
|
741
722
|
}
|
|
742
723
|
}
|
|
743
724
|
async function cancelOrder(account, params, config) {
|
|
@@ -765,7 +746,7 @@ async function cancelOrder(account, params, config) {
|
|
|
765
746
|
await provider.waitForTransaction(tx.transaction_hash);
|
|
766
747
|
return { txHash: tx.transaction_hash };
|
|
767
748
|
} catch (err) {
|
|
768
|
-
throw new MedialaneError("Failed to cancel order", err);
|
|
749
|
+
throw new MedialaneError("Failed to cancel order", "TRANSACTION_FAILED", err);
|
|
769
750
|
}
|
|
770
751
|
}
|
|
771
752
|
function encodeByteArray(str) {
|
|
@@ -788,7 +769,7 @@ async function mint(account, params, config) {
|
|
|
788
769
|
await provider.waitForTransaction(tx.transaction_hash);
|
|
789
770
|
return { txHash: tx.transaction_hash };
|
|
790
771
|
} catch (err) {
|
|
791
|
-
throw new MedialaneError("Failed to mint NFT", err);
|
|
772
|
+
throw new MedialaneError("Failed to mint NFT", "TRANSACTION_FAILED", err);
|
|
792
773
|
}
|
|
793
774
|
}
|
|
794
775
|
async function createCollection(account, params, config) {
|
|
@@ -805,11 +786,11 @@ async function createCollection(account, params, config) {
|
|
|
805
786
|
await provider.waitForTransaction(tx.transaction_hash);
|
|
806
787
|
return { txHash: tx.transaction_hash };
|
|
807
788
|
} catch (err) {
|
|
808
|
-
throw new MedialaneError("Failed to create collection", err);
|
|
789
|
+
throw new MedialaneError("Failed to create collection", "TRANSACTION_FAILED", err);
|
|
809
790
|
}
|
|
810
791
|
}
|
|
811
792
|
async function checkoutCart(account, items, config) {
|
|
812
|
-
if (items.length === 0) throw new MedialaneError("Cart is empty");
|
|
793
|
+
if (items.length === 0) throw new MedialaneError("Cart is empty", "INVALID_PARAMS");
|
|
813
794
|
const { contract, provider } = makeContract(config);
|
|
814
795
|
const tokenTotals = /* @__PURE__ */ new Map();
|
|
815
796
|
for (const item of items) {
|
|
@@ -856,8 +837,47 @@ async function checkoutCart(account, items, config) {
|
|
|
856
837
|
await provider.waitForTransaction(tx.transaction_hash);
|
|
857
838
|
return { txHash: tx.transaction_hash };
|
|
858
839
|
} catch (err) {
|
|
859
|
-
throw new MedialaneError("Cart checkout failed", err);
|
|
840
|
+
throw new MedialaneError("Cart checkout failed", "TRANSACTION_FAILED", err);
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
// src/config.ts
|
|
845
|
+
var MedialaneConfigSchema = z.object({
|
|
846
|
+
network: z.enum(SUPPORTED_NETWORKS).default("mainnet"),
|
|
847
|
+
rpcUrl: z.string().url().optional(),
|
|
848
|
+
backendUrl: z.string().url().optional(),
|
|
849
|
+
/** API key for authenticated /v1/* backend endpoints */
|
|
850
|
+
apiKey: z.string().optional(),
|
|
851
|
+
marketplaceContract: z.string().optional(),
|
|
852
|
+
collectionContract: z.string().optional(),
|
|
853
|
+
retryOptions: z.object({
|
|
854
|
+
maxAttempts: z.number().int().min(1).max(10).optional(),
|
|
855
|
+
baseDelayMs: z.number().int().min(0).optional(),
|
|
856
|
+
maxDelayMs: z.number().int().min(0).optional()
|
|
857
|
+
}).optional()
|
|
858
|
+
});
|
|
859
|
+
function resolveConfig(raw) {
|
|
860
|
+
const parsed = MedialaneConfigSchema.parse(raw);
|
|
861
|
+
const isMainnet = parsed.network === "mainnet";
|
|
862
|
+
const defaultMarketplace = isMainnet ? MARKETPLACE_CONTRACT_MAINNET : MARKETPLACE_CONTRACT_SEPOLIA;
|
|
863
|
+
const defaultCollection = isMainnet ? COLLECTION_CONTRACT_MAINNET : COLLECTION_CONTRACT_SEPOLIA;
|
|
864
|
+
const marketplaceContract = parsed.marketplaceContract ?? defaultMarketplace;
|
|
865
|
+
const collectionContract = parsed.collectionContract ?? defaultCollection;
|
|
866
|
+
if (!marketplaceContract || !collectionContract) {
|
|
867
|
+
throw new MedialaneError(
|
|
868
|
+
`Sepolia network is not yet supported: marketplace and collection contract addresses are not configured. Pass 'marketplaceContract' and 'collectionContract' explicitly in your MedialaneClient config.`,
|
|
869
|
+
"NETWORK_NOT_SUPPORTED"
|
|
870
|
+
);
|
|
860
871
|
}
|
|
872
|
+
return {
|
|
873
|
+
network: parsed.network,
|
|
874
|
+
rpcUrl: parsed.rpcUrl ?? DEFAULT_RPC_URLS[parsed.network],
|
|
875
|
+
backendUrl: parsed.backendUrl,
|
|
876
|
+
apiKey: parsed.apiKey,
|
|
877
|
+
marketplaceContract,
|
|
878
|
+
collectionContract,
|
|
879
|
+
retryOptions: parsed.retryOptions
|
|
880
|
+
};
|
|
861
881
|
}
|
|
862
882
|
|
|
863
883
|
// src/marketplace/index.ts
|
|
@@ -909,18 +929,60 @@ function shortenAddress(address, chars = 4) {
|
|
|
909
929
|
return `${norm.slice(0, chars + 2)}...${norm.slice(-chars)}`;
|
|
910
930
|
}
|
|
911
931
|
|
|
932
|
+
// src/utils/retry.ts
|
|
933
|
+
var DEFAULT_MAX_ATTEMPTS = 3;
|
|
934
|
+
var DEFAULT_BASE_DELAY_MS = 300;
|
|
935
|
+
var DEFAULT_MAX_DELAY_MS = 5e3;
|
|
936
|
+
function sleep(ms) {
|
|
937
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
938
|
+
}
|
|
939
|
+
async function withRetry(fn, opts) {
|
|
940
|
+
const maxAttempts = opts?.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
|
|
941
|
+
const baseDelayMs = opts?.baseDelayMs ?? DEFAULT_BASE_DELAY_MS;
|
|
942
|
+
const maxDelayMs = opts?.maxDelayMs ?? DEFAULT_MAX_DELAY_MS;
|
|
943
|
+
let lastError;
|
|
944
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
945
|
+
try {
|
|
946
|
+
return await fn();
|
|
947
|
+
} catch (err) {
|
|
948
|
+
lastError = err;
|
|
949
|
+
if (err instanceof MedialaneApiError && err.status < 500) {
|
|
950
|
+
throw err;
|
|
951
|
+
}
|
|
952
|
+
const isRetryable = err instanceof MedialaneApiError && err.status >= 500 || err instanceof TypeError;
|
|
953
|
+
if (!isRetryable || attempt === maxAttempts - 1) {
|
|
954
|
+
throw err;
|
|
955
|
+
}
|
|
956
|
+
const jitter = Math.random() * baseDelayMs;
|
|
957
|
+
const delay = Math.min(baseDelayMs * Math.pow(2, attempt) + jitter, maxDelayMs);
|
|
958
|
+
await sleep(delay);
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
throw lastError;
|
|
962
|
+
}
|
|
963
|
+
|
|
912
964
|
// src/api/client.ts
|
|
965
|
+
function deriveErrorCode(status) {
|
|
966
|
+
if (status === 404) return "TOKEN_NOT_FOUND";
|
|
967
|
+
if (status === 429) return "RATE_LIMITED";
|
|
968
|
+
if (status === 410) return "INTENT_EXPIRED";
|
|
969
|
+
if (status === 401 || status === 403) return "UNAUTHORIZED";
|
|
970
|
+
if (status === 400) return "INVALID_PARAMS";
|
|
971
|
+
return "UNKNOWN";
|
|
972
|
+
}
|
|
913
973
|
var MedialaneApiError = class extends Error {
|
|
914
974
|
constructor(status, message) {
|
|
915
975
|
super(message);
|
|
916
976
|
this.status = status;
|
|
917
977
|
this.name = "MedialaneApiError";
|
|
978
|
+
this.code = deriveErrorCode(status);
|
|
918
979
|
}
|
|
919
980
|
};
|
|
920
981
|
var ApiClient = class {
|
|
921
|
-
constructor(baseUrl, apiKey) {
|
|
982
|
+
constructor(baseUrl, apiKey, retryOptions) {
|
|
922
983
|
this.baseUrl = baseUrl;
|
|
923
984
|
this.baseHeaders = apiKey ? { "x-api-key": apiKey } : {};
|
|
985
|
+
this.retryOptions = retryOptions;
|
|
924
986
|
}
|
|
925
987
|
async request(path, init) {
|
|
926
988
|
const url = `${this.baseUrl.replace(/\/$/, "")}${path}`;
|
|
@@ -928,19 +990,23 @@ var ApiClient = class {
|
|
|
928
990
|
if (!(init?.body instanceof FormData)) {
|
|
929
991
|
headers["Content-Type"] = "application/json";
|
|
930
992
|
}
|
|
931
|
-
const res = await
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
993
|
+
const res = await withRetry(async () => {
|
|
994
|
+
const response = await fetch(url, {
|
|
995
|
+
...init,
|
|
996
|
+
headers: { ...headers, ...init?.headers }
|
|
997
|
+
});
|
|
998
|
+
if (!response.ok) {
|
|
999
|
+
const text = await response.text().catch(() => response.statusText);
|
|
1000
|
+
let message = text;
|
|
1001
|
+
try {
|
|
1002
|
+
const body = JSON.parse(text);
|
|
1003
|
+
if (body.error) message = body.error;
|
|
1004
|
+
} catch {
|
|
1005
|
+
}
|
|
1006
|
+
throw new MedialaneApiError(response.status, message);
|
|
941
1007
|
}
|
|
942
|
-
|
|
943
|
-
}
|
|
1008
|
+
return response;
|
|
1009
|
+
}, this.retryOptions);
|
|
944
1010
|
return res.json();
|
|
945
1011
|
}
|
|
946
1012
|
get(path) {
|
|
@@ -998,9 +1064,10 @@ var ApiClient = class {
|
|
|
998
1064
|
);
|
|
999
1065
|
}
|
|
1000
1066
|
// ─── Collections ───────────────────────────────────────────────────────────
|
|
1001
|
-
getCollections(page = 1, limit = 20, isKnown) {
|
|
1067
|
+
getCollections(page = 1, limit = 20, isKnown, sort) {
|
|
1002
1068
|
const params = new URLSearchParams({ page: String(page), limit: String(limit) });
|
|
1003
1069
|
if (isKnown !== void 0) params.set("isKnown", String(isKnown));
|
|
1070
|
+
if (sort) params.set("sort", sort);
|
|
1004
1071
|
return this.get(`/v1/collections?${params}`);
|
|
1005
1072
|
}
|
|
1006
1073
|
getCollectionsByOwner(owner, page = 1, limit = 50) {
|
|
@@ -1125,7 +1192,7 @@ var MedialaneClient = class {
|
|
|
1125
1192
|
}
|
|
1126
1193
|
});
|
|
1127
1194
|
} else {
|
|
1128
|
-
this.api = new ApiClient(this.config.backendUrl, this.config.apiKey);
|
|
1195
|
+
this.api = new ApiClient(this.config.backendUrl, this.config.apiKey, this.config.retryOptions);
|
|
1129
1196
|
}
|
|
1130
1197
|
}
|
|
1131
1198
|
get network() {
|