@rareprotocol/rare-cli 0.2.2 → 0.4.1
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 +127 -1
- package/dist/client.d.ts +2224 -0
- package/dist/client.js +3439 -0
- package/dist/index.js +1554 -572
- package/package.json +18 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { Command as
|
|
4
|
+
import { Command as Command13 } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/commands/configure.ts
|
|
7
7
|
import { Command } from "commander";
|
|
@@ -55,6 +55,42 @@ var contractAddresses = {
|
|
|
55
55
|
auction: "0x1f0c946f0ee87acb268d50ede6c9b4d010af65d2"
|
|
56
56
|
}
|
|
57
57
|
};
|
|
58
|
+
var ETH_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
59
|
+
var currencyAddresses = {
|
|
60
|
+
eth: {
|
|
61
|
+
mainnet: ETH_ADDRESS,
|
|
62
|
+
sepolia: ETH_ADDRESS,
|
|
63
|
+
base: ETH_ADDRESS,
|
|
64
|
+
"base-sepolia": ETH_ADDRESS
|
|
65
|
+
},
|
|
66
|
+
rare: {
|
|
67
|
+
mainnet: "0xba5BDe662c17e2aDFF1075610382B9B691296350",
|
|
68
|
+
sepolia: "0x197FaeF3f59eC80113e773Bb6206a17d183F97CB",
|
|
69
|
+
base: "0x691077c8e8de54ea84efd454630439f99bd8c92f",
|
|
70
|
+
"base-sepolia": "0x8b21bC8571d11F7AdB705ad8F6f6BD1deb79cE01"
|
|
71
|
+
},
|
|
72
|
+
usdc: {
|
|
73
|
+
mainnet: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
74
|
+
sepolia: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
|
|
75
|
+
base: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
76
|
+
"base-sepolia": "0x036CbD53842c5426634e7929541eC2318f3dCF7e"
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
var currencyNames = Object.keys(currencyAddresses);
|
|
80
|
+
function resolveCurrency(input, chain) {
|
|
81
|
+
const lower = input.toLowerCase();
|
|
82
|
+
if (lower in currencyAddresses) {
|
|
83
|
+
const addr = currencyAddresses[lower][chain];
|
|
84
|
+
if (!addr) {
|
|
85
|
+
throw new Error(`Currency "${lower}" is not available on "${chain}".`);
|
|
86
|
+
}
|
|
87
|
+
return addr;
|
|
88
|
+
}
|
|
89
|
+
if (input.startsWith("0x")) {
|
|
90
|
+
return input;
|
|
91
|
+
}
|
|
92
|
+
throw new Error(`Unknown currency "${input}". Supported: ${currencyNames.join(", ")} or a 0x address.`);
|
|
93
|
+
}
|
|
58
94
|
function getContractAddresses(chain) {
|
|
59
95
|
const addresses = contractAddresses[chain];
|
|
60
96
|
if (!addresses) {
|
|
@@ -218,6 +254,14 @@ function getWalletClient(chain) {
|
|
|
218
254
|
};
|
|
219
255
|
}
|
|
220
256
|
|
|
257
|
+
// src/sdk/client.ts
|
|
258
|
+
import {
|
|
259
|
+
erc20Abi,
|
|
260
|
+
parseEther,
|
|
261
|
+
parseEventLogs,
|
|
262
|
+
maxUint256
|
|
263
|
+
} from "viem";
|
|
264
|
+
|
|
221
265
|
// src/contracts/abis/factory.ts
|
|
222
266
|
var factoryAbi = [
|
|
223
267
|
{
|
|
@@ -383,71 +427,6 @@ var factoryAbi = [
|
|
|
383
427
|
}
|
|
384
428
|
];
|
|
385
429
|
|
|
386
|
-
// src/commands/deploy.ts
|
|
387
|
-
function deployErc721Command() {
|
|
388
|
-
const cmd = new Command2("erc721");
|
|
389
|
-
cmd.description("Deploy a new ERC-721 NFT contract via the RARE factory");
|
|
390
|
-
cmd.argument("<name>", "name of the NFT collection").argument("<symbol>", "symbol of the NFT collection").option("--max-tokens <number>", "maximum number of tokens (optional)").option("--chain <chain>", "chain to use (mainnet, sepolia, base, base-sepolia)").action(async (name, symbol, opts) => {
|
|
391
|
-
const chain = getActiveChain(opts.chain);
|
|
392
|
-
const { client, account } = getWalletClient(chain);
|
|
393
|
-
const publicClient = getPublicClient(chain);
|
|
394
|
-
const factoryAddress = getContractAddresses(chain).factory;
|
|
395
|
-
console.log(`Deploying ERC-721 contract on ${chain}...`);
|
|
396
|
-
console.log(` Factory: ${factoryAddress}`);
|
|
397
|
-
console.log(` Name: ${name}`);
|
|
398
|
-
console.log(` Symbol: ${symbol}`);
|
|
399
|
-
if (opts.maxTokens) console.log(` Max tokens: ${opts.maxTokens}`);
|
|
400
|
-
let txHash;
|
|
401
|
-
if (opts.maxTokens) {
|
|
402
|
-
txHash = await client.writeContract({
|
|
403
|
-
address: factoryAddress,
|
|
404
|
-
abi: factoryAbi,
|
|
405
|
-
functionName: "createSovereignBatchMint",
|
|
406
|
-
args: [name, symbol, BigInt(opts.maxTokens)],
|
|
407
|
-
account,
|
|
408
|
-
chain: void 0
|
|
409
|
-
});
|
|
410
|
-
} else {
|
|
411
|
-
txHash = await client.writeContract({
|
|
412
|
-
address: factoryAddress,
|
|
413
|
-
abi: factoryAbi,
|
|
414
|
-
functionName: "createSovereignBatchMint",
|
|
415
|
-
args: [name, symbol],
|
|
416
|
-
account,
|
|
417
|
-
chain: void 0
|
|
418
|
-
});
|
|
419
|
-
}
|
|
420
|
-
console.log(`Transaction sent: ${txHash}`);
|
|
421
|
-
console.log("Waiting for confirmation...");
|
|
422
|
-
const receipt = await publicClient.waitForTransactionReceipt({ hash: txHash });
|
|
423
|
-
const { parseEventLogs } = await import("viem");
|
|
424
|
-
const logs = parseEventLogs({
|
|
425
|
-
abi: factoryAbi,
|
|
426
|
-
logs: receipt.logs,
|
|
427
|
-
eventName: "SovereignBatchMintCreated"
|
|
428
|
-
});
|
|
429
|
-
if (logs.length > 0) {
|
|
430
|
-
const deployedAddress = logs[0].args.contractAddress;
|
|
431
|
-
console.log(`
|
|
432
|
-
ERC-721 contract deployed at: ${deployedAddress}`);
|
|
433
|
-
} else {
|
|
434
|
-
console.log(`
|
|
435
|
-
Transaction confirmed. Block: ${receipt.blockNumber}`);
|
|
436
|
-
console.log("Could not parse deployed address from logs.");
|
|
437
|
-
}
|
|
438
|
-
});
|
|
439
|
-
return cmd;
|
|
440
|
-
}
|
|
441
|
-
function deployCommand() {
|
|
442
|
-
const cmd = new Command2("deploy");
|
|
443
|
-
cmd.description("Deploy a new contract via the RARE protocol");
|
|
444
|
-
cmd.addCommand(deployErc721Command());
|
|
445
|
-
return cmd;
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
// src/commands/mint.ts
|
|
449
|
-
import { Command as Command3 } from "commander";
|
|
450
|
-
|
|
451
430
|
// src/contracts/abis/token.ts
|
|
452
431
|
var tokenAbi = [
|
|
453
432
|
{
|
|
@@ -1206,290 +1185,30 @@ var tokenAbi = [
|
|
|
1206
1185
|
}
|
|
1207
1186
|
];
|
|
1208
1187
|
|
|
1209
|
-
// src/
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
".svg": "image/svg+xml",
|
|
1221
|
-
".mp4": "video/mp4",
|
|
1222
|
-
".mov": "video/quicktime",
|
|
1223
|
-
".webm": "video/webm",
|
|
1224
|
-
".glb": "model/gltf-binary",
|
|
1225
|
-
".gltf": "model/gltf+json",
|
|
1226
|
-
".html": "text/html"
|
|
1227
|
-
};
|
|
1228
|
-
function inferMimeType(filename) {
|
|
1229
|
-
const ext = extname(filename).toLowerCase();
|
|
1230
|
-
return MIME_TYPES[ext] ?? "application/octet-stream";
|
|
1231
|
-
}
|
|
1232
|
-
function assertPositiveInteger(value, fieldName) {
|
|
1233
|
-
if (!Number.isInteger(value) || value <= 0) {
|
|
1234
|
-
throw new Error(`${fieldName} must be a positive integer`);
|
|
1235
|
-
}
|
|
1236
|
-
}
|
|
1237
|
-
function assertEvmAddress(value, fieldName) {
|
|
1238
|
-
if (!isAddress(value)) {
|
|
1239
|
-
throw new Error(`${fieldName} must be a valid EVM address`);
|
|
1240
|
-
}
|
|
1241
|
-
}
|
|
1242
|
-
function parseDimensions(dimensions) {
|
|
1243
|
-
if (!dimensions) return void 0;
|
|
1244
|
-
const [w, h] = dimensions.split("x");
|
|
1245
|
-
if (!w || !h) return void 0;
|
|
1246
|
-
const width = parseInt(w, 10);
|
|
1247
|
-
const height = parseInt(h, 10);
|
|
1248
|
-
if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) return void 0;
|
|
1249
|
-
return { width, height };
|
|
1250
|
-
}
|
|
1251
|
-
async function apiPost(path2, payload) {
|
|
1252
|
-
const url = `${API_BASE_URL}${path2}`;
|
|
1253
|
-
const response = await fetch(url, {
|
|
1254
|
-
method: "POST",
|
|
1255
|
-
headers: { "Content-Type": "application/json" },
|
|
1256
|
-
body: JSON.stringify(payload)
|
|
1257
|
-
});
|
|
1258
|
-
const text = await response.text();
|
|
1259
|
-
const json = text ? JSON.parse(text) : {};
|
|
1260
|
-
if (!response.ok) {
|
|
1261
|
-
const message = json.error ?? text;
|
|
1262
|
-
throw new Error(`API error ${response.status} on ${path2}: ${message}`);
|
|
1263
|
-
}
|
|
1264
|
-
return json;
|
|
1265
|
-
}
|
|
1266
|
-
async function uploadParts(fileBuffer, partSize, presignedUrls) {
|
|
1267
|
-
const parts = [];
|
|
1268
|
-
for (let i = 0; i < presignedUrls.length; i++) {
|
|
1269
|
-
const start = i * partSize;
|
|
1270
|
-
const end = start + partSize;
|
|
1271
|
-
const partBuffer = fileBuffer.subarray(start, end);
|
|
1272
|
-
const response = await fetch(presignedUrls[i], {
|
|
1273
|
-
method: "PUT",
|
|
1274
|
-
body: new Uint8Array(partBuffer)
|
|
1275
|
-
});
|
|
1276
|
-
if (response.status !== 200 && response.status !== 204) {
|
|
1277
|
-
throw new Error(`Part ${i + 1} upload failed with status ${response.status}`);
|
|
1278
|
-
}
|
|
1279
|
-
const etag = response.headers.get("etag");
|
|
1280
|
-
if (!etag) {
|
|
1281
|
-
throw new Error(`Missing etag header for part ${i + 1}`);
|
|
1282
|
-
}
|
|
1283
|
-
parts.push({ ETag: etag, PartNumber: i + 1 });
|
|
1284
|
-
}
|
|
1285
|
-
return parts;
|
|
1286
|
-
}
|
|
1287
|
-
async function uploadMedia(filePath, label) {
|
|
1288
|
-
const fileStats = await stat(filePath);
|
|
1289
|
-
const fileSize = fileStats.size;
|
|
1290
|
-
const fileName = basename(filePath);
|
|
1291
|
-
const fileBuffer = await readFile(filePath);
|
|
1292
|
-
const mimeType = inferMimeType(fileName);
|
|
1293
|
-
console.log(`Uploading ${label}: ${fileName} (${fileSize} bytes, ${mimeType})`);
|
|
1294
|
-
const init = await apiPost("/api/nft/media-upload-url", {
|
|
1295
|
-
fileSize,
|
|
1296
|
-
filename: fileName
|
|
1297
|
-
});
|
|
1298
|
-
console.log(` Multipart upload initialized (${init.presignedUrls.length} parts)`);
|
|
1299
|
-
const parts = await uploadParts(fileBuffer, init.partSize, init.presignedUrls);
|
|
1300
|
-
console.log(` All parts uploaded`);
|
|
1301
|
-
const complete = await apiPost("/api/nft/media-upload-complete", {
|
|
1302
|
-
key: init.key,
|
|
1303
|
-
uploadId: init.uploadId,
|
|
1304
|
-
bucket: init.bucket,
|
|
1305
|
-
parts
|
|
1306
|
-
});
|
|
1307
|
-
console.log(` Upload complete: ${complete.ipfsUrl}`);
|
|
1308
|
-
const generated = await apiPost("/api/nft/media-generate", {
|
|
1309
|
-
uri: complete.ipfsUrl,
|
|
1310
|
-
mimeType
|
|
1311
|
-
});
|
|
1312
|
-
const dimensions = parseDimensions(generated.media.dimensions);
|
|
1313
|
-
const entry = {
|
|
1314
|
-
url: generated.media.uri,
|
|
1315
|
-
mimeType: generated.media.mimeType,
|
|
1316
|
-
size: generated.media.size ?? fileSize,
|
|
1317
|
-
...dimensions ? { dimensions } : {}
|
|
1318
|
-
};
|
|
1319
|
-
console.log(` Media generated: ${entry.url}`);
|
|
1320
|
-
return entry;
|
|
1321
|
-
}
|
|
1322
|
-
async function importErc721(opts) {
|
|
1323
|
-
assertPositiveInteger(opts.chainId, "chainId");
|
|
1324
|
-
assertEvmAddress(opts.contractAddress, "contractAddress");
|
|
1325
|
-
assertEvmAddress(opts.ownerAddress, "ownerAddress");
|
|
1326
|
-
const normalizedContractAddress = opts.contractAddress.toLowerCase();
|
|
1327
|
-
const normalizedOwnerAddress = opts.ownerAddress.toLowerCase();
|
|
1328
|
-
const result = await apiPost("/api/nft/import-erc721", {
|
|
1329
|
-
chainId: opts.chainId,
|
|
1330
|
-
contractAddress: normalizedContractAddress,
|
|
1331
|
-
ownerAddress: normalizedOwnerAddress
|
|
1332
|
-
});
|
|
1333
|
-
if (result.ok !== true) {
|
|
1334
|
-
throw new Error("Unexpected response from /api/nft/import-erc721");
|
|
1335
|
-
}
|
|
1336
|
-
}
|
|
1337
|
-
async function pinMetadata(opts) {
|
|
1338
|
-
const nftMedia = {
|
|
1339
|
-
image: opts.image
|
|
1340
|
-
};
|
|
1341
|
-
if (opts.video) {
|
|
1342
|
-
nftMedia.video = opts.video;
|
|
1343
|
-
}
|
|
1344
|
-
const payload = {
|
|
1345
|
-
name: opts.name,
|
|
1346
|
-
description: opts.description,
|
|
1347
|
-
nftMedia,
|
|
1348
|
-
tags: opts.tags ?? []
|
|
1349
|
-
};
|
|
1350
|
-
if (opts.attributes && opts.attributes.length > 0) {
|
|
1351
|
-
payload.attributes = opts.attributes;
|
|
1352
|
-
}
|
|
1353
|
-
console.log("Pinning metadata to IPFS...");
|
|
1354
|
-
const result = await apiPost("/api/nft/metadata", payload);
|
|
1355
|
-
console.log(`Metadata pinned: ${result.ipfsUrl}`);
|
|
1356
|
-
console.log(`Gateway URL: ${result.gatewayUrl}`);
|
|
1357
|
-
return result.ipfsUrl;
|
|
1358
|
-
}
|
|
1359
|
-
|
|
1360
|
-
// src/commands/mint.ts
|
|
1361
|
-
function parseAttribute(raw) {
|
|
1362
|
-
if (raw.startsWith("{")) {
|
|
1363
|
-
const parsed = JSON.parse(raw);
|
|
1364
|
-
if (parsed.value === void 0) {
|
|
1365
|
-
throw new Error(`Attribute JSON must include "value": ${raw}`);
|
|
1366
|
-
}
|
|
1367
|
-
return parsed;
|
|
1368
|
-
}
|
|
1369
|
-
const eqIndex = raw.indexOf("=");
|
|
1370
|
-
if (eqIndex === -1) {
|
|
1371
|
-
return { value: raw };
|
|
1372
|
-
}
|
|
1373
|
-
const trait_type = raw.slice(0, eqIndex);
|
|
1374
|
-
const rawValue = raw.slice(eqIndex + 1);
|
|
1375
|
-
const numValue = Number(rawValue);
|
|
1376
|
-
const value = rawValue.length > 0 && !Number.isNaN(numValue) ? numValue : rawValue;
|
|
1377
|
-
return { trait_type, value };
|
|
1378
|
-
}
|
|
1379
|
-
function mintCommand() {
|
|
1380
|
-
const cmd = new Command3("mint");
|
|
1381
|
-
cmd.description("Mint a new NFT on a deployed token contract");
|
|
1382
|
-
cmd.requiredOption("--contract <address>", "token contract address").option("--token-uri <uri>", "token metadata URI (skip upload if provided)").option("--name <name>", "NFT name").option("--description <description>", "NFT description").option("--image <path>", "path to image file").option("--video <path>", "path to video file").option("--tag <tag>", "tag (repeatable)", (val, acc) => [...acc, val], []).option("--attribute <attr>", 'attribute as "trait=value" or JSON (repeatable)', (val, acc) => [...acc, val], []).option("--to <address>", "recipient address (defaults to caller)").option("--royalty-receiver <address>", "royalty receiver address (defaults to caller)").option("--chain <chain>", "chain to use (mainnet, sepolia, base, base-sepolia)").action(async (opts) => {
|
|
1383
|
-
let tokenUri;
|
|
1384
|
-
if (opts.tokenUri) {
|
|
1385
|
-
tokenUri = opts.tokenUri;
|
|
1386
|
-
} else {
|
|
1387
|
-
if (!opts.name) {
|
|
1388
|
-
console.error("Error: --name is required when not using --token-uri");
|
|
1389
|
-
process.exit(1);
|
|
1390
|
-
}
|
|
1391
|
-
if (!opts.description) {
|
|
1392
|
-
console.error("Error: --description is required when not using --token-uri");
|
|
1393
|
-
process.exit(1);
|
|
1188
|
+
// src/contracts/abis/auction.ts
|
|
1189
|
+
var auctionAbi = [
|
|
1190
|
+
{
|
|
1191
|
+
"type": "function",
|
|
1192
|
+
"name": "COLDIE_AUCTION",
|
|
1193
|
+
"inputs": [],
|
|
1194
|
+
"outputs": [
|
|
1195
|
+
{
|
|
1196
|
+
"name": "",
|
|
1197
|
+
"type": "bytes32",
|
|
1198
|
+
"internalType": "bytes32"
|
|
1394
1199
|
}
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
name:
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
video: videoMedia,
|
|
1408
|
-
tags,
|
|
1409
|
-
attributes
|
|
1410
|
-
});
|
|
1411
|
-
}
|
|
1412
|
-
const chain = getActiveChain(opts.chain);
|
|
1413
|
-
const { client, account } = getWalletClient(chain);
|
|
1414
|
-
const publicClient = getPublicClient(chain);
|
|
1415
|
-
const contractAddress = opts.contract;
|
|
1416
|
-
const useMintTo = opts.to || opts.royaltyReceiver;
|
|
1417
|
-
console.log(`
|
|
1418
|
-
Minting NFT on ${chain}...`);
|
|
1419
|
-
console.log(` Contract: ${contractAddress}`);
|
|
1420
|
-
console.log(` URI: ${tokenUri}`);
|
|
1421
|
-
let txHash;
|
|
1422
|
-
if (useMintTo) {
|
|
1423
|
-
const receiver = opts.to ?? account.address;
|
|
1424
|
-
const royaltyReceiver = opts.royaltyReceiver ?? account.address;
|
|
1425
|
-
console.log(` To: ${receiver}`);
|
|
1426
|
-
console.log(` Royalty receiver: ${royaltyReceiver}`);
|
|
1427
|
-
txHash = await client.writeContract({
|
|
1428
|
-
address: contractAddress,
|
|
1429
|
-
abi: tokenAbi,
|
|
1430
|
-
functionName: "mintTo",
|
|
1431
|
-
args: [tokenUri, receiver, royaltyReceiver],
|
|
1432
|
-
account,
|
|
1433
|
-
chain: void 0
|
|
1434
|
-
});
|
|
1435
|
-
} else {
|
|
1436
|
-
txHash = await client.writeContract({
|
|
1437
|
-
address: contractAddress,
|
|
1438
|
-
abi: tokenAbi,
|
|
1439
|
-
functionName: "addNewToken",
|
|
1440
|
-
args: [tokenUri],
|
|
1441
|
-
account,
|
|
1442
|
-
chain: void 0
|
|
1443
|
-
});
|
|
1444
|
-
}
|
|
1445
|
-
console.log(`Transaction sent: ${txHash}`);
|
|
1446
|
-
console.log("Waiting for confirmation...");
|
|
1447
|
-
const receipt = await publicClient.waitForTransactionReceipt({ hash: txHash });
|
|
1448
|
-
const { parseEventLogs } = await import("viem");
|
|
1449
|
-
const logs = parseEventLogs({
|
|
1450
|
-
abi: tokenAbi,
|
|
1451
|
-
logs: receipt.logs,
|
|
1452
|
-
eventName: "Transfer"
|
|
1453
|
-
});
|
|
1454
|
-
if (logs.length > 0) {
|
|
1455
|
-
console.log(`
|
|
1456
|
-
NFT minted! Token ID: ${logs[0].args.tokenId}`);
|
|
1457
|
-
} else {
|
|
1458
|
-
console.log(`
|
|
1459
|
-
Transaction confirmed. Block: ${receipt.blockNumber}`);
|
|
1460
|
-
}
|
|
1461
|
-
});
|
|
1462
|
-
return cmd;
|
|
1463
|
-
}
|
|
1464
|
-
|
|
1465
|
-
// src/commands/auction.ts
|
|
1466
|
-
import { Command as Command4 } from "commander";
|
|
1467
|
-
import { parseEther, formatEther } from "viem";
|
|
1468
|
-
|
|
1469
|
-
// src/contracts/abis/auction.ts
|
|
1470
|
-
var auctionAbi = [
|
|
1471
|
-
{
|
|
1472
|
-
"type": "function",
|
|
1473
|
-
"name": "COLDIE_AUCTION",
|
|
1474
|
-
"inputs": [],
|
|
1475
|
-
"outputs": [
|
|
1476
|
-
{
|
|
1477
|
-
"name": "",
|
|
1478
|
-
"type": "bytes32",
|
|
1479
|
-
"internalType": "bytes32"
|
|
1480
|
-
}
|
|
1481
|
-
],
|
|
1482
|
-
"stateMutability": "view"
|
|
1483
|
-
},
|
|
1484
|
-
{
|
|
1485
|
-
"type": "function",
|
|
1486
|
-
"name": "NO_AUCTION",
|
|
1487
|
-
"inputs": [],
|
|
1488
|
-
"outputs": [
|
|
1489
|
-
{
|
|
1490
|
-
"name": "",
|
|
1491
|
-
"type": "bytes32",
|
|
1492
|
-
"internalType": "bytes32"
|
|
1200
|
+
],
|
|
1201
|
+
"stateMutability": "view"
|
|
1202
|
+
},
|
|
1203
|
+
{
|
|
1204
|
+
"type": "function",
|
|
1205
|
+
"name": "NO_AUCTION",
|
|
1206
|
+
"inputs": [],
|
|
1207
|
+
"outputs": [
|
|
1208
|
+
{
|
|
1209
|
+
"name": "",
|
|
1210
|
+
"type": "bytes32",
|
|
1211
|
+
"internalType": "bytes32"
|
|
1493
1212
|
}
|
|
1494
1213
|
],
|
|
1495
1214
|
"stateMutability": "view"
|
|
@@ -3037,35 +2756,178 @@ var auctionAbi = [
|
|
|
3037
2756
|
}
|
|
3038
2757
|
];
|
|
3039
2758
|
|
|
3040
|
-
// src/
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
2759
|
+
// src/sdk/api.ts
|
|
2760
|
+
import { isAddress } from "viem";
|
|
2761
|
+
var API_BASE_URL = "https://api.superrare.org";
|
|
2762
|
+
var MIME_TYPES = {
|
|
2763
|
+
".png": "image/png",
|
|
2764
|
+
".jpg": "image/jpeg",
|
|
2765
|
+
".jpeg": "image/jpeg",
|
|
2766
|
+
".gif": "image/gif",
|
|
2767
|
+
".webp": "image/webp",
|
|
2768
|
+
".svg": "image/svg+xml",
|
|
2769
|
+
".mp4": "video/mp4",
|
|
2770
|
+
".mov": "video/quicktime",
|
|
2771
|
+
".webm": "video/webm",
|
|
2772
|
+
".glb": "model/gltf-binary",
|
|
2773
|
+
".gltf": "model/gltf+json",
|
|
2774
|
+
".html": "text/html"
|
|
2775
|
+
};
|
|
2776
|
+
function inferMimeType(filename) {
|
|
2777
|
+
const extIndex = filename.lastIndexOf(".");
|
|
2778
|
+
const ext = extIndex === -1 ? "" : filename.slice(extIndex).toLowerCase();
|
|
2779
|
+
return MIME_TYPES[ext] ?? "application/octet-stream";
|
|
2780
|
+
}
|
|
2781
|
+
function normalizeFilename(filename) {
|
|
2782
|
+
const normalized = filename.replaceAll("\\", "/");
|
|
2783
|
+
const lastSeparator = normalized.lastIndexOf("/");
|
|
2784
|
+
return lastSeparator === -1 ? normalized : normalized.slice(lastSeparator + 1);
|
|
2785
|
+
}
|
|
2786
|
+
function assertPositiveInteger(value, fieldName) {
|
|
2787
|
+
if (!Number.isInteger(value) || value <= 0) {
|
|
2788
|
+
throw new Error(`${fieldName} must be a positive integer`);
|
|
3045
2789
|
}
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
2790
|
+
}
|
|
2791
|
+
function assertEvmAddress(value, fieldName) {
|
|
2792
|
+
if (!isAddress(value)) {
|
|
2793
|
+
throw new Error(`${fieldName} must be a valid EVM address`);
|
|
2794
|
+
}
|
|
2795
|
+
}
|
|
2796
|
+
function parseDimensions(dimensions) {
|
|
2797
|
+
if (!dimensions) return void 0;
|
|
2798
|
+
const [w, h] = dimensions.split("x");
|
|
2799
|
+
if (!w || !h) return void 0;
|
|
2800
|
+
const width = Number.parseInt(w, 10);
|
|
2801
|
+
const height = Number.parseInt(h, 10);
|
|
2802
|
+
if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {
|
|
2803
|
+
return void 0;
|
|
2804
|
+
}
|
|
2805
|
+
return { width, height };
|
|
2806
|
+
}
|
|
2807
|
+
async function apiPost(path2, payload) {
|
|
2808
|
+
const url = `${API_BASE_URL}${path2}`;
|
|
2809
|
+
const response = await fetch(url, {
|
|
2810
|
+
method: "POST",
|
|
2811
|
+
headers: { "Content-Type": "application/json" },
|
|
2812
|
+
body: JSON.stringify(payload)
|
|
2813
|
+
});
|
|
2814
|
+
const text = await response.text();
|
|
2815
|
+
const json = text ? JSON.parse(text) : {};
|
|
2816
|
+
if (!response.ok) {
|
|
2817
|
+
const message = json.error ?? text;
|
|
2818
|
+
throw new Error(`API error ${response.status} on ${path2}: ${String(message)}`);
|
|
2819
|
+
}
|
|
2820
|
+
return json;
|
|
2821
|
+
}
|
|
2822
|
+
async function uploadParts(fileBuffer, partSize, presignedUrls) {
|
|
2823
|
+
const parts = [];
|
|
2824
|
+
for (let i = 0; i < presignedUrls.length; i++) {
|
|
2825
|
+
const start = i * partSize;
|
|
2826
|
+
const end = start + partSize;
|
|
2827
|
+
const partBuffer = fileBuffer.subarray(start, end);
|
|
2828
|
+
const response = await fetch(presignedUrls[i], {
|
|
2829
|
+
method: "PUT",
|
|
2830
|
+
body: new Uint8Array(partBuffer)
|
|
2831
|
+
});
|
|
2832
|
+
if (response.status !== 200 && response.status !== 204) {
|
|
2833
|
+
throw new Error(`Part ${i + 1} upload failed with status ${response.status}`);
|
|
3055
2834
|
}
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
}
|
|
2835
|
+
const etag = response.headers.get("etag");
|
|
2836
|
+
if (!etag) {
|
|
2837
|
+
throw new Error(`Missing etag header for part ${i + 1}`);
|
|
3060
2838
|
}
|
|
3061
|
-
|
|
3062
|
-
depth++;
|
|
2839
|
+
parts.push({ ETag: etag, PartNumber: i + 1 });
|
|
3063
2840
|
}
|
|
3064
|
-
|
|
2841
|
+
return parts;
|
|
2842
|
+
}
|
|
2843
|
+
async function uploadMedia(buffer, filename) {
|
|
2844
|
+
const fileSize = buffer.byteLength;
|
|
2845
|
+
const safeFilename = normalizeFilename(filename);
|
|
2846
|
+
const mimeType = inferMimeType(safeFilename);
|
|
2847
|
+
const init = await apiPost("/api/nft/media-upload-url", {
|
|
2848
|
+
fileSize,
|
|
2849
|
+
filename: safeFilename
|
|
2850
|
+
});
|
|
2851
|
+
const parts = await uploadParts(buffer, init.partSize, init.presignedUrls);
|
|
2852
|
+
const complete = await apiPost("/api/nft/media-upload-complete", {
|
|
2853
|
+
key: init.key,
|
|
2854
|
+
uploadId: init.uploadId,
|
|
2855
|
+
bucket: init.bucket,
|
|
2856
|
+
parts
|
|
2857
|
+
});
|
|
2858
|
+
const generated = await apiPost("/api/nft/media-generate", {
|
|
2859
|
+
uri: complete.ipfsUrl,
|
|
2860
|
+
mimeType
|
|
2861
|
+
});
|
|
2862
|
+
const dimensions = parseDimensions(generated.media.dimensions);
|
|
2863
|
+
return {
|
|
2864
|
+
url: generated.media.uri,
|
|
2865
|
+
mimeType: generated.media.mimeType,
|
|
2866
|
+
size: generated.media.size ?? fileSize,
|
|
2867
|
+
...dimensions ? { dimensions } : {}
|
|
2868
|
+
};
|
|
2869
|
+
}
|
|
2870
|
+
async function pinMetadata(opts) {
|
|
2871
|
+
const nftMedia = {
|
|
2872
|
+
image: opts.image
|
|
2873
|
+
};
|
|
2874
|
+
if (opts.video) {
|
|
2875
|
+
nftMedia.video = opts.video;
|
|
2876
|
+
}
|
|
2877
|
+
const payload = {
|
|
2878
|
+
name: opts.name,
|
|
2879
|
+
description: opts.description,
|
|
2880
|
+
nftMedia,
|
|
2881
|
+
tags: opts.tags ?? []
|
|
2882
|
+
};
|
|
2883
|
+
if (opts.attributes && opts.attributes.length > 0) {
|
|
2884
|
+
payload.attributes = opts.attributes;
|
|
2885
|
+
}
|
|
2886
|
+
const result = await apiPost("/api/nft/metadata", payload);
|
|
2887
|
+
return result.ipfsUrl;
|
|
2888
|
+
}
|
|
2889
|
+
async function importErc721(opts) {
|
|
2890
|
+
assertPositiveInteger(opts.chainId, "chainId");
|
|
2891
|
+
assertEvmAddress(opts.contract, "contract");
|
|
2892
|
+
assertEvmAddress(opts.owner, "owner");
|
|
2893
|
+
const result = await apiPost("/api/nft/import-erc721", {
|
|
2894
|
+
chainId: opts.chainId,
|
|
2895
|
+
contractAddress: opts.contract.toLowerCase(),
|
|
2896
|
+
ownerAddress: opts.owner.toLowerCase()
|
|
2897
|
+
});
|
|
2898
|
+
if (result.ok !== true) {
|
|
2899
|
+
throw new Error("Unexpected response from /api/nft/import-erc721");
|
|
2900
|
+
}
|
|
2901
|
+
}
|
|
2902
|
+
async function searchPost(path2, payload) {
|
|
2903
|
+
return apiPost(path2, payload);
|
|
2904
|
+
}
|
|
2905
|
+
async function searchNfts(params = {}) {
|
|
2906
|
+
return searchPost("/api/search/nfts", {
|
|
2907
|
+
query: params.query ?? "",
|
|
2908
|
+
take: params.take ?? 24,
|
|
2909
|
+
cursor: params.cursor ?? 0,
|
|
2910
|
+
sortBy: params.sortBy ?? "RECENT_ACTIVITY_DESC",
|
|
2911
|
+
ownerAddresses: params.ownerAddresses ?? [],
|
|
2912
|
+
creatorAddresses: params.creatorAddresses ?? [],
|
|
2913
|
+
collectionIds: params.collectionIds ?? [],
|
|
2914
|
+
contractAddresses: params.contractAddresses ?? [],
|
|
2915
|
+
...params.auctionStates ? { auctionStates: params.auctionStates } : {},
|
|
2916
|
+
...params.chainIds ? { chainIds: params.chainIds } : {}
|
|
2917
|
+
});
|
|
2918
|
+
}
|
|
2919
|
+
async function searchCollections(params = {}) {
|
|
2920
|
+
return searchPost("/api/search/collections", {
|
|
2921
|
+
query: params.query ?? "",
|
|
2922
|
+
take: params.take ?? 24,
|
|
2923
|
+
cursor: params.cursor ?? 0,
|
|
2924
|
+
sortBy: params.sortBy ?? "NEWEST",
|
|
2925
|
+
ownerAddresses: params.ownerAddresses ?? []
|
|
2926
|
+
});
|
|
3065
2927
|
}
|
|
3066
2928
|
|
|
3067
|
-
// src/
|
|
3068
|
-
var
|
|
2929
|
+
// src/sdk/client.ts
|
|
2930
|
+
var ETH_ADDRESS2 = "0x0000000000000000000000000000000000000000";
|
|
3069
2931
|
var approvalAbi = [
|
|
3070
2932
|
{
|
|
3071
2933
|
inputs: [{ name: "owner", type: "address" }, { name: "operator", type: "address" }],
|
|
@@ -3082,183 +2944,1099 @@ var approvalAbi = [
|
|
|
3082
2944
|
type: "function"
|
|
3083
2945
|
}
|
|
3084
2946
|
];
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
2947
|
+
var marketplaceSettingsAbi = [
|
|
2948
|
+
{
|
|
2949
|
+
inputs: [{ name: "_amount", type: "uint256" }],
|
|
2950
|
+
name: "calculateMarketplaceFee",
|
|
2951
|
+
outputs: [{ name: "", type: "uint256" }],
|
|
2952
|
+
stateMutability: "view",
|
|
2953
|
+
type: "function"
|
|
2954
|
+
}
|
|
2955
|
+
];
|
|
2956
|
+
function resolveChainFromPublicClient(publicClient) {
|
|
2957
|
+
const chainId = publicClient.chain?.id;
|
|
2958
|
+
if (!chainId) {
|
|
2959
|
+
throw new Error("Unable to resolve chain from publicClient.chain.id. Create your public client with an explicit chain.");
|
|
2960
|
+
}
|
|
2961
|
+
for (const [chain, id] of Object.entries(chainIds)) {
|
|
2962
|
+
if (id === chainId) {
|
|
2963
|
+
return chain;
|
|
2964
|
+
}
|
|
2965
|
+
}
|
|
2966
|
+
throw new Error(`Unsupported chain id: ${chainId}. Supported chain ids: ${Object.values(chainIds).join(", ")}`);
|
|
2967
|
+
}
|
|
2968
|
+
function requireWallet(config) {
|
|
2969
|
+
if (!config.walletClient) {
|
|
2970
|
+
throw new Error("walletClient is required for write operations.");
|
|
2971
|
+
}
|
|
2972
|
+
const walletAccount = config.walletClient.account;
|
|
2973
|
+
if (config.account) {
|
|
2974
|
+
if (walletAccount && walletAccount.address.toLowerCase() === config.account.toLowerCase()) {
|
|
2975
|
+
return {
|
|
2976
|
+
walletClient: config.walletClient,
|
|
2977
|
+
account: walletAccount,
|
|
2978
|
+
accountAddress: walletAccount.address
|
|
2979
|
+
};
|
|
2980
|
+
}
|
|
2981
|
+
return {
|
|
2982
|
+
walletClient: config.walletClient,
|
|
2983
|
+
account: config.account,
|
|
2984
|
+
accountAddress: config.account
|
|
2985
|
+
};
|
|
2986
|
+
}
|
|
2987
|
+
if (!walletAccount) {
|
|
2988
|
+
throw new Error("No account available for write operations. Pass config.account or provide walletClient with an account.");
|
|
2989
|
+
}
|
|
2990
|
+
return {
|
|
2991
|
+
walletClient: config.walletClient,
|
|
2992
|
+
account: walletAccount,
|
|
2993
|
+
accountAddress: walletAccount.address
|
|
2994
|
+
};
|
|
2995
|
+
}
|
|
2996
|
+
function toInteger(value, field) {
|
|
2997
|
+
if (typeof value === "bigint") return value;
|
|
2998
|
+
if (typeof value === "number") {
|
|
2999
|
+
if (!Number.isFinite(value) || !Number.isInteger(value)) {
|
|
3000
|
+
throw new Error(`${field} must be an integer.`);
|
|
3001
|
+
}
|
|
3002
|
+
return BigInt(value);
|
|
3003
|
+
}
|
|
3004
|
+
try {
|
|
3005
|
+
return BigInt(value);
|
|
3006
|
+
} catch {
|
|
3007
|
+
throw new Error(`${field} must be an integer.`);
|
|
3008
|
+
}
|
|
3009
|
+
}
|
|
3010
|
+
function toWei(value) {
|
|
3011
|
+
if (typeof value === "bigint") {
|
|
3012
|
+
return value;
|
|
3013
|
+
}
|
|
3014
|
+
return parseEther(String(value));
|
|
3015
|
+
}
|
|
3016
|
+
function createRareClient(config) {
|
|
3017
|
+
const { publicClient } = config;
|
|
3018
|
+
const chain = resolveChainFromPublicClient(publicClient);
|
|
3019
|
+
const chainId = chainIds[chain];
|
|
3020
|
+
const addresses = getContractAddresses(chain);
|
|
3021
|
+
return {
|
|
3022
|
+
chain,
|
|
3023
|
+
chainId,
|
|
3024
|
+
contracts: {
|
|
3025
|
+
factory: addresses.factory,
|
|
3026
|
+
auction: addresses.auction
|
|
3027
|
+
},
|
|
3028
|
+
deploy: {
|
|
3029
|
+
async erc721(params) {
|
|
3030
|
+
const { walletClient, account } = requireWallet(config);
|
|
3031
|
+
let txHash;
|
|
3032
|
+
if (params.maxTokens !== void 0) {
|
|
3033
|
+
txHash = await walletClient.writeContract({
|
|
3034
|
+
address: addresses.factory,
|
|
3035
|
+
abi: factoryAbi,
|
|
3036
|
+
functionName: "createSovereignBatchMint",
|
|
3037
|
+
args: [params.name, params.symbol, toInteger(params.maxTokens, "maxTokens")],
|
|
3038
|
+
account,
|
|
3039
|
+
chain: void 0
|
|
3040
|
+
});
|
|
3041
|
+
} else {
|
|
3042
|
+
txHash = await walletClient.writeContract({
|
|
3043
|
+
address: addresses.factory,
|
|
3044
|
+
abi: factoryAbi,
|
|
3045
|
+
functionName: "createSovereignBatchMint",
|
|
3046
|
+
args: [params.name, params.symbol],
|
|
3047
|
+
account,
|
|
3048
|
+
chain: void 0
|
|
3049
|
+
});
|
|
3050
|
+
}
|
|
3051
|
+
const receipt = await publicClient.waitForTransactionReceipt({ hash: txHash });
|
|
3052
|
+
const logs = parseEventLogs({
|
|
3053
|
+
abi: factoryAbi,
|
|
3054
|
+
logs: receipt.logs,
|
|
3055
|
+
eventName: "SovereignBatchMintCreated"
|
|
3056
|
+
});
|
|
3057
|
+
return {
|
|
3058
|
+
txHash,
|
|
3059
|
+
receipt,
|
|
3060
|
+
contract: logs[0]?.args.contractAddress
|
|
3061
|
+
};
|
|
3062
|
+
}
|
|
3063
|
+
},
|
|
3064
|
+
mint: {
|
|
3065
|
+
async mintTo(params) {
|
|
3066
|
+
const { walletClient, account, accountAddress } = requireWallet(config);
|
|
3067
|
+
const useMintTo = Boolean(params.to || params.royaltyReceiver);
|
|
3068
|
+
let txHash;
|
|
3069
|
+
if (useMintTo) {
|
|
3070
|
+
const receiver = params.to ?? accountAddress;
|
|
3071
|
+
const royaltyReceiver = params.royaltyReceiver ?? accountAddress;
|
|
3072
|
+
txHash = await walletClient.writeContract({
|
|
3073
|
+
address: params.contract,
|
|
3074
|
+
abi: tokenAbi,
|
|
3075
|
+
functionName: "mintTo",
|
|
3076
|
+
args: [params.tokenUri, receiver, royaltyReceiver],
|
|
3077
|
+
account,
|
|
3078
|
+
chain: void 0
|
|
3079
|
+
});
|
|
3080
|
+
} else {
|
|
3081
|
+
txHash = await walletClient.writeContract({
|
|
3082
|
+
address: params.contract,
|
|
3083
|
+
abi: tokenAbi,
|
|
3084
|
+
functionName: "addNewToken",
|
|
3085
|
+
args: [params.tokenUri],
|
|
3086
|
+
account,
|
|
3087
|
+
chain: void 0
|
|
3088
|
+
});
|
|
3089
|
+
}
|
|
3090
|
+
const receipt = await publicClient.waitForTransactionReceipt({ hash: txHash });
|
|
3091
|
+
const logs = parseEventLogs({
|
|
3092
|
+
abi: tokenAbi,
|
|
3093
|
+
logs: receipt.logs,
|
|
3094
|
+
eventName: "Transfer"
|
|
3095
|
+
});
|
|
3096
|
+
return {
|
|
3097
|
+
txHash,
|
|
3098
|
+
receipt,
|
|
3099
|
+
tokenId: logs[0]?.args.tokenId
|
|
3100
|
+
};
|
|
3101
|
+
}
|
|
3102
|
+
},
|
|
3103
|
+
auction: {
|
|
3104
|
+
async create(params) {
|
|
3105
|
+
const { walletClient, account, accountAddress } = requireWallet(config);
|
|
3106
|
+
const nftAddress = params.contract;
|
|
3107
|
+
const currency = params.currency ?? ETH_ADDRESS2;
|
|
3108
|
+
const tokenId = toInteger(params.tokenId, "tokenId");
|
|
3109
|
+
const startingPrice = toWei(params.startingPrice);
|
|
3110
|
+
const duration = toInteger(params.duration, "duration");
|
|
3111
|
+
const splitAddresses = params.splitAddresses ?? [accountAddress];
|
|
3112
|
+
const splitRatios = params.splitRatios ?? [100];
|
|
3113
|
+
let approvalTxHash;
|
|
3114
|
+
if (params.autoApprove !== false) {
|
|
3115
|
+
const isApproved = await publicClient.readContract({
|
|
3116
|
+
address: nftAddress,
|
|
3117
|
+
abi: approvalAbi,
|
|
3118
|
+
functionName: "isApprovedForAll",
|
|
3119
|
+
args: [accountAddress, addresses.auction]
|
|
3120
|
+
});
|
|
3121
|
+
if (!isApproved) {
|
|
3122
|
+
approvalTxHash = await walletClient.writeContract({
|
|
3123
|
+
address: nftAddress,
|
|
3124
|
+
abi: approvalAbi,
|
|
3125
|
+
functionName: "setApprovalForAll",
|
|
3126
|
+
args: [addresses.auction, true],
|
|
3127
|
+
account,
|
|
3128
|
+
chain: void 0
|
|
3129
|
+
});
|
|
3130
|
+
await publicClient.waitForTransactionReceipt({ hash: approvalTxHash });
|
|
3131
|
+
}
|
|
3132
|
+
}
|
|
3133
|
+
const auctionType = await publicClient.readContract({
|
|
3134
|
+
address: addresses.auction,
|
|
3135
|
+
abi: auctionAbi,
|
|
3136
|
+
functionName: "COLDIE_AUCTION"
|
|
3137
|
+
});
|
|
3138
|
+
const txHash = await walletClient.writeContract({
|
|
3139
|
+
address: addresses.auction,
|
|
3140
|
+
abi: auctionAbi,
|
|
3141
|
+
functionName: "configureAuction",
|
|
3142
|
+
args: [
|
|
3143
|
+
auctionType,
|
|
3144
|
+
nftAddress,
|
|
3145
|
+
tokenId,
|
|
3146
|
+
startingPrice,
|
|
3147
|
+
currency,
|
|
3148
|
+
duration,
|
|
3149
|
+
0n,
|
|
3150
|
+
splitAddresses,
|
|
3151
|
+
splitRatios
|
|
3152
|
+
],
|
|
3153
|
+
account,
|
|
3154
|
+
chain: void 0
|
|
3155
|
+
});
|
|
3156
|
+
const receipt = await publicClient.waitForTransactionReceipt({ hash: txHash });
|
|
3157
|
+
return {
|
|
3158
|
+
txHash,
|
|
3159
|
+
receipt,
|
|
3160
|
+
approvalTxHash
|
|
3161
|
+
};
|
|
3162
|
+
},
|
|
3163
|
+
async bid(params) {
|
|
3164
|
+
const { walletClient, account, accountAddress } = requireWallet(config);
|
|
3165
|
+
const currency = params.currency ?? ETH_ADDRESS2;
|
|
3166
|
+
const amount = toWei(params.amount);
|
|
3167
|
+
const isEth = currency === ETH_ADDRESS2;
|
|
3168
|
+
let value = 0n;
|
|
3169
|
+
if (isEth) {
|
|
3170
|
+
const settingsAddress = await publicClient.readContract({
|
|
3171
|
+
address: addresses.auction,
|
|
3172
|
+
abi: auctionAbi,
|
|
3173
|
+
functionName: "marketplaceSettings"
|
|
3174
|
+
});
|
|
3175
|
+
const fee = await publicClient.readContract({
|
|
3176
|
+
address: settingsAddress,
|
|
3177
|
+
abi: marketplaceSettingsAbi,
|
|
3178
|
+
functionName: "calculateMarketplaceFee",
|
|
3179
|
+
args: [amount]
|
|
3180
|
+
});
|
|
3181
|
+
value = amount + fee;
|
|
3182
|
+
} else {
|
|
3183
|
+
try {
|
|
3184
|
+
const allowance = await publicClient.readContract({
|
|
3185
|
+
address: currency,
|
|
3186
|
+
abi: erc20Abi,
|
|
3187
|
+
functionName: "allowance",
|
|
3188
|
+
args: [accountAddress, addresses.auction]
|
|
3189
|
+
});
|
|
3190
|
+
if (BigInt(allowance) < amount) {
|
|
3191
|
+
const approveTx = await walletClient.writeContract({
|
|
3192
|
+
address: currency,
|
|
3193
|
+
abi: erc20Abi,
|
|
3194
|
+
functionName: "approve",
|
|
3195
|
+
args: [addresses.auction, maxUint256],
|
|
3196
|
+
account,
|
|
3197
|
+
chain: void 0
|
|
3198
|
+
});
|
|
3199
|
+
await publicClient.waitForTransactionReceipt({ hash: approveTx });
|
|
3200
|
+
}
|
|
3201
|
+
} catch {
|
|
3202
|
+
const approveTx = await walletClient.writeContract({
|
|
3203
|
+
address: currency,
|
|
3204
|
+
abi: erc20Abi,
|
|
3205
|
+
functionName: "approve",
|
|
3206
|
+
args: [addresses.auction, maxUint256],
|
|
3207
|
+
account,
|
|
3208
|
+
chain: void 0
|
|
3209
|
+
});
|
|
3210
|
+
await publicClient.waitForTransactionReceipt({ hash: approveTx });
|
|
3211
|
+
}
|
|
3212
|
+
}
|
|
3213
|
+
const txHash = await walletClient.writeContract({
|
|
3214
|
+
address: addresses.auction,
|
|
3215
|
+
abi: auctionAbi,
|
|
3216
|
+
functionName: "bid",
|
|
3217
|
+
args: [params.contract, toInteger(params.tokenId, "tokenId"), currency, amount],
|
|
3218
|
+
account,
|
|
3219
|
+
chain: void 0,
|
|
3220
|
+
value
|
|
3221
|
+
});
|
|
3222
|
+
const receipt = await publicClient.waitForTransactionReceipt({ hash: txHash });
|
|
3223
|
+
return { txHash, receipt };
|
|
3224
|
+
},
|
|
3225
|
+
async settle(params) {
|
|
3226
|
+
const { walletClient, account } = requireWallet(config);
|
|
3227
|
+
const txHash = await walletClient.writeContract({
|
|
3228
|
+
address: addresses.auction,
|
|
3229
|
+
abi: auctionAbi,
|
|
3230
|
+
functionName: "settleAuction",
|
|
3231
|
+
args: [params.contract, toInteger(params.tokenId, "tokenId")],
|
|
3232
|
+
account,
|
|
3233
|
+
chain: void 0
|
|
3234
|
+
});
|
|
3235
|
+
const receipt = await publicClient.waitForTransactionReceipt({ hash: txHash });
|
|
3236
|
+
return { txHash, receipt };
|
|
3237
|
+
},
|
|
3238
|
+
async cancel(params) {
|
|
3239
|
+
const { walletClient, account } = requireWallet(config);
|
|
3240
|
+
const txHash = await walletClient.writeContract({
|
|
3241
|
+
address: addresses.auction,
|
|
3242
|
+
abi: auctionAbi,
|
|
3243
|
+
functionName: "cancelAuction",
|
|
3244
|
+
args: [params.contract, toInteger(params.tokenId, "tokenId")],
|
|
3245
|
+
account,
|
|
3246
|
+
chain: void 0
|
|
3247
|
+
});
|
|
3248
|
+
const receipt = await publicClient.waitForTransactionReceipt({ hash: txHash });
|
|
3249
|
+
return { txHash, receipt };
|
|
3250
|
+
},
|
|
3251
|
+
async getStatus(params) {
|
|
3252
|
+
const result = await publicClient.readContract({
|
|
3253
|
+
address: addresses.auction,
|
|
3254
|
+
abi: auctionAbi,
|
|
3255
|
+
functionName: "getAuctionDetails",
|
|
3256
|
+
args: [params.contract, toInteger(params.tokenId, "tokenId")]
|
|
3257
|
+
});
|
|
3258
|
+
const [
|
|
3259
|
+
seller,
|
|
3260
|
+
creationBlock,
|
|
3261
|
+
startingTime,
|
|
3262
|
+
lengthOfAuction,
|
|
3263
|
+
currency,
|
|
3264
|
+
minimumBid,
|
|
3265
|
+
auctionType,
|
|
3266
|
+
splitAddresses,
|
|
3267
|
+
splitRatios
|
|
3268
|
+
] = result;
|
|
3269
|
+
const started = startingTime > 0n;
|
|
3270
|
+
const endTime = started ? startingTime + lengthOfAuction : null;
|
|
3271
|
+
const now = BigInt(Math.floor(Date.now() / 1e3));
|
|
3272
|
+
let status = "PENDING";
|
|
3273
|
+
if (started) {
|
|
3274
|
+
status = endTime !== null && now >= endTime ? "ENDED" : "RUNNING";
|
|
3275
|
+
}
|
|
3276
|
+
return {
|
|
3277
|
+
seller,
|
|
3278
|
+
creationBlock,
|
|
3279
|
+
startingTime,
|
|
3280
|
+
lengthOfAuction,
|
|
3281
|
+
currency,
|
|
3282
|
+
minimumBid,
|
|
3283
|
+
auctionType,
|
|
3284
|
+
splitAddresses: [...splitAddresses],
|
|
3285
|
+
splitRatios: [...splitRatios],
|
|
3286
|
+
isEth: currency === ETH_ADDRESS2,
|
|
3287
|
+
started,
|
|
3288
|
+
endTime,
|
|
3289
|
+
status
|
|
3290
|
+
};
|
|
3291
|
+
}
|
|
3292
|
+
},
|
|
3293
|
+
offer: {
|
|
3294
|
+
async create(params) {
|
|
3295
|
+
const { walletClient, account, accountAddress } = requireWallet(config);
|
|
3296
|
+
const currency = params.currency ?? ETH_ADDRESS2;
|
|
3297
|
+
const amount = toWei(params.amount);
|
|
3298
|
+
const isEth = currency === ETH_ADDRESS2;
|
|
3299
|
+
const convertible = params.convertible ?? false;
|
|
3300
|
+
let value = 0n;
|
|
3301
|
+
if (isEth) {
|
|
3302
|
+
const settingsAddress = await publicClient.readContract({
|
|
3303
|
+
address: addresses.auction,
|
|
3304
|
+
abi: auctionAbi,
|
|
3305
|
+
functionName: "marketplaceSettings"
|
|
3306
|
+
});
|
|
3307
|
+
const fee = await publicClient.readContract({
|
|
3308
|
+
address: settingsAddress,
|
|
3309
|
+
abi: marketplaceSettingsAbi,
|
|
3310
|
+
functionName: "calculateMarketplaceFee",
|
|
3311
|
+
args: [amount]
|
|
3312
|
+
});
|
|
3313
|
+
value = amount + fee;
|
|
3314
|
+
} else {
|
|
3315
|
+
try {
|
|
3316
|
+
const allowance = await publicClient.readContract({
|
|
3317
|
+
address: currency,
|
|
3318
|
+
abi: erc20Abi,
|
|
3319
|
+
functionName: "allowance",
|
|
3320
|
+
args: [accountAddress, addresses.auction]
|
|
3321
|
+
});
|
|
3322
|
+
if (BigInt(allowance) < amount) {
|
|
3323
|
+
const approveTx = await walletClient.writeContract({
|
|
3324
|
+
address: currency,
|
|
3325
|
+
abi: erc20Abi,
|
|
3326
|
+
functionName: "approve",
|
|
3327
|
+
args: [addresses.auction, maxUint256],
|
|
3328
|
+
account,
|
|
3329
|
+
chain: void 0
|
|
3330
|
+
});
|
|
3331
|
+
await publicClient.waitForTransactionReceipt({ hash: approveTx });
|
|
3332
|
+
}
|
|
3333
|
+
} catch {
|
|
3334
|
+
const approveTx = await walletClient.writeContract({
|
|
3335
|
+
address: currency,
|
|
3336
|
+
abi: erc20Abi,
|
|
3337
|
+
functionName: "approve",
|
|
3338
|
+
args: [addresses.auction, maxUint256],
|
|
3339
|
+
account,
|
|
3340
|
+
chain: void 0
|
|
3341
|
+
});
|
|
3342
|
+
await publicClient.waitForTransactionReceipt({ hash: approveTx });
|
|
3343
|
+
}
|
|
3344
|
+
}
|
|
3345
|
+
const txHash = await walletClient.writeContract({
|
|
3346
|
+
address: addresses.auction,
|
|
3347
|
+
abi: auctionAbi,
|
|
3348
|
+
functionName: "offer",
|
|
3349
|
+
args: [params.contract, toInteger(params.tokenId, "tokenId"), currency, amount, convertible],
|
|
3350
|
+
account,
|
|
3351
|
+
chain: void 0,
|
|
3352
|
+
value
|
|
3353
|
+
});
|
|
3354
|
+
const receipt = await publicClient.waitForTransactionReceipt({ hash: txHash });
|
|
3355
|
+
return { txHash, receipt };
|
|
3356
|
+
},
|
|
3357
|
+
async cancel(params) {
|
|
3358
|
+
const { walletClient, account } = requireWallet(config);
|
|
3359
|
+
const currency = params.currency ?? ETH_ADDRESS2;
|
|
3360
|
+
const txHash = await walletClient.writeContract({
|
|
3361
|
+
address: addresses.auction,
|
|
3362
|
+
abi: auctionAbi,
|
|
3363
|
+
functionName: "cancelOffer",
|
|
3364
|
+
args: [params.contract, toInteger(params.tokenId, "tokenId"), currency],
|
|
3365
|
+
account,
|
|
3366
|
+
chain: void 0
|
|
3367
|
+
});
|
|
3368
|
+
const receipt = await publicClient.waitForTransactionReceipt({ hash: txHash });
|
|
3369
|
+
return { txHash, receipt };
|
|
3370
|
+
},
|
|
3371
|
+
async accept(params) {
|
|
3372
|
+
const { walletClient, account, accountAddress } = requireWallet(config);
|
|
3373
|
+
const currency = params.currency ?? ETH_ADDRESS2;
|
|
3374
|
+
const amount = toWei(params.amount);
|
|
3375
|
+
const splitAddresses = params.splitAddresses ?? [accountAddress];
|
|
3376
|
+
const splitRatios = params.splitRatios ?? [100];
|
|
3377
|
+
const txHash = await walletClient.writeContract({
|
|
3378
|
+
address: addresses.auction,
|
|
3379
|
+
abi: auctionAbi,
|
|
3380
|
+
functionName: "acceptOffer",
|
|
3381
|
+
args: [params.contract, toInteger(params.tokenId, "tokenId"), currency, amount, splitAddresses, splitRatios],
|
|
3382
|
+
account,
|
|
3383
|
+
chain: void 0
|
|
3384
|
+
});
|
|
3385
|
+
const receipt = await publicClient.waitForTransactionReceipt({ hash: txHash });
|
|
3386
|
+
return { txHash, receipt };
|
|
3387
|
+
},
|
|
3388
|
+
async getStatus(params) {
|
|
3389
|
+
const currency = params.currency ?? ETH_ADDRESS2;
|
|
3390
|
+
const [buyer, amount, timestamp, marketplaceFee, convertible] = await publicClient.readContract({
|
|
3391
|
+
address: addresses.auction,
|
|
3392
|
+
abi: auctionAbi,
|
|
3393
|
+
functionName: "tokenCurrentOffers",
|
|
3394
|
+
args: [params.contract, toInteger(params.tokenId, "tokenId"), currency]
|
|
3395
|
+
});
|
|
3396
|
+
const hasOffer = amount > 0n;
|
|
3397
|
+
return { buyer, amount, timestamp, marketplaceFee, convertible, hasOffer };
|
|
3398
|
+
}
|
|
3399
|
+
},
|
|
3400
|
+
listing: {
|
|
3401
|
+
async create(params) {
|
|
3402
|
+
const { walletClient, account, accountAddress } = requireWallet(config);
|
|
3403
|
+
const currency = params.currency ?? ETH_ADDRESS2;
|
|
3404
|
+
const price = toWei(params.price);
|
|
3405
|
+
const target = params.target ?? ETH_ADDRESS2;
|
|
3406
|
+
const splitAddresses = params.splitAddresses ?? [accountAddress];
|
|
3407
|
+
const splitRatios = params.splitRatios ?? [100];
|
|
3408
|
+
const nftAddress = params.contract;
|
|
3409
|
+
let approvalTxHash;
|
|
3410
|
+
if (params.autoApprove !== false) {
|
|
3411
|
+
const isApproved = await publicClient.readContract({
|
|
3412
|
+
address: nftAddress,
|
|
3413
|
+
abi: approvalAbi,
|
|
3414
|
+
functionName: "isApprovedForAll",
|
|
3415
|
+
args: [accountAddress, addresses.auction]
|
|
3416
|
+
});
|
|
3417
|
+
if (!isApproved) {
|
|
3418
|
+
approvalTxHash = await walletClient.writeContract({
|
|
3419
|
+
address: nftAddress,
|
|
3420
|
+
abi: approvalAbi,
|
|
3421
|
+
functionName: "setApprovalForAll",
|
|
3422
|
+
args: [addresses.auction, true],
|
|
3423
|
+
account,
|
|
3424
|
+
chain: void 0
|
|
3425
|
+
});
|
|
3426
|
+
await publicClient.waitForTransactionReceipt({ hash: approvalTxHash });
|
|
3427
|
+
}
|
|
3428
|
+
}
|
|
3429
|
+
const txHash = await walletClient.writeContract({
|
|
3430
|
+
address: addresses.auction,
|
|
3431
|
+
abi: auctionAbi,
|
|
3432
|
+
functionName: "setSalePrice",
|
|
3433
|
+
args: [nftAddress, toInteger(params.tokenId, "tokenId"), currency, price, target, splitAddresses, splitRatios],
|
|
3434
|
+
account,
|
|
3435
|
+
chain: void 0
|
|
3436
|
+
});
|
|
3437
|
+
const receipt = await publicClient.waitForTransactionReceipt({ hash: txHash });
|
|
3438
|
+
return { txHash, receipt, approvalTxHash };
|
|
3439
|
+
},
|
|
3440
|
+
async cancel(params) {
|
|
3441
|
+
const { walletClient, account } = requireWallet(config);
|
|
3442
|
+
const target = params.target ?? ETH_ADDRESS2;
|
|
3443
|
+
const txHash = await walletClient.writeContract({
|
|
3444
|
+
address: addresses.auction,
|
|
3445
|
+
abi: auctionAbi,
|
|
3446
|
+
functionName: "removeSalePrice",
|
|
3447
|
+
args: [params.contract, toInteger(params.tokenId, "tokenId"), target],
|
|
3448
|
+
account,
|
|
3449
|
+
chain: void 0
|
|
3450
|
+
});
|
|
3451
|
+
const receipt = await publicClient.waitForTransactionReceipt({ hash: txHash });
|
|
3452
|
+
return { txHash, receipt };
|
|
3453
|
+
},
|
|
3454
|
+
async buy(params) {
|
|
3455
|
+
const { walletClient, account, accountAddress } = requireWallet(config);
|
|
3456
|
+
const currency = params.currency ?? ETH_ADDRESS2;
|
|
3457
|
+
const amount = toWei(params.amount);
|
|
3458
|
+
const isEth = currency === ETH_ADDRESS2;
|
|
3459
|
+
let value = 0n;
|
|
3460
|
+
if (isEth) {
|
|
3461
|
+
const settingsAddress = await publicClient.readContract({
|
|
3462
|
+
address: addresses.auction,
|
|
3463
|
+
abi: auctionAbi,
|
|
3464
|
+
functionName: "marketplaceSettings"
|
|
3465
|
+
});
|
|
3466
|
+
const fee = await publicClient.readContract({
|
|
3467
|
+
address: settingsAddress,
|
|
3468
|
+
abi: marketplaceSettingsAbi,
|
|
3469
|
+
functionName: "calculateMarketplaceFee",
|
|
3470
|
+
args: [amount]
|
|
3471
|
+
});
|
|
3472
|
+
value = amount + fee;
|
|
3473
|
+
} else {
|
|
3474
|
+
try {
|
|
3475
|
+
const allowance = await publicClient.readContract({
|
|
3476
|
+
address: currency,
|
|
3477
|
+
abi: erc20Abi,
|
|
3478
|
+
functionName: "allowance",
|
|
3479
|
+
args: [accountAddress, addresses.auction]
|
|
3480
|
+
});
|
|
3481
|
+
if (BigInt(allowance) < amount) {
|
|
3482
|
+
const approveTx = await walletClient.writeContract({
|
|
3483
|
+
address: currency,
|
|
3484
|
+
abi: erc20Abi,
|
|
3485
|
+
functionName: "approve",
|
|
3486
|
+
args: [addresses.auction, maxUint256],
|
|
3487
|
+
account,
|
|
3488
|
+
chain: void 0
|
|
3489
|
+
});
|
|
3490
|
+
await publicClient.waitForTransactionReceipt({ hash: approveTx });
|
|
3491
|
+
}
|
|
3492
|
+
} catch {
|
|
3493
|
+
const approveTx = await walletClient.writeContract({
|
|
3494
|
+
address: currency,
|
|
3495
|
+
abi: erc20Abi,
|
|
3496
|
+
functionName: "approve",
|
|
3497
|
+
args: [addresses.auction, maxUint256],
|
|
3498
|
+
account,
|
|
3499
|
+
chain: void 0
|
|
3500
|
+
});
|
|
3501
|
+
await publicClient.waitForTransactionReceipt({ hash: approveTx });
|
|
3502
|
+
}
|
|
3503
|
+
}
|
|
3504
|
+
const txHash = await walletClient.writeContract({
|
|
3505
|
+
address: addresses.auction,
|
|
3506
|
+
abi: auctionAbi,
|
|
3507
|
+
functionName: "buy",
|
|
3508
|
+
args: [params.contract, toInteger(params.tokenId, "tokenId"), currency, amount],
|
|
3509
|
+
account,
|
|
3510
|
+
chain: void 0,
|
|
3511
|
+
value
|
|
3512
|
+
});
|
|
3513
|
+
const receipt = await publicClient.waitForTransactionReceipt({ hash: txHash });
|
|
3514
|
+
return { txHash, receipt };
|
|
3515
|
+
},
|
|
3516
|
+
async getStatus(params) {
|
|
3517
|
+
const target = params.target ?? ETH_ADDRESS2;
|
|
3518
|
+
const [seller, currencyAddress, amount] = await publicClient.readContract({
|
|
3519
|
+
address: addresses.auction,
|
|
3520
|
+
abi: auctionAbi,
|
|
3521
|
+
functionName: "tokenSalePrices",
|
|
3522
|
+
args: [params.contract, toInteger(params.tokenId, "tokenId"), target]
|
|
3523
|
+
});
|
|
3524
|
+
const hasListing = amount > 0n;
|
|
3525
|
+
const isEth = currencyAddress === ETH_ADDRESS2;
|
|
3526
|
+
return { seller, currencyAddress, amount, hasListing, isEth };
|
|
3527
|
+
}
|
|
3528
|
+
},
|
|
3529
|
+
search: {
|
|
3530
|
+
async nfts(params = {}) {
|
|
3531
|
+
const requestParams = params.chainIds ? params : { ...params, chainIds: [chainId] };
|
|
3532
|
+
return searchNfts(requestParams);
|
|
3533
|
+
},
|
|
3534
|
+
async collections(params = {}) {
|
|
3535
|
+
return searchCollections(params);
|
|
3536
|
+
}
|
|
3537
|
+
},
|
|
3538
|
+
media: {
|
|
3539
|
+
async upload(buffer, filename) {
|
|
3540
|
+
return uploadMedia(buffer, filename);
|
|
3541
|
+
},
|
|
3542
|
+
async pinMetadata(opts) {
|
|
3543
|
+
return pinMetadata(opts);
|
|
3544
|
+
}
|
|
3545
|
+
},
|
|
3546
|
+
import: {
|
|
3547
|
+
async erc721(params) {
|
|
3548
|
+
const owner = params.owner ?? config.account ?? config.walletClient?.account?.address;
|
|
3549
|
+
if (!owner) {
|
|
3550
|
+
throw new Error("No owner available for import. Pass params.owner or provide config.account/walletClient with an account.");
|
|
3551
|
+
}
|
|
3552
|
+
await importErc721({
|
|
3553
|
+
chainId,
|
|
3554
|
+
contract: params.contract,
|
|
3555
|
+
owner
|
|
3556
|
+
});
|
|
3557
|
+
}
|
|
3558
|
+
},
|
|
3559
|
+
token: {
|
|
3560
|
+
async getContractInfo(params) {
|
|
3561
|
+
const [name, symbol, totalSupply] = await Promise.all([
|
|
3562
|
+
publicClient.readContract({
|
|
3563
|
+
address: params.contract,
|
|
3564
|
+
abi: tokenAbi,
|
|
3565
|
+
functionName: "name"
|
|
3566
|
+
}),
|
|
3567
|
+
publicClient.readContract({
|
|
3568
|
+
address: params.contract,
|
|
3569
|
+
abi: tokenAbi,
|
|
3570
|
+
functionName: "symbol"
|
|
3571
|
+
}),
|
|
3572
|
+
publicClient.readContract({
|
|
3573
|
+
address: params.contract,
|
|
3574
|
+
abi: tokenAbi,
|
|
3575
|
+
functionName: "totalSupply"
|
|
3576
|
+
})
|
|
3577
|
+
]);
|
|
3578
|
+
return {
|
|
3579
|
+
contract: params.contract,
|
|
3580
|
+
chain,
|
|
3581
|
+
name,
|
|
3582
|
+
symbol,
|
|
3583
|
+
totalSupply
|
|
3584
|
+
};
|
|
3585
|
+
},
|
|
3586
|
+
async getTokenInfo(params) {
|
|
3587
|
+
const tokenId = toInteger(params.tokenId, "tokenId");
|
|
3588
|
+
const [owner, tokenUri] = await Promise.all([
|
|
3589
|
+
publicClient.readContract({
|
|
3590
|
+
address: params.contract,
|
|
3591
|
+
abi: tokenAbi,
|
|
3592
|
+
functionName: "ownerOf",
|
|
3593
|
+
args: [tokenId]
|
|
3594
|
+
}),
|
|
3595
|
+
publicClient.readContract({
|
|
3596
|
+
address: params.contract,
|
|
3597
|
+
abi: tokenAbi,
|
|
3598
|
+
functionName: "tokenURI",
|
|
3599
|
+
args: [tokenId]
|
|
3600
|
+
})
|
|
3601
|
+
]);
|
|
3602
|
+
return {
|
|
3603
|
+
contract: params.contract,
|
|
3604
|
+
tokenId,
|
|
3605
|
+
owner,
|
|
3606
|
+
tokenUri
|
|
3607
|
+
};
|
|
3608
|
+
}
|
|
3609
|
+
}
|
|
3610
|
+
};
|
|
3611
|
+
}
|
|
3612
|
+
|
|
3613
|
+
// src/commands/deploy.ts
|
|
3614
|
+
function deployErc721Command() {
|
|
3615
|
+
const cmd = new Command2("erc721");
|
|
3616
|
+
cmd.description("Deploy a new ERC-721 NFT contract via the RARE factory");
|
|
3617
|
+
cmd.argument("<name>", "name of the NFT collection").argument("<symbol>", "symbol of the NFT collection").option("--max-tokens <number>", "maximum number of tokens (optional)").option("--chain <chain>", "chain to use (mainnet, sepolia, base, base-sepolia)").action(async (name, symbol, opts) => {
|
|
3618
|
+
const chain = getActiveChain(opts.chain);
|
|
3619
|
+
const { client } = getWalletClient(chain);
|
|
3620
|
+
const publicClient = getPublicClient(chain);
|
|
3621
|
+
const rare = createRareClient({ publicClient, walletClient: client });
|
|
3622
|
+
console.log(`Deploying ERC-721 contract on ${chain}...`);
|
|
3623
|
+
console.log(` Factory: ${rare.contracts.factory}`);
|
|
3624
|
+
console.log(` Name: ${name}`);
|
|
3625
|
+
console.log(` Symbol: ${symbol}`);
|
|
3626
|
+
if (opts.maxTokens) console.log(` Max tokens: ${opts.maxTokens}`);
|
|
3627
|
+
console.log("Waiting for confirmation...");
|
|
3628
|
+
const result = await rare.deploy.erc721({
|
|
3629
|
+
name,
|
|
3630
|
+
symbol,
|
|
3631
|
+
maxTokens: opts.maxTokens
|
|
3632
|
+
});
|
|
3633
|
+
console.log(`Transaction sent: ${result.txHash}`);
|
|
3634
|
+
if (result.contract) {
|
|
3635
|
+
console.log(`
|
|
3636
|
+
ERC-721 contract deployed at: ${result.contract}`);
|
|
3637
|
+
} else {
|
|
3638
|
+
console.log(`
|
|
3639
|
+
Transaction confirmed. Block: ${result.receipt.blockNumber}`);
|
|
3640
|
+
console.log("Could not parse deployed address from logs.");
|
|
3641
|
+
}
|
|
3642
|
+
});
|
|
3643
|
+
return cmd;
|
|
3644
|
+
}
|
|
3645
|
+
function deployCommand() {
|
|
3646
|
+
const cmd = new Command2("deploy");
|
|
3647
|
+
cmd.description("Deploy a new contract via the RARE protocol");
|
|
3648
|
+
cmd.addCommand(deployErc721Command());
|
|
3649
|
+
return cmd;
|
|
3650
|
+
}
|
|
3651
|
+
|
|
3652
|
+
// src/commands/mint.ts
|
|
3653
|
+
import { Command as Command3 } from "commander";
|
|
3654
|
+
|
|
3655
|
+
// src/ipfs.ts
|
|
3656
|
+
import { basename, extname } from "path";
|
|
3657
|
+
import { readFile, stat } from "fs/promises";
|
|
3658
|
+
import { isAddress as isAddress2 } from "viem";
|
|
3659
|
+
var API_BASE_URL2 = "https://api.superrare.org";
|
|
3660
|
+
var MIME_TYPES2 = {
|
|
3661
|
+
".png": "image/png",
|
|
3662
|
+
".jpg": "image/jpeg",
|
|
3663
|
+
".jpeg": "image/jpeg",
|
|
3664
|
+
".gif": "image/gif",
|
|
3665
|
+
".webp": "image/webp",
|
|
3666
|
+
".svg": "image/svg+xml",
|
|
3667
|
+
".mp4": "video/mp4",
|
|
3668
|
+
".mov": "video/quicktime",
|
|
3669
|
+
".webm": "video/webm",
|
|
3670
|
+
".glb": "model/gltf-binary",
|
|
3671
|
+
".gltf": "model/gltf+json",
|
|
3672
|
+
".html": "text/html"
|
|
3673
|
+
};
|
|
3674
|
+
function inferMimeType2(filename) {
|
|
3675
|
+
const ext = extname(filename).toLowerCase();
|
|
3676
|
+
return MIME_TYPES2[ext] ?? "application/octet-stream";
|
|
3677
|
+
}
|
|
3678
|
+
function assertPositiveInteger2(value, fieldName) {
|
|
3679
|
+
if (!Number.isInteger(value) || value <= 0) {
|
|
3680
|
+
throw new Error(`${fieldName} must be a positive integer`);
|
|
3681
|
+
}
|
|
3682
|
+
}
|
|
3683
|
+
function assertEvmAddress2(value, fieldName) {
|
|
3684
|
+
if (!isAddress2(value)) {
|
|
3685
|
+
throw new Error(`${fieldName} must be a valid EVM address`);
|
|
3686
|
+
}
|
|
3687
|
+
}
|
|
3688
|
+
function parseDimensions2(dimensions) {
|
|
3689
|
+
if (!dimensions) return void 0;
|
|
3690
|
+
const [w, h] = dimensions.split("x");
|
|
3691
|
+
if (!w || !h) return void 0;
|
|
3692
|
+
const width = parseInt(w, 10);
|
|
3693
|
+
const height = parseInt(h, 10);
|
|
3694
|
+
if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) return void 0;
|
|
3695
|
+
return { width, height };
|
|
3696
|
+
}
|
|
3697
|
+
async function apiPost2(path2, payload) {
|
|
3698
|
+
const url = `${API_BASE_URL2}${path2}`;
|
|
3699
|
+
const response = await fetch(url, {
|
|
3700
|
+
method: "POST",
|
|
3701
|
+
headers: { "Content-Type": "application/json" },
|
|
3702
|
+
body: JSON.stringify(payload)
|
|
3703
|
+
});
|
|
3704
|
+
const text = await response.text();
|
|
3705
|
+
const json = text ? JSON.parse(text) : {};
|
|
3706
|
+
if (!response.ok) {
|
|
3707
|
+
const message = json.error ?? text;
|
|
3708
|
+
throw new Error(`API error ${response.status} on ${path2}: ${message}`);
|
|
3709
|
+
}
|
|
3710
|
+
return json;
|
|
3711
|
+
}
|
|
3712
|
+
async function uploadParts2(fileBuffer, partSize, presignedUrls) {
|
|
3713
|
+
const parts = [];
|
|
3714
|
+
for (let i = 0; i < presignedUrls.length; i++) {
|
|
3715
|
+
const start = i * partSize;
|
|
3716
|
+
const end = start + partSize;
|
|
3717
|
+
const partBuffer = fileBuffer.subarray(start, end);
|
|
3718
|
+
const response = await fetch(presignedUrls[i], {
|
|
3719
|
+
method: "PUT",
|
|
3720
|
+
body: new Uint8Array(partBuffer)
|
|
3721
|
+
});
|
|
3722
|
+
if (response.status !== 200 && response.status !== 204) {
|
|
3723
|
+
throw new Error(`Part ${i + 1} upload failed with status ${response.status}`);
|
|
3724
|
+
}
|
|
3725
|
+
const etag = response.headers.get("etag");
|
|
3726
|
+
if (!etag) {
|
|
3727
|
+
throw new Error(`Missing etag header for part ${i + 1}`);
|
|
3728
|
+
}
|
|
3729
|
+
parts.push({ ETag: etag, PartNumber: i + 1 });
|
|
3730
|
+
}
|
|
3731
|
+
return parts;
|
|
3732
|
+
}
|
|
3733
|
+
async function uploadMedia2(filePath, label) {
|
|
3734
|
+
const fileStats = await stat(filePath);
|
|
3735
|
+
const fileSize = fileStats.size;
|
|
3736
|
+
const fileName = basename(filePath);
|
|
3737
|
+
const fileBuffer = await readFile(filePath);
|
|
3738
|
+
const mimeType = inferMimeType2(fileName);
|
|
3739
|
+
console.log(`Uploading ${label}: ${fileName} (${fileSize} bytes, ${mimeType})`);
|
|
3740
|
+
const init = await apiPost2("/api/nft/media-upload-url", {
|
|
3741
|
+
fileSize,
|
|
3742
|
+
filename: fileName
|
|
3743
|
+
});
|
|
3744
|
+
console.log(` Multipart upload initialized (${init.presignedUrls.length} parts)`);
|
|
3745
|
+
const parts = await uploadParts2(fileBuffer, init.partSize, init.presignedUrls);
|
|
3746
|
+
console.log(` All parts uploaded`);
|
|
3747
|
+
const complete = await apiPost2("/api/nft/media-upload-complete", {
|
|
3748
|
+
key: init.key,
|
|
3749
|
+
uploadId: init.uploadId,
|
|
3750
|
+
bucket: init.bucket,
|
|
3751
|
+
parts
|
|
3752
|
+
});
|
|
3753
|
+
console.log(` Upload complete: ${complete.ipfsUrl}`);
|
|
3754
|
+
const generated = await apiPost2("/api/nft/media-generate", {
|
|
3755
|
+
uri: complete.ipfsUrl,
|
|
3756
|
+
mimeType
|
|
3757
|
+
});
|
|
3758
|
+
const dimensions = parseDimensions2(generated.media.dimensions);
|
|
3759
|
+
const entry = {
|
|
3760
|
+
url: generated.media.uri,
|
|
3761
|
+
mimeType: generated.media.mimeType,
|
|
3762
|
+
size: generated.media.size ?? fileSize,
|
|
3763
|
+
...dimensions ? { dimensions } : {}
|
|
3764
|
+
};
|
|
3765
|
+
console.log(` Media generated: ${entry.url}`);
|
|
3766
|
+
return entry;
|
|
3767
|
+
}
|
|
3768
|
+
async function importErc7212(opts) {
|
|
3769
|
+
assertPositiveInteger2(opts.chainId, "chainId");
|
|
3770
|
+
assertEvmAddress2(opts.contractAddress, "contractAddress");
|
|
3771
|
+
assertEvmAddress2(opts.ownerAddress, "ownerAddress");
|
|
3772
|
+
const normalizedContractAddress = opts.contractAddress.toLowerCase();
|
|
3773
|
+
const normalizedOwnerAddress = opts.ownerAddress.toLowerCase();
|
|
3774
|
+
const result = await apiPost2("/api/nft/import-erc721", {
|
|
3775
|
+
chainId: opts.chainId,
|
|
3776
|
+
contractAddress: normalizedContractAddress,
|
|
3777
|
+
ownerAddress: normalizedOwnerAddress
|
|
3778
|
+
});
|
|
3779
|
+
if (result.ok !== true) {
|
|
3780
|
+
throw new Error("Unexpected response from /api/nft/import-erc721");
|
|
3781
|
+
}
|
|
3782
|
+
}
|
|
3783
|
+
async function pinMetadata2(opts) {
|
|
3784
|
+
const nftMedia = {
|
|
3785
|
+
image: opts.image
|
|
3786
|
+
};
|
|
3787
|
+
if (opts.video) {
|
|
3788
|
+
nftMedia.video = opts.video;
|
|
3789
|
+
}
|
|
3790
|
+
const payload = {
|
|
3791
|
+
name: opts.name,
|
|
3792
|
+
description: opts.description,
|
|
3793
|
+
nftMedia,
|
|
3794
|
+
tags: opts.tags ?? []
|
|
3795
|
+
};
|
|
3796
|
+
if (opts.attributes && opts.attributes.length > 0) {
|
|
3797
|
+
payload.attributes = opts.attributes;
|
|
3798
|
+
}
|
|
3799
|
+
console.log("Pinning metadata to IPFS...");
|
|
3800
|
+
const result = await apiPost2("/api/nft/metadata", payload);
|
|
3801
|
+
console.log(`Metadata pinned: ${result.ipfsUrl}`);
|
|
3802
|
+
console.log(`Gateway URL: ${result.gatewayUrl}`);
|
|
3803
|
+
return result.ipfsUrl;
|
|
3804
|
+
}
|
|
3805
|
+
|
|
3806
|
+
// src/commands/mint.ts
|
|
3807
|
+
function parseAttribute(raw) {
|
|
3808
|
+
if (raw.startsWith("{")) {
|
|
3809
|
+
const parsed = JSON.parse(raw);
|
|
3810
|
+
if (parsed.value === void 0) {
|
|
3811
|
+
throw new Error(`Attribute JSON must include "value": ${raw}`);
|
|
3812
|
+
}
|
|
3813
|
+
return parsed;
|
|
3814
|
+
}
|
|
3815
|
+
const eqIndex = raw.indexOf("=");
|
|
3816
|
+
if (eqIndex === -1) {
|
|
3817
|
+
return { value: raw };
|
|
3818
|
+
}
|
|
3819
|
+
const trait_type = raw.slice(0, eqIndex);
|
|
3820
|
+
const rawValue = raw.slice(eqIndex + 1);
|
|
3821
|
+
const numValue = Number(rawValue);
|
|
3822
|
+
const value = rawValue.length > 0 && !Number.isNaN(numValue) ? numValue : rawValue;
|
|
3823
|
+
return { trait_type, value };
|
|
3824
|
+
}
|
|
3825
|
+
function mintCommand() {
|
|
3826
|
+
const cmd = new Command3("mint");
|
|
3827
|
+
cmd.description("Mint a new NFT on a deployed token contract");
|
|
3828
|
+
cmd.requiredOption("--contract <address>", "token contract address").option("--token-uri <uri>", "token metadata URI (skip upload if provided)").option("--name <name>", "NFT name").option("--description <description>", "NFT description").option("--image <path>", "path to image file").option("--video <path>", "path to video file").option("--tag <tag>", "tag (repeatable)", (val, acc) => [...acc, val], []).option("--attribute <attr>", 'attribute as "trait=value" or JSON (repeatable)', (val, acc) => [...acc, val], []).option("--to <address>", "recipient address (defaults to caller)").option("--royalty-receiver <address>", "royalty receiver address (defaults to caller)").option("--chain <chain>", "chain to use (mainnet, sepolia, base, base-sepolia)").action(async (opts) => {
|
|
3829
|
+
let tokenUri;
|
|
3830
|
+
if (opts.tokenUri) {
|
|
3831
|
+
tokenUri = opts.tokenUri;
|
|
3832
|
+
} else {
|
|
3833
|
+
if (!opts.name) {
|
|
3834
|
+
console.error("Error: --name is required when not using --token-uri");
|
|
3835
|
+
process.exit(1);
|
|
3836
|
+
}
|
|
3837
|
+
if (!opts.description) {
|
|
3838
|
+
console.error("Error: --description is required when not using --token-uri");
|
|
3839
|
+
process.exit(1);
|
|
3840
|
+
}
|
|
3841
|
+
if (!opts.image) {
|
|
3842
|
+
console.error("Error: --image is required when not using --token-uri");
|
|
3843
|
+
process.exit(1);
|
|
3844
|
+
}
|
|
3845
|
+
const imageMedia = await uploadMedia2(opts.image, "image");
|
|
3846
|
+
const videoMedia = opts.video ? await uploadMedia2(opts.video, "video") : void 0;
|
|
3847
|
+
const tags = opts.tag.length > 0 ? opts.tag : void 0;
|
|
3848
|
+
const attributes = opts.attribute.length > 0 ? opts.attribute.map(parseAttribute) : void 0;
|
|
3849
|
+
tokenUri = await pinMetadata2({
|
|
3850
|
+
name: opts.name,
|
|
3851
|
+
description: opts.description,
|
|
3852
|
+
image: imageMedia,
|
|
3853
|
+
video: videoMedia,
|
|
3854
|
+
tags,
|
|
3855
|
+
attributes
|
|
3856
|
+
});
|
|
3161
3857
|
}
|
|
3162
|
-
console.log(`
|
|
3163
|
-
Transaction sent: ${txHash}`);
|
|
3164
|
-
const receipt = await publicClient.waitForTransactionReceipt({ hash: txHash });
|
|
3165
|
-
console.log(`Auction created! Block: ${receipt.blockNumber}`);
|
|
3166
|
-
});
|
|
3167
|
-
cmd.command("bid").description("Place a bid on an auction").requiredOption("--contract <address>", "NFT contract address").requiredOption("--token-id <id>", "token ID").requiredOption("--amount <amount>", "bid amount in ETH (or token units)").option("--currency <address>", "ERC20 currency address (defaults to ETH)").option("--chain <chain>", "chain to use (mainnet, sepolia, base, base-sepolia)").action(async (opts) => {
|
|
3168
3858
|
const chain = getActiveChain(opts.chain);
|
|
3169
3859
|
const { client, account } = getWalletClient(chain);
|
|
3170
3860
|
const publicClient = getPublicClient(chain);
|
|
3171
|
-
const
|
|
3172
|
-
const
|
|
3173
|
-
|
|
3174
|
-
|
|
3861
|
+
const rare = createRareClient({ publicClient, walletClient: client });
|
|
3862
|
+
const contractAddress = opts.contract;
|
|
3863
|
+
console.log(`
|
|
3864
|
+
Minting NFT on ${chain}...`);
|
|
3865
|
+
console.log(` Contract: ${contractAddress}`);
|
|
3866
|
+
console.log(` URI: ${tokenUri}`);
|
|
3867
|
+
if (opts.to || opts.royaltyReceiver) {
|
|
3868
|
+
const receiver = opts.to ?? account.address;
|
|
3869
|
+
const royaltyReceiver = opts.royaltyReceiver ?? account.address;
|
|
3870
|
+
console.log(` To: ${receiver}`);
|
|
3871
|
+
console.log(` Royalty receiver: ${royaltyReceiver}`);
|
|
3872
|
+
}
|
|
3873
|
+
console.log("Waiting for confirmation...");
|
|
3874
|
+
const result = await rare.mint.mintTo({
|
|
3875
|
+
contract: contractAddress,
|
|
3876
|
+
tokenUri,
|
|
3877
|
+
to: opts.to,
|
|
3878
|
+
royaltyReceiver: opts.royaltyReceiver
|
|
3879
|
+
});
|
|
3880
|
+
console.log(`Transaction sent: ${result.txHash}`);
|
|
3881
|
+
if (result.tokenId !== void 0) {
|
|
3882
|
+
console.log(`
|
|
3883
|
+
NFT minted! Token ID: ${result.tokenId}`);
|
|
3884
|
+
} else {
|
|
3885
|
+
console.log(`
|
|
3886
|
+
Transaction confirmed. Block: ${result.receipt.blockNumber}`);
|
|
3887
|
+
}
|
|
3888
|
+
});
|
|
3889
|
+
return cmd;
|
|
3890
|
+
}
|
|
3891
|
+
|
|
3892
|
+
// src/commands/auction.ts
|
|
3893
|
+
import { Command as Command4 } from "commander";
|
|
3894
|
+
import { formatEther } from "viem";
|
|
3895
|
+
|
|
3896
|
+
// src/errors.ts
|
|
3897
|
+
function printContractError(error) {
|
|
3898
|
+
if (!(error instanceof Error)) {
|
|
3899
|
+
console.error("\nTransaction failed:", error);
|
|
3900
|
+
process.exit(1);
|
|
3901
|
+
}
|
|
3902
|
+
console.error("\nTransaction failed:");
|
|
3903
|
+
let current = error;
|
|
3904
|
+
let depth = 0;
|
|
3905
|
+
while (current instanceof Error) {
|
|
3906
|
+
const indent = " ".repeat(depth + 1);
|
|
3907
|
+
const msg = current.shortMessage ?? current.message;
|
|
3908
|
+
console.error(`${indent}${msg}`);
|
|
3909
|
+
if ("reason" in current && current.reason) {
|
|
3910
|
+
console.error(`${indent}Revert reason: ${current.reason}`);
|
|
3911
|
+
}
|
|
3912
|
+
if ("metaMessages" in current && Array.isArray(current.metaMessages)) {
|
|
3913
|
+
for (const line of current.metaMessages) {
|
|
3914
|
+
if (line.trim()) console.error(`${indent}${line.trim()}`);
|
|
3915
|
+
}
|
|
3916
|
+
}
|
|
3917
|
+
current = current.cause;
|
|
3918
|
+
depth++;
|
|
3919
|
+
}
|
|
3920
|
+
process.exit(1);
|
|
3921
|
+
}
|
|
3922
|
+
|
|
3923
|
+
// src/commands/auction.ts
|
|
3924
|
+
var ETH_ADDRESS3 = "0x0000000000000000000000000000000000000000";
|
|
3925
|
+
function auctionCommand() {
|
|
3926
|
+
const cmd = new Command4("auction");
|
|
3927
|
+
cmd.description("Auction subcommands (create, bid, settle, cancel, status)");
|
|
3928
|
+
cmd.command("create").description("Configure and start an auction").requiredOption("--contract <address>", "NFT contract address").requiredOption("--token-id <id>", "token ID to auction").requiredOption("--starting-price <amount>", "starting price in ETH (or token units)").requiredOption("--duration <seconds>", "auction duration in seconds").option("--currency <currency>", "currency: eth, usdc, rare, or ERC20 address (defaults to eth)").option("--chain <chain>", "chain to use (mainnet, sepolia, base, base-sepolia)").action(async (opts) => {
|
|
3929
|
+
const chain = getActiveChain(opts.chain);
|
|
3930
|
+
const { client } = getWalletClient(chain);
|
|
3931
|
+
const publicClient = getPublicClient(chain);
|
|
3932
|
+
const rare = createRareClient({ publicClient, walletClient: client });
|
|
3933
|
+
const currency = opts.currency ? resolveCurrency(opts.currency, chain) : ETH_ADDRESS3;
|
|
3934
|
+
console.log(`Creating auction on ${chain}...`);
|
|
3935
|
+
console.log(` Auction contract: ${rare.contracts.auction}`);
|
|
3936
|
+
console.log(` NFT contract: ${opts.contract}`);
|
|
3937
|
+
console.log(` Token ID: ${opts.tokenId}`);
|
|
3938
|
+
console.log(` Starting price: ${opts.startingPrice} ETH`);
|
|
3939
|
+
console.log(` Duration: ${opts.duration} seconds`);
|
|
3940
|
+
console.log(` Currency: ${currency === ETH_ADDRESS3 ? "ETH" : currency}`);
|
|
3941
|
+
try {
|
|
3942
|
+
const result = await rare.auction.create({
|
|
3943
|
+
contract: opts.contract,
|
|
3944
|
+
tokenId: opts.tokenId,
|
|
3945
|
+
startingPrice: opts.startingPrice,
|
|
3946
|
+
duration: opts.duration,
|
|
3947
|
+
currency
|
|
3948
|
+
});
|
|
3949
|
+
if (result.approvalTxHash) {
|
|
3950
|
+
console.log(`Approval tx sent: ${result.approvalTxHash}`);
|
|
3951
|
+
}
|
|
3952
|
+
console.log(`
|
|
3953
|
+
Transaction sent: ${result.txHash}`);
|
|
3954
|
+
console.log(`Auction created! Block: ${result.receipt.blockNumber}`);
|
|
3955
|
+
} catch (error) {
|
|
3956
|
+
printContractError(error);
|
|
3957
|
+
}
|
|
3958
|
+
});
|
|
3959
|
+
cmd.command("bid").description("Place a bid on an auction").requiredOption("--contract <address>", "NFT contract address").requiredOption("--token-id <id>", "token ID").requiredOption("--amount <amount>", "bid amount in ETH (or token units)").option("--currency <currency>", "currency: eth, usdc, rare, or ERC20 address (defaults to eth)").option("--chain <chain>", "chain to use (mainnet, sepolia, base, base-sepolia)").action(async (opts) => {
|
|
3960
|
+
const chain = getActiveChain(opts.chain);
|
|
3961
|
+
const { client } = getWalletClient(chain);
|
|
3962
|
+
const publicClient = getPublicClient(chain);
|
|
3963
|
+
const rare = createRareClient({ publicClient, walletClient: client });
|
|
3964
|
+
const currency = opts.currency ? resolveCurrency(opts.currency, chain) : ETH_ADDRESS3;
|
|
3965
|
+
const isEth = currency === ETH_ADDRESS3;
|
|
3175
3966
|
console.log(`Placing bid on ${chain}...`);
|
|
3176
|
-
console.log(` Auction contract: ${
|
|
3967
|
+
console.log(` Auction contract: ${rare.contracts.auction}`);
|
|
3177
3968
|
console.log(` NFT contract: ${opts.contract}`);
|
|
3178
3969
|
console.log(` Token ID: ${opts.tokenId}`);
|
|
3179
3970
|
console.log(` Amount: ${opts.amount} ${isEth ? "ETH" : currency}`);
|
|
3180
|
-
let txHash;
|
|
3181
3971
|
try {
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
account,
|
|
3188
|
-
chain: void 0,
|
|
3189
|
-
value: isEth ? bidAmount : 0n
|
|
3972
|
+
const result = await rare.auction.bid({
|
|
3973
|
+
contract: opts.contract,
|
|
3974
|
+
tokenId: opts.tokenId,
|
|
3975
|
+
amount: opts.amount,
|
|
3976
|
+
currency
|
|
3190
3977
|
});
|
|
3978
|
+
console.log(`
|
|
3979
|
+
Transaction sent: ${result.txHash}`);
|
|
3980
|
+
console.log(`Bid placed! Block: ${result.receipt.blockNumber}`);
|
|
3191
3981
|
} catch (error) {
|
|
3192
3982
|
printContractError(error);
|
|
3193
3983
|
}
|
|
3194
|
-
console.log(`
|
|
3195
|
-
Transaction sent: ${txHash}`);
|
|
3196
|
-
const receipt = await publicClient.waitForTransactionReceipt({ hash: txHash });
|
|
3197
|
-
console.log(`Bid placed! Block: ${receipt.blockNumber}`);
|
|
3198
3984
|
});
|
|
3199
3985
|
cmd.command("settle").description("Settle a completed auction").requiredOption("--contract <address>", "NFT contract address").requiredOption("--token-id <id>", "token ID").option("--chain <chain>", "chain to use (mainnet, sepolia, base, base-sepolia)").action(async (opts) => {
|
|
3200
3986
|
const chain = getActiveChain(opts.chain);
|
|
3201
|
-
const { client
|
|
3987
|
+
const { client } = getWalletClient(chain);
|
|
3202
3988
|
const publicClient = getPublicClient(chain);
|
|
3203
|
-
const
|
|
3989
|
+
const rare = createRareClient({ publicClient, walletClient: client });
|
|
3204
3990
|
console.log(`Settling auction on ${chain}...`);
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
})
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
console.log(`Auction settled! Block: ${receipt.blockNumber}`);
|
|
3991
|
+
try {
|
|
3992
|
+
const result = await rare.auction.settle({
|
|
3993
|
+
contract: opts.contract,
|
|
3994
|
+
tokenId: opts.tokenId
|
|
3995
|
+
});
|
|
3996
|
+
console.log(`Transaction sent: ${result.txHash}`);
|
|
3997
|
+
console.log(`Auction settled! Block: ${result.receipt.blockNumber}`);
|
|
3998
|
+
} catch (error) {
|
|
3999
|
+
printContractError(error);
|
|
4000
|
+
}
|
|
3216
4001
|
});
|
|
3217
4002
|
cmd.command("cancel").description("Cancel an auction").requiredOption("--contract <address>", "NFT contract address").requiredOption("--token-id <id>", "token ID").option("--chain <chain>", "chain to use (mainnet, sepolia, base, base-sepolia)").action(async (opts) => {
|
|
3218
4003
|
const chain = getActiveChain(opts.chain);
|
|
3219
|
-
const { client
|
|
4004
|
+
const { client } = getWalletClient(chain);
|
|
3220
4005
|
const publicClient = getPublicClient(chain);
|
|
3221
|
-
const
|
|
4006
|
+
const rare = createRareClient({ publicClient, walletClient: client });
|
|
3222
4007
|
console.log(`Cancelling auction on ${chain}...`);
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
})
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
console.log(`Auction cancelled! Block: ${receipt.blockNumber}`);
|
|
4008
|
+
try {
|
|
4009
|
+
const result = await rare.auction.cancel({
|
|
4010
|
+
contract: opts.contract,
|
|
4011
|
+
tokenId: opts.tokenId
|
|
4012
|
+
});
|
|
4013
|
+
console.log(`Transaction sent: ${result.txHash}`);
|
|
4014
|
+
console.log(`Auction cancelled! Block: ${result.receipt.blockNumber}`);
|
|
4015
|
+
} catch (error) {
|
|
4016
|
+
printContractError(error);
|
|
4017
|
+
}
|
|
3234
4018
|
});
|
|
3235
4019
|
cmd.command("status").description("Get auction details (read-only)").requiredOption("--contract <address>", "NFT contract address").requiredOption("--token-id <id>", "token ID").option("--chain <chain>", "chain to use (mainnet, sepolia, base, base-sepolia)").action(async (opts) => {
|
|
3236
4020
|
const chain = getActiveChain(opts.chain);
|
|
3237
4021
|
const publicClient = getPublicClient(chain);
|
|
3238
|
-
const
|
|
3239
|
-
const result = await
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
functionName: "getAuctionDetails",
|
|
3243
|
-
args: [opts.contract, BigInt(opts.tokenId)]
|
|
4022
|
+
const rare = createRareClient({ publicClient });
|
|
4023
|
+
const result = await rare.auction.getStatus({
|
|
4024
|
+
contract: opts.contract,
|
|
4025
|
+
tokenId: opts.tokenId
|
|
3244
4026
|
});
|
|
3245
|
-
const
|
|
3246
|
-
const isEth = currency === ETH_ADDRESS;
|
|
3247
|
-
const started = Number(startingTime) > 0;
|
|
3248
|
-
const endTime = started ? Number(startingTime) + Number(lengthOfAuction) : null;
|
|
3249
|
-
const endDate = endTime ? new Date(endTime * 1e3) : null;
|
|
4027
|
+
const endDate = result.endTime ? new Date(Number(result.endTime) * 1e3) : null;
|
|
3250
4028
|
console.log("\nAuction Details:");
|
|
3251
|
-
console.log(` Seller: ${seller}`);
|
|
3252
|
-
console.log(` Minimum bid: ${formatEther(minimumBid)} ${isEth ? "ETH" : currency}`);
|
|
3253
|
-
console.log(` Currency: ${isEth ? "ETH" : currency}`);
|
|
3254
|
-
console.log(` Duration: ${lengthOfAuction}s`);
|
|
3255
|
-
console.log(` Status: ${
|
|
3256
|
-
if (started) {
|
|
3257
|
-
console.log(` Started at: ${new Date(Number(startingTime) * 1e3).toISOString()}`);
|
|
4029
|
+
console.log(` Seller: ${result.seller}`);
|
|
4030
|
+
console.log(` Minimum bid: ${formatEther(result.minimumBid)} ${result.isEth ? "ETH" : result.currency}`);
|
|
4031
|
+
console.log(` Currency: ${result.isEth ? "ETH" : result.currency}`);
|
|
4032
|
+
console.log(` Duration: ${result.lengthOfAuction}s`);
|
|
4033
|
+
console.log(` Status: ${result.status}`);
|
|
4034
|
+
if (result.started) {
|
|
4035
|
+
console.log(` Started at: ${new Date(Number(result.startingTime) * 1e3).toISOString()}`);
|
|
3258
4036
|
console.log(` Ends at: ${endDate.toISOString()}`);
|
|
3259
4037
|
}
|
|
3260
|
-
console.log(` Creation block: ${creationBlock}`);
|
|
3261
|
-
console.log(` Auction type: ${auctionType}`);
|
|
4038
|
+
console.log(` Creation block: ${result.creationBlock}`);
|
|
4039
|
+
console.log(` Auction type: ${result.auctionType}`);
|
|
3262
4040
|
});
|
|
3263
4041
|
return cmd;
|
|
3264
4042
|
}
|
|
@@ -3271,51 +4049,25 @@ function statusCommand() {
|
|
|
3271
4049
|
cmd.requiredOption("--contract <address>", "token contract address").option("--token-id <id>", "token ID to query (optional)").option("--chain <chain>", "chain to use (mainnet, sepolia, base, base-sepolia)").action(async (opts) => {
|
|
3272
4050
|
const chain = getActiveChain(opts.chain);
|
|
3273
4051
|
const publicClient = getPublicClient(chain);
|
|
4052
|
+
const rare = createRareClient({ publicClient });
|
|
3274
4053
|
const contractAddress = opts.contract;
|
|
3275
|
-
const
|
|
3276
|
-
publicClient.readContract({
|
|
3277
|
-
address: contractAddress,
|
|
3278
|
-
abi: tokenAbi,
|
|
3279
|
-
functionName: "name"
|
|
3280
|
-
}),
|
|
3281
|
-
publicClient.readContract({
|
|
3282
|
-
address: contractAddress,
|
|
3283
|
-
abi: tokenAbi,
|
|
3284
|
-
functionName: "symbol"
|
|
3285
|
-
}),
|
|
3286
|
-
publicClient.readContract({
|
|
3287
|
-
address: contractAddress,
|
|
3288
|
-
abi: tokenAbi,
|
|
3289
|
-
functionName: "totalSupply"
|
|
3290
|
-
})
|
|
3291
|
-
]);
|
|
4054
|
+
const contractInfo = await rare.token.getContractInfo({ contract: contractAddress });
|
|
3292
4055
|
console.log("\nContract Info:");
|
|
3293
|
-
console.log(` Address: ${
|
|
3294
|
-
console.log(` Chain: ${chain}`);
|
|
3295
|
-
console.log(` Name: ${name}`);
|
|
3296
|
-
console.log(` Symbol: ${symbol}`);
|
|
3297
|
-
console.log(` Total Supply: ${totalSupply}`);
|
|
4056
|
+
console.log(` Address: ${contractInfo.contract}`);
|
|
4057
|
+
console.log(` Chain: ${contractInfo.chain}`);
|
|
4058
|
+
console.log(` Name: ${contractInfo.name}`);
|
|
4059
|
+
console.log(` Symbol: ${contractInfo.symbol}`);
|
|
4060
|
+
console.log(` Total Supply: ${contractInfo.totalSupply}`);
|
|
3298
4061
|
if (opts.tokenId !== void 0) {
|
|
3299
|
-
const tokenId = BigInt(opts.tokenId);
|
|
3300
4062
|
try {
|
|
3301
|
-
const
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
functionName: "ownerOf",
|
|
3306
|
-
args: [tokenId]
|
|
3307
|
-
}),
|
|
3308
|
-
publicClient.readContract({
|
|
3309
|
-
address: contractAddress,
|
|
3310
|
-
abi: tokenAbi,
|
|
3311
|
-
functionName: "tokenURI",
|
|
3312
|
-
args: [tokenId]
|
|
3313
|
-
})
|
|
3314
|
-
]);
|
|
4063
|
+
const tokenInfo = await rare.token.getTokenInfo({
|
|
4064
|
+
contract: contractAddress,
|
|
4065
|
+
tokenId: opts.tokenId
|
|
4066
|
+
});
|
|
3315
4067
|
console.log(`
|
|
3316
|
-
Token #${
|
|
3317
|
-
console.log(` Owner: ${owner}`);
|
|
3318
|
-
console.log(` URI: ${
|
|
4068
|
+
Token #${tokenInfo.tokenId}:`);
|
|
4069
|
+
console.log(` Owner: ${tokenInfo.owner}`);
|
|
4070
|
+
console.log(` URI: ${tokenInfo.tokenUri}`);
|
|
3319
4071
|
} catch (err) {
|
|
3320
4072
|
console.log(`
|
|
3321
4073
|
Token #${opts.tokenId}: not found or error reading token`);
|
|
@@ -3369,9 +4121,9 @@ Private key saved to config for chain: ${chain}`);
|
|
|
3369
4121
|
import { Command as Command7 } from "commander";
|
|
3370
4122
|
|
|
3371
4123
|
// src/search.ts
|
|
3372
|
-
var
|
|
3373
|
-
async function
|
|
3374
|
-
const url = `${
|
|
4124
|
+
var API_BASE_URL3 = "https://api.superrare.org";
|
|
4125
|
+
async function searchPost2(path2, payload) {
|
|
4126
|
+
const url = `${API_BASE_URL3}${path2}`;
|
|
3375
4127
|
const response = await fetch(url, {
|
|
3376
4128
|
method: "POST",
|
|
3377
4129
|
headers: { "Content-Type": "application/json" },
|
|
@@ -3385,8 +4137,8 @@ async function searchPost(path2, payload) {
|
|
|
3385
4137
|
}
|
|
3386
4138
|
return json;
|
|
3387
4139
|
}
|
|
3388
|
-
async function
|
|
3389
|
-
return
|
|
4140
|
+
async function searchNfts2(params) {
|
|
4141
|
+
return searchPost2("/api/search/nfts", {
|
|
3390
4142
|
query: params.query ?? "",
|
|
3391
4143
|
take: params.take ?? 24,
|
|
3392
4144
|
cursor: params.cursor ?? 0,
|
|
@@ -3399,8 +4151,8 @@ async function searchNfts(params) {
|
|
|
3399
4151
|
...params.chainIds ? { chainIds: params.chainIds } : {}
|
|
3400
4152
|
});
|
|
3401
4153
|
}
|
|
3402
|
-
async function
|
|
3403
|
-
return
|
|
4154
|
+
async function searchCollections2(params) {
|
|
4155
|
+
return searchPost2("/api/search/collections", {
|
|
3404
4156
|
query: params.query ?? "",
|
|
3405
4157
|
take: params.take ?? 24,
|
|
3406
4158
|
cursor: params.cursor ?? 0,
|
|
@@ -3449,7 +4201,7 @@ function searchCommand() {
|
|
|
3449
4201
|
const ownerAddresses = opts.mine ? [getWalletAddress(chain)] : opts.owner ? [opts.owner] : [];
|
|
3450
4202
|
const label = opts.mine ? `NFTs owned by ${ownerAddresses[0]}` : opts.owner ? `NFTs owned by ${opts.owner}` : "NFTs";
|
|
3451
4203
|
console.log(`Searching ${label} on ${chain}...`);
|
|
3452
|
-
const page = await
|
|
4204
|
+
const page = await searchNfts2({
|
|
3453
4205
|
query: opts.query,
|
|
3454
4206
|
take: parseInt(opts.take, 10),
|
|
3455
4207
|
cursor: parseInt(opts.cursor, 10),
|
|
@@ -3461,7 +4213,7 @@ function searchCommand() {
|
|
|
3461
4213
|
cmd.command("auctions").description("List NFTs with active or configured auctions").option("--chain <chain>", "chain to use (mainnet, sepolia, base, base-sepolia)").option("--state <states...>", "auction states to filter (PENDING, RUNNING, SETTLED, UNSETTLED)", ["PENDING", "RUNNING"]).option("--owner <address>", "filter by owner address (optional)").option("--query <text>", "text search query", "").option("--take <n>", "number of results per page", "24").option("--cursor <n>", "pagination cursor", "0").action(async (opts) => {
|
|
3462
4214
|
const chain = getActiveChain(opts.chain);
|
|
3463
4215
|
console.log(`Searching auctions (${opts.state.join(", ")}) on ${chain}...`);
|
|
3464
|
-
const page = await
|
|
4216
|
+
const page = await searchNfts2({
|
|
3465
4217
|
query: opts.query,
|
|
3466
4218
|
take: parseInt(opts.take, 10),
|
|
3467
4219
|
cursor: parseInt(opts.cursor, 10),
|
|
@@ -3475,7 +4227,7 @@ function searchCommand() {
|
|
|
3475
4227
|
const chain = getActiveChain(opts.chain);
|
|
3476
4228
|
const address = getWalletAddress(chain);
|
|
3477
4229
|
console.log(`Searching collections owned by ${address}...`);
|
|
3478
|
-
const page = await
|
|
4230
|
+
const page = await searchCollections2({
|
|
3479
4231
|
query: opts.query,
|
|
3480
4232
|
take: parseInt(opts.take, 10),
|
|
3481
4233
|
cursor: parseInt(opts.cursor, 10),
|
|
@@ -3503,7 +4255,7 @@ function listCollectionsCommand() {
|
|
|
3503
4255
|
let cursor = 0;
|
|
3504
4256
|
let hasMore = true;
|
|
3505
4257
|
while (hasMore) {
|
|
3506
|
-
const page = await
|
|
4258
|
+
const page = await searchCollections2({
|
|
3507
4259
|
query: opts.query,
|
|
3508
4260
|
take: 100,
|
|
3509
4261
|
cursor,
|
|
@@ -3546,7 +4298,7 @@ function importCommand() {
|
|
|
3546
4298
|
console.log(` Chain: ${chain} (${chainId})`);
|
|
3547
4299
|
console.log(` Contract: ${contractAddress}`);
|
|
3548
4300
|
console.log(` Owner: ${ownerAddress}`);
|
|
3549
|
-
await
|
|
4301
|
+
await importErc7212({
|
|
3550
4302
|
chainId,
|
|
3551
4303
|
contractAddress,
|
|
3552
4304
|
ownerAddress
|
|
@@ -3557,9 +4309,236 @@ Contract imported successfully.`);
|
|
|
3557
4309
|
return cmd;
|
|
3558
4310
|
}
|
|
3559
4311
|
|
|
4312
|
+
// src/commands/offer.ts
|
|
4313
|
+
import { Command as Command10 } from "commander";
|
|
4314
|
+
import { formatEther as formatEther2 } from "viem";
|
|
4315
|
+
var ETH_ADDRESS4 = "0x0000000000000000000000000000000000000000";
|
|
4316
|
+
function offerCommand() {
|
|
4317
|
+
const cmd = new Command10("offer");
|
|
4318
|
+
cmd.description("Offer subcommands (create, cancel, accept, status)");
|
|
4319
|
+
cmd.command("create").description("Create an offer on a token").requiredOption("--contract <address>", "NFT contract address").requiredOption("--token-id <id>", "token ID").requiredOption("--amount <amount>", "offer amount in ETH (or token units)").option("--currency <currency>", "currency: eth, usdc, rare, or ERC20 address (defaults to eth)").option("--convertible", "mark offer as convertible").option("--chain <chain>", "chain to use (mainnet, sepolia, base, base-sepolia)").action(async (opts) => {
|
|
4320
|
+
const chain = getActiveChain(opts.chain);
|
|
4321
|
+
const { client } = getWalletClient(chain);
|
|
4322
|
+
const publicClient = getPublicClient(chain);
|
|
4323
|
+
const rare = createRareClient({ publicClient, walletClient: client });
|
|
4324
|
+
const currency = opts.currency ? resolveCurrency(opts.currency, chain) : ETH_ADDRESS4;
|
|
4325
|
+
const isEth = currency === ETH_ADDRESS4;
|
|
4326
|
+
console.log(`Creating offer on ${chain}...`);
|
|
4327
|
+
console.log(` Marketplace contract: ${rare.contracts.auction}`);
|
|
4328
|
+
console.log(` NFT contract: ${opts.contract}`);
|
|
4329
|
+
console.log(` Token ID: ${opts.tokenId}`);
|
|
4330
|
+
console.log(` Amount: ${opts.amount} ${isEth ? "ETH" : currency}`);
|
|
4331
|
+
console.log(` Convertible: ${opts.convertible ? "yes" : "no"}`);
|
|
4332
|
+
try {
|
|
4333
|
+
const result = await rare.offer.create({
|
|
4334
|
+
contract: opts.contract,
|
|
4335
|
+
tokenId: opts.tokenId,
|
|
4336
|
+
amount: opts.amount,
|
|
4337
|
+
currency,
|
|
4338
|
+
convertible: opts.convertible ?? false
|
|
4339
|
+
});
|
|
4340
|
+
console.log(`
|
|
4341
|
+
Transaction sent: ${result.txHash}`);
|
|
4342
|
+
console.log(`Offer created! Block: ${result.receipt.blockNumber}`);
|
|
4343
|
+
} catch (error) {
|
|
4344
|
+
printContractError(error);
|
|
4345
|
+
}
|
|
4346
|
+
});
|
|
4347
|
+
cmd.command("cancel").description("Cancel an existing offer").requiredOption("--contract <address>", "NFT contract address").requiredOption("--token-id <id>", "token ID").option("--currency <currency>", "currency: eth, usdc, rare, or ERC20 address (defaults to eth)").option("--chain <chain>", "chain to use (mainnet, sepolia, base, base-sepolia)").action(async (opts) => {
|
|
4348
|
+
const chain = getActiveChain(opts.chain);
|
|
4349
|
+
const { client } = getWalletClient(chain);
|
|
4350
|
+
const publicClient = getPublicClient(chain);
|
|
4351
|
+
const rare = createRareClient({ publicClient, walletClient: client });
|
|
4352
|
+
const currency = opts.currency ? resolveCurrency(opts.currency, chain) : ETH_ADDRESS4;
|
|
4353
|
+
console.log(`Cancelling offer on ${chain}...`);
|
|
4354
|
+
try {
|
|
4355
|
+
const result = await rare.offer.cancel({
|
|
4356
|
+
contract: opts.contract,
|
|
4357
|
+
tokenId: opts.tokenId,
|
|
4358
|
+
currency
|
|
4359
|
+
});
|
|
4360
|
+
console.log(`Transaction sent: ${result.txHash}`);
|
|
4361
|
+
console.log(`Offer cancelled! Block: ${result.receipt.blockNumber}`);
|
|
4362
|
+
} catch (error) {
|
|
4363
|
+
printContractError(error);
|
|
4364
|
+
}
|
|
4365
|
+
});
|
|
4366
|
+
cmd.command("accept").description("Accept an offer on a token you own").requiredOption("--contract <address>", "NFT contract address").requiredOption("--token-id <id>", "token ID").requiredOption("--amount <amount>", "offer amount to accept in ETH (or token units)").option("--currency <currency>", "currency: eth, usdc, rare, or ERC20 address (defaults to eth)").option("--chain <chain>", "chain to use (mainnet, sepolia, base, base-sepolia)").action(async (opts) => {
|
|
4367
|
+
const chain = getActiveChain(opts.chain);
|
|
4368
|
+
const { client } = getWalletClient(chain);
|
|
4369
|
+
const publicClient = getPublicClient(chain);
|
|
4370
|
+
const rare = createRareClient({ publicClient, walletClient: client });
|
|
4371
|
+
const currency = opts.currency ? resolveCurrency(opts.currency, chain) : ETH_ADDRESS4;
|
|
4372
|
+
const isEth = currency === ETH_ADDRESS4;
|
|
4373
|
+
console.log(`Accepting offer on ${chain}...`);
|
|
4374
|
+
console.log(` NFT contract: ${opts.contract}`);
|
|
4375
|
+
console.log(` Token ID: ${opts.tokenId}`);
|
|
4376
|
+
console.log(` Amount: ${opts.amount} ${isEth ? "ETH" : currency}`);
|
|
4377
|
+
try {
|
|
4378
|
+
const result = await rare.offer.accept({
|
|
4379
|
+
contract: opts.contract,
|
|
4380
|
+
tokenId: opts.tokenId,
|
|
4381
|
+
amount: opts.amount,
|
|
4382
|
+
currency
|
|
4383
|
+
});
|
|
4384
|
+
console.log(`
|
|
4385
|
+
Transaction sent: ${result.txHash}`);
|
|
4386
|
+
console.log(`Offer accepted! Block: ${result.receipt.blockNumber}`);
|
|
4387
|
+
} catch (error) {
|
|
4388
|
+
printContractError(error);
|
|
4389
|
+
}
|
|
4390
|
+
});
|
|
4391
|
+
cmd.command("status").description("Get current offer details (read-only)").requiredOption("--contract <address>", "NFT contract address").requiredOption("--token-id <id>", "token ID").option("--currency <currency>", "currency: eth, usdc, rare, or ERC20 address (defaults to eth)").option("--chain <chain>", "chain to use (mainnet, sepolia, base, base-sepolia)").action(async (opts) => {
|
|
4392
|
+
const chain = getActiveChain(opts.chain);
|
|
4393
|
+
const publicClient = getPublicClient(chain);
|
|
4394
|
+
const rare = createRareClient({ publicClient });
|
|
4395
|
+
const currency = opts.currency ? resolveCurrency(opts.currency, chain) : ETH_ADDRESS4;
|
|
4396
|
+
const isEth = currency === ETH_ADDRESS4;
|
|
4397
|
+
const result = await rare.offer.getStatus({
|
|
4398
|
+
contract: opts.contract,
|
|
4399
|
+
tokenId: opts.tokenId,
|
|
4400
|
+
currency
|
|
4401
|
+
});
|
|
4402
|
+
console.log("\nOffer Details:");
|
|
4403
|
+
if (!result.hasOffer) {
|
|
4404
|
+
console.log(" No active offer found.");
|
|
4405
|
+
} else {
|
|
4406
|
+
console.log(` Buyer: ${result.buyer}`);
|
|
4407
|
+
console.log(` Amount: ${formatEther2(result.amount)} ${isEth ? "ETH" : currency}`);
|
|
4408
|
+
console.log(` Timestamp: ${new Date(Number(result.timestamp) * 1e3).toISOString()}`);
|
|
4409
|
+
console.log(` Marketplace fee: ${result.marketplaceFee}%`);
|
|
4410
|
+
console.log(` Convertible: ${result.convertible ? "yes" : "no"}`);
|
|
4411
|
+
}
|
|
4412
|
+
});
|
|
4413
|
+
return cmd;
|
|
4414
|
+
}
|
|
4415
|
+
|
|
4416
|
+
// src/commands/listing.ts
|
|
4417
|
+
import { Command as Command11 } from "commander";
|
|
4418
|
+
import { formatEther as formatEther3 } from "viem";
|
|
4419
|
+
var ETH_ADDRESS5 = "0x0000000000000000000000000000000000000000";
|
|
4420
|
+
function listingCommand() {
|
|
4421
|
+
const cmd = new Command11("listing");
|
|
4422
|
+
cmd.description("Listing subcommands (create, cancel, buy, status)");
|
|
4423
|
+
cmd.command("create").description("Create a listing (set sale price) for a token").requiredOption("--contract <address>", "NFT contract address").requiredOption("--token-id <id>", "token ID").requiredOption("--price <amount>", "listing price in ETH (or token units)").option("--currency <currency>", "currency: eth, usdc, rare, or ERC20 address (defaults to eth)").option("--target <address>", "target buyer address (defaults to public listing)").option("--chain <chain>", "chain to use (mainnet, sepolia, base, base-sepolia)").action(async (opts) => {
|
|
4424
|
+
const chain = getActiveChain(opts.chain);
|
|
4425
|
+
const { client } = getWalletClient(chain);
|
|
4426
|
+
const publicClient = getPublicClient(chain);
|
|
4427
|
+
const rare = createRareClient({ publicClient, walletClient: client });
|
|
4428
|
+
const currency = opts.currency ? resolveCurrency(opts.currency, chain) : ETH_ADDRESS5;
|
|
4429
|
+
const isEth = currency === ETH_ADDRESS5;
|
|
4430
|
+
const target = opts.target ?? ETH_ADDRESS5;
|
|
4431
|
+
console.log(`Creating listing on ${chain}...`);
|
|
4432
|
+
console.log(` Marketplace contract: ${rare.contracts.auction}`);
|
|
4433
|
+
console.log(` NFT contract: ${opts.contract}`);
|
|
4434
|
+
console.log(` Token ID: ${opts.tokenId}`);
|
|
4435
|
+
console.log(` Price: ${opts.price} ${isEth ? "ETH" : currency}`);
|
|
4436
|
+
console.log(` Target: ${target === ETH_ADDRESS5 ? "public" : target}`);
|
|
4437
|
+
try {
|
|
4438
|
+
const result = await rare.listing.create({
|
|
4439
|
+
contract: opts.contract,
|
|
4440
|
+
tokenId: opts.tokenId,
|
|
4441
|
+
price: opts.price,
|
|
4442
|
+
currency,
|
|
4443
|
+
target
|
|
4444
|
+
});
|
|
4445
|
+
if (result.approvalTxHash) {
|
|
4446
|
+
console.log(`Approval tx sent: ${result.approvalTxHash}`);
|
|
4447
|
+
}
|
|
4448
|
+
console.log(`
|
|
4449
|
+
Transaction sent: ${result.txHash}`);
|
|
4450
|
+
console.log(`Listing created! Block: ${result.receipt.blockNumber}`);
|
|
4451
|
+
} catch (error) {
|
|
4452
|
+
printContractError(error);
|
|
4453
|
+
}
|
|
4454
|
+
});
|
|
4455
|
+
cmd.command("cancel").description("Cancel a listing (remove sale price)").requiredOption("--contract <address>", "NFT contract address").requiredOption("--token-id <id>", "token ID").option("--target <address>", "target buyer address (defaults to public listing)").option("--chain <chain>", "chain to use (mainnet, sepolia, base, base-sepolia)").action(async (opts) => {
|
|
4456
|
+
const chain = getActiveChain(opts.chain);
|
|
4457
|
+
const { client } = getWalletClient(chain);
|
|
4458
|
+
const publicClient = getPublicClient(chain);
|
|
4459
|
+
const rare = createRareClient({ publicClient, walletClient: client });
|
|
4460
|
+
const target = opts.target ?? ETH_ADDRESS5;
|
|
4461
|
+
console.log(`Cancelling listing on ${chain}...`);
|
|
4462
|
+
try {
|
|
4463
|
+
const result = await rare.listing.cancel({
|
|
4464
|
+
contract: opts.contract,
|
|
4465
|
+
tokenId: opts.tokenId,
|
|
4466
|
+
target
|
|
4467
|
+
});
|
|
4468
|
+
console.log(`Transaction sent: ${result.txHash}`);
|
|
4469
|
+
console.log(`Listing cancelled! Block: ${result.receipt.blockNumber}`);
|
|
4470
|
+
} catch (error) {
|
|
4471
|
+
printContractError(error);
|
|
4472
|
+
}
|
|
4473
|
+
});
|
|
4474
|
+
cmd.command("buy").description("Buy a listed token").requiredOption("--contract <address>", "NFT contract address").requiredOption("--token-id <id>", "token ID").requiredOption("--amount <amount>", "purchase amount in ETH (or token units)").option("--currency <currency>", "currency: eth, usdc, rare, or ERC20 address (defaults to eth)").option("--chain <chain>", "chain to use (mainnet, sepolia, base, base-sepolia)").action(async (opts) => {
|
|
4475
|
+
const chain = getActiveChain(opts.chain);
|
|
4476
|
+
const { client } = getWalletClient(chain);
|
|
4477
|
+
const publicClient = getPublicClient(chain);
|
|
4478
|
+
const rare = createRareClient({ publicClient, walletClient: client });
|
|
4479
|
+
const currency = opts.currency ? resolveCurrency(opts.currency, chain) : ETH_ADDRESS5;
|
|
4480
|
+
const isEth = currency === ETH_ADDRESS5;
|
|
4481
|
+
console.log(`Buying token on ${chain}...`);
|
|
4482
|
+
console.log(` Marketplace contract: ${rare.contracts.auction}`);
|
|
4483
|
+
console.log(` NFT contract: ${opts.contract}`);
|
|
4484
|
+
console.log(` Token ID: ${opts.tokenId}`);
|
|
4485
|
+
console.log(` Amount: ${opts.amount} ${isEth ? "ETH" : currency}`);
|
|
4486
|
+
try {
|
|
4487
|
+
const result = await rare.listing.buy({
|
|
4488
|
+
contract: opts.contract,
|
|
4489
|
+
tokenId: opts.tokenId,
|
|
4490
|
+
amount: opts.amount,
|
|
4491
|
+
currency
|
|
4492
|
+
});
|
|
4493
|
+
console.log(`
|
|
4494
|
+
Transaction sent: ${result.txHash}`);
|
|
4495
|
+
console.log(`Token purchased! Block: ${result.receipt.blockNumber}`);
|
|
4496
|
+
} catch (error) {
|
|
4497
|
+
printContractError(error);
|
|
4498
|
+
}
|
|
4499
|
+
});
|
|
4500
|
+
cmd.command("status").description("Get listing details (read-only)").requiredOption("--contract <address>", "NFT contract address").requiredOption("--token-id <id>", "token ID").option("--target <address>", "target buyer address (defaults to public listing)").option("--chain <chain>", "chain to use (mainnet, sepolia, base, base-sepolia)").action(async (opts) => {
|
|
4501
|
+
const chain = getActiveChain(opts.chain);
|
|
4502
|
+
const publicClient = getPublicClient(chain);
|
|
4503
|
+
const rare = createRareClient({ publicClient });
|
|
4504
|
+
const target = opts.target ?? ETH_ADDRESS5;
|
|
4505
|
+
const result = await rare.listing.getStatus({
|
|
4506
|
+
contract: opts.contract,
|
|
4507
|
+
tokenId: opts.tokenId,
|
|
4508
|
+
target
|
|
4509
|
+
});
|
|
4510
|
+
console.log("\nListing Details:");
|
|
4511
|
+
if (!result.hasListing) {
|
|
4512
|
+
console.log(" No active listing found.");
|
|
4513
|
+
} else {
|
|
4514
|
+
console.log(` Seller: ${result.seller}`);
|
|
4515
|
+
console.log(` Amount: ${formatEther3(result.amount)} ${result.isEth ? "ETH" : result.currencyAddress}`);
|
|
4516
|
+
console.log(` Currency: ${result.isEth ? "ETH" : result.currencyAddress}`);
|
|
4517
|
+
}
|
|
4518
|
+
});
|
|
4519
|
+
return cmd;
|
|
4520
|
+
}
|
|
4521
|
+
|
|
4522
|
+
// src/commands/currencies.ts
|
|
4523
|
+
import { Command as Command12 } from "commander";
|
|
4524
|
+
function currenciesCommand() {
|
|
4525
|
+
const cmd = new Command12("currencies");
|
|
4526
|
+
cmd.description("List supported currencies and their addresses").option("--chain <chain>", "chain to use (mainnet, sepolia, base, base-sepolia)").action((opts) => {
|
|
4527
|
+
const chain = getActiveChain(opts.chain);
|
|
4528
|
+
console.log(`
|
|
4529
|
+
Supported currencies on ${chain}:
|
|
4530
|
+
`);
|
|
4531
|
+
for (const name of currencyNames) {
|
|
4532
|
+
const address = resolveCurrency(name, chain);
|
|
4533
|
+
console.log(` ${name.toUpperCase().padEnd(6)} ${address}`);
|
|
4534
|
+
}
|
|
4535
|
+
});
|
|
4536
|
+
return cmd;
|
|
4537
|
+
}
|
|
4538
|
+
|
|
3560
4539
|
// src/index.ts
|
|
3561
|
-
var program = new
|
|
3562
|
-
program.name("rare").description("CLI tool for interacting with the RARE protocol smart contracts").version("0.
|
|
4540
|
+
var program = new Command13();
|
|
4541
|
+
program.name("rare").description("CLI tool for interacting with the RARE protocol smart contracts").version("0.4.1");
|
|
3563
4542
|
program.addCommand(configureCommand());
|
|
3564
4543
|
program.addCommand(deployCommand());
|
|
3565
4544
|
program.addCommand(mintCommand());
|
|
@@ -3569,6 +4548,9 @@ program.addCommand(walletCommand());
|
|
|
3569
4548
|
program.addCommand(searchCommand());
|
|
3570
4549
|
program.addCommand(listCollectionsCommand());
|
|
3571
4550
|
program.addCommand(importCommand());
|
|
4551
|
+
program.addCommand(offerCommand());
|
|
4552
|
+
program.addCommand(listingCommand());
|
|
4553
|
+
program.addCommand(currenciesCommand());
|
|
3572
4554
|
program.parseAsync(process.argv).catch((err) => {
|
|
3573
4555
|
console.error("Error:", err.message ?? err);
|
|
3574
4556
|
process.exit(1);
|