@boxfish-studio/candymachine-client-sdk
Version:
Metaplex Candy Machine Client SDK
1,295 lines (1,281 loc) • 48.1 kB
JavaScript
var __defProp = Object.defineProperty;
var __defProps = Object.defineProperties;
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
var __async = (__this, __arguments, generator) => {
return new Promise((resolve, reject) => {
var fulfilled = (value) => {
try {
step(generator.next(value));
} catch (e) {
reject(e);
}
};
var rejected = (value) => {
try {
step(generator.throw(value));
} catch (e) {
reject(e);
}
};
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
step((generator = generator.apply(__this, __arguments)).next());
});
};
// src/update.ts
import { PublicKey } from "@solana/web3.js";
// src/cache/actions.ts
import fileDownloader from "js-file-download";
function saveCache(cacheName, env, cacheContent) {
cacheContent.env = env;
cacheContent.cacheName = cacheName;
fileDownloader(JSON.stringify(cacheContent), cacheName);
}
// src/update.ts
function updateV2(_0) {
return __async(this, arguments, function* ({
newSettings,
candyMachinePubkey,
publicKey,
treasuryWallet,
anchorProgram,
cache,
newAuthority
}) {
try {
const cacheContent = JSON.parse(cache);
const env = cacheContent.env;
const cacheName = cacheContent.cacheName;
const tx = yield anchorProgram.methods.updateCandyMachine(newSettings).accounts({
candyMachine: new PublicKey(candyMachinePubkey),
authority: publicKey,
wallet: treasuryWallet
}).signers([]).rpc();
cacheContent.startDate = newSettings.goLiveDate;
console.log("update_candy_machine finished", tx);
if (newAuthority) {
const tx2 = yield anchorProgram.methods.updateAuthority(new PublicKey(newAuthority)).accounts({
candyMachine: new PublicKey(candyMachinePubkey),
authority: publicKey,
wallet: treasuryWallet
}).rpc();
cacheContent.authority = new PublicKey(newAuthority).toBase58();
console.log(` - updated authority: ${cacheContent.authority}`);
console.log("update_authority finished", tx2);
}
saveCache(cacheName, env, cacheContent);
} catch (err) {
console.error(err);
throw new Error();
}
});
}
// src/upload/arweave.ts
import { calculate } from "@metaplex/arweave-cost";
import * as anchor2 from "@project-serum/anchor";
// src/constants.ts
import { PublicKey as PublicKey2 } from "@solana/web3.js";
var SUPPORTED_IMAGE_TYPES = ["image/png", "image/gif", "image/jpeg"];
var SUPPORTED_ANIMATION_TYPES = [
"video/mp4",
"video/quicktime",
"audio/mpeg",
"audio/x-flac",
"audio/wav",
"model/gltf-binary",
"text/html"
];
var DEFAULT_GATEKEEPER = {
gatekeeperNetwork: "ignREusXmGrscGNUesoU9mxfds9AiYTezUKex2PsZV6",
expireOnUse: true
};
var JSON_EXTENSION = "application/json";
var DEFAULT_TIMEOUT = 3e4;
var NOTIFICATION_TIMEOUT_NEVER = -1;
var NOTIFICATION_TIMEOUT_DEFAULT = 5e3;
var CANDY_MACHINE = "candy_machine";
var AUCTION_HOUSE = "auction_house";
var TOKEN_ENTANGLER = "token_entangler";
var ESCROW = "escrow";
var A = "A";
var B = "B";
var FEE_PAYER = "fee_payer";
var TREASURY = "treasury";
var MAX_NAME_LENGTH = 32;
var MAX_URI_LENGTH = 200;
var MAX_SYMBOL_LENGTH = 10;
var MAX_CREATOR_LEN = 32 + 1 + 1;
var MAX_CREATOR_LIMIT = 5;
var ARWEAVE_PAYMENT_WALLET = new PublicKey2("6FKvsq4ydWFci6nGq9ckbjYMtnmaqAoatz5c9XWjiDuS");
var CANDY_MACHINE_PROGRAM_ID = new PublicKey2("cndyAnrLdpjq1Ssp1z8xxDsB8dxe7u4HL5Nxi2K5WXZ");
var CANDY_MACHINE_PROGRAM_V2_ID = new PublicKey2("cndy3Z4yapfJBmL3ShUp5exZKqR3z33thTzeNMm2gRZ");
var TOKEN_METADATA_PROGRAM_ID = new PublicKey2("metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s");
var SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID = new PublicKey2("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL");
var TOKEN_PROGRAM_ID = new PublicKey2("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
var FAIR_LAUNCH_PROGRAM_ID = new PublicKey2("faircnAB9k59Y4TXmLabBULeuTLgV7TkGMGNkjnA15j");
var AUCTION_HOUSE_PROGRAM_ID = new PublicKey2("hausS13jsjafwWwGqZTUQRmWyvyxn9EQpqMwV1PBBmk");
var TOKEN_ENTANGLEMENT_PROGRAM_ID = new PublicKey2("qntmGodpGkrM42mN68VCZHXnKqDCT8rdY23wFcXCLPd");
var WRAPPED_SOL_MINT = new PublicKey2("So11111111111111111111111111111111111111112");
var ARWEAVE_UPLOAD_ENDPOINT = "https://us-central1-metaplex-studios.cloudfunctions.net/uploadFile";
var CONFIG_ARRAY_START = 32 + 4 + 6 + 4 + 10 + 2 + 1 + 4 + 5 * 34 + 8 + 1 + 1 + 4;
var CONFIG_ARRAY_START_V2 = 8 + 32 + 32 + 33 + 4 + 6 + 8 + 8 + 9 + 10 + 4 + MAX_SYMBOL_LENGTH + 2 + 4 + MAX_CREATOR_LIMIT * MAX_CREATOR_LEN + 8 + 1 + 1 + 1 + 4 + MAX_NAME_LENGTH + 4 + MAX_URI_LENGTH + 32 + 4 + 8 + 1 + 1 + 1 + 9 + 32 + 1 + 32 + 1;
var CONFIG_LINE_SIZE_V2 = 4 + 32 + 4 + 200;
var CONFIG_LINE_SIZE = 4 + 32 + 4 + 200;
var CACHE_PATH = "./.cache";
// src/upload/helpers.ts
import * as anchor from "@project-serum/anchor";
import { Keypair, SystemProgram } from "@solana/web3.js";
function parseDate(date) {
if (date === "now") {
return Date.now() / 1e3;
}
return Date.parse(date) / 1e3;
}
function sleep(milliseconds) {
return new Promise((resolve) => setTimeout(resolve, milliseconds));
}
function uuidFromConfigPubkey(configAccount) {
return configAccount.toBase58().slice(0, 6);
}
var createCandyMachineV2 = function(anchorProgram, payerWallet, treasuryWallet, candyData) {
return __async(this, null, function* () {
const candyAccount = Keypair.generate();
candyData.uuid = uuidFromConfigPubkey(candyAccount.publicKey);
if (!candyData.symbol) {
throw new Error("Invalid config, there must be a symbol.");
}
if (!candyData.creators || candyData.creators.length === 0) {
throw new Error("Invalid config, there must be at least one creator.");
}
const totalShare = (candyData.creators || []).reduce((acc, curr) => acc + curr.share, 0);
if (totalShare !== 100) {
throw new Error("Invalid config, creators shares must add up to 100");
}
const remainingAccounts = [];
const cmCreation = {
candyMachine: candyAccount.publicKey,
uuid: candyData.uuid,
txId: yield anchorProgram.methods.initializeCandyMachine(candyData).accounts({
candyMachine: candyAccount.publicKey,
wallet: treasuryWallet,
authority: payerWallet.publicKey,
payer: payerWallet.publicKey,
systemProgram: SystemProgram.programId,
rent: anchor.web3.SYSVAR_RENT_PUBKEY
}).signers([candyAccount]).remainingAccounts(remainingAccounts).preInstructions([
yield createCandyMachineV2Account(anchorProgram, candyData, payerWallet.publicKey, candyAccount.publicKey)
]).rpc()
};
console.log("cmCreation", cmCreation);
return cmCreation;
});
};
function createCandyMachineV2Account(anchorProgram, candyData, payerWallet, candyAccount) {
return __async(this, null, function* () {
console.log("creating v2 account");
const size = CONFIG_ARRAY_START_V2 + 4 + candyData.itemsAvailable.toNumber() * CONFIG_LINE_SIZE_V2 + 8 + 2 * (Math.floor(candyData.itemsAvailable.toNumber() / 8) + 1);
const candyMachineAccount = anchor.web3.SystemProgram.createAccount({
fromPubkey: payerWallet,
newAccountPubkey: candyAccount,
space: size,
lamports: yield anchorProgram.provider.connection.getMinimumBalanceForRentExemption(size),
programId: CANDY_MACHINE_PROGRAM_V2_ID
});
console.log("account created", candyMachineAccount);
return candyMachineAccount;
});
}
var getUnixTs = () => new Date().getTime() / 1e3;
var getFileName = (fileName) => fileName.split(".")[0];
var getFileExtension = (fileName) => fileName.split(".")[1];
// src/upload/transactions.ts
import {
Transaction
} from "@solana/web3.js";
var sendTransactionWithRetryWithKeypair = (connection, wallet, instructions, commitment = "singleGossip", block, beforeSend) => __async(void 0, null, function* () {
const transaction = new Transaction();
instructions.forEach((instruction) => transaction.add(instruction));
transaction.recentBlockhash = (block || (yield connection.getLatestBlockhash(commitment))).blockhash;
transaction.feePayer = wallet.publicKey;
yield wallet.signTransaction(transaction);
if (beforeSend) {
beforeSend();
}
const { txid, slot } = yield sendSignedTransaction({
connection,
signedTransaction: transaction
});
return { txid, slot };
});
function sendSignedTransaction(_0) {
return __async(this, arguments, function* ({
signedTransaction,
connection,
timeout = DEFAULT_TIMEOUT
}) {
const rawTransaction = signedTransaction.serialize();
const startTime = getUnixTs();
let slot = 0;
let txid = yield connection.sendRawTransaction(rawTransaction, {
skipPreflight: true
});
console.log("Started awaiting confirmation for", txid);
let done = false;
(() => __async(this, null, function* () {
while (!done && getUnixTs() - startTime < timeout) {
connection.sendRawTransaction(rawTransaction, {
skipPreflight: true
});
yield sleep(500);
}
}))();
try {
const confirmation = yield awaitTransactionSignatureConfirmation(txid, timeout, connection, "confirmed", true);
if (!confirmation)
throw new Error("Timed out awaiting confirmation on transaction");
if (confirmation.err) {
console.error(confirmation.err);
throw new Error("Transaction failed: Custom instruction error");
}
slot = (confirmation == null ? void 0 : confirmation.slot) || 0;
} catch (err) {
console.error("Timeout Error caught", err);
if (err.timeout) {
throw new Error("Timed out awaiting confirmation on transaction");
}
let simulateResult = null;
try {
simulateResult = (yield simulateTransaction(connection, signedTransaction, "single")).value;
} catch (e) {
console.error("Simulate Transaction error", e);
}
if (simulateResult && simulateResult.err) {
if (simulateResult.logs) {
for (let i = simulateResult.logs.length - 1; i >= 0; --i) {
const line = simulateResult.logs[i];
if (line.startsWith("Program log: ")) {
throw new Error("Transaction failed: " + line.slice("Program log: ".length));
}
}
}
throw new Error(JSON.stringify(simulateResult.err));
}
console.error("Got this far.");
txid = simulateResult == null ? void 0 : simulateResult.err;
} finally {
done = true;
}
console.log("Latency (ms)", txid, getUnixTs() - startTime);
return { txid, slot };
});
}
function simulateTransaction(connection, transaction, commitment) {
return __async(this, null, function* () {
transaction.recentBlockhash = (yield connection.getLatestBlockhash()).blockhash;
const signData = transaction.serializeMessage();
const encodedTransaction = signData.toString("base64");
const config = { encoding: "base64", commitment };
const args = [encodedTransaction, config];
const res = yield connection._rpcRequest("simulateTransaction", args);
if (res.error) {
throw new Error("failed to simulate transaction: " + res.error.message);
}
return res.result;
});
}
function awaitTransactionSignatureConfirmation(txid, timeout, connection, commitment = "recent", queryStatus = false) {
return __async(this, null, function* () {
let done = false;
let status = {
slot: 0,
confirmations: 0,
err: null
};
let subId = 0;
status = yield new Promise((resolve, reject) => __async(this, null, function* () {
setTimeout(() => {
if (done) {
return;
}
done = true;
console.warn("Rejecting for timeout...");
reject({ timeout: true });
}, timeout);
try {
subId = connection.onSignature(
txid,
(result, context) => {
done = true;
status = {
err: result.err,
slot: context.slot,
confirmations: 0
};
if (result.err) {
console.warn("Rejected via websocket", result.err);
reject(status);
} else {
console.log("Resolved via websocket", result);
resolve(status);
}
},
commitment
);
} catch (e) {
done = true;
console.error("WS error in setup", txid, e);
}
while (!done && queryStatus) {
;
(() => __async(this, null, function* () {
try {
const signatureStatuses = yield connection.getSignatureStatuses([txid]);
status = signatureStatuses && signatureStatuses.value[0];
if (!done) {
if (!status) {
console.log("REST null result for", txid, status);
} else if (status.err) {
console.error("REST error for", txid, status);
done = true;
reject(status.err);
} else if (!status.confirmations) {
console.log("REST no confirmations for", txid, status);
} else {
console.log("REST confirmation for", txid, status);
done = true;
resolve(status);
}
}
} catch (e) {
if (!done) {
console.error("REST connection error: txid", txid, e);
}
}
}))();
yield sleep(2e3);
}
}));
connection.removeSignatureListener(subId);
done = true;
console.log("Returning status", status);
return status;
});
}
var SequenceType = /* @__PURE__ */ ((SequenceType2) => {
SequenceType2[SequenceType2["Sequential"] = 0] = "Sequential";
SequenceType2[SequenceType2["Parallel"] = 1] = "Parallel";
SequenceType2[SequenceType2["StopOnFailure"] = 2] = "StopOnFailure";
return SequenceType2;
})(SequenceType || {});
var sendTransactions = (_0, _1, _2, _3, ..._4) => __async(void 0, [_0, _1, _2, _3, ..._4], function* (connection, wallet, instructionSet, signersSet, sequenceType = 1 /* Parallel */, commitment = "singleGossip", successCallback = (txid, ind) => {
}, failCallback = (txid, ind) => false, block, beforeTransactions = [], afterTransactions = []) {
if (!wallet.publicKey)
throw new Error("Wallet not connected");
const unsignedTxns = beforeTransactions;
if (!block) {
block = yield connection.getLatestBlockhash(commitment);
}
for (let i = 0; i < instructionSet.length; i++) {
const instructions = instructionSet[i];
const signers = signersSet[i];
if (instructions.length === 0) {
continue;
}
const transaction = new Transaction();
instructions.forEach((instruction) => transaction.add(instruction));
transaction.recentBlockhash = block.blockhash;
transaction.setSigners(
wallet.publicKey,
...signers.map((s) => s.publicKey)
);
if (signers.length > 0) {
transaction.partialSign(...signers);
}
unsignedTxns.push(transaction);
}
unsignedTxns.push(...afterTransactions);
const partiallySignedTransactions = unsignedTxns.filter(
(t) => t.signatures.find((sig) => sig.publicKey.equals(wallet.publicKey))
);
const fullySignedTransactions = unsignedTxns.filter(
(t) => !t.signatures.find((sig) => sig.publicKey.equals(wallet.publicKey))
);
let signedTxns = yield wallet.signAllTransactions(partiallySignedTransactions);
signedTxns = fullySignedTransactions.concat(signedTxns);
const pendingTxns = [];
console.log("Signed txns length", signedTxns.length, "vs handed in length", instructionSet.length);
for (let i = 0; i < signedTxns.length; i++) {
const signedTxnPromise = sendSignedTransaction({
connection,
signedTransaction: signedTxns[i]
});
if (sequenceType !== 1 /* Parallel */) {
try {
yield signedTxnPromise.then(({ txid, slot }) => successCallback(txid, i));
pendingTxns.push(signedTxnPromise);
} catch (e) {
console.log("Failed at txn index:", i);
console.log("Caught failure:", e);
failCallback(signedTxns[i], i);
if (sequenceType === 2 /* StopOnFailure */) {
return {
number: i,
txs: yield Promise.all(pendingTxns)
};
}
}
} else {
pendingTxns.push(signedTxnPromise);
}
}
if (sequenceType !== 1 /* Parallel */) {
const result = yield Promise.all(pendingTxns);
return { number: signedTxns.length, txs: result };
}
return { number: signedTxns.length, txs: yield Promise.all(pendingTxns) };
});
// src/upload/arweave.ts
function fetchAssetCostToStore(fileSizes) {
return __async(this, null, function* () {
const result = yield calculate(fileSizes);
console.log("Arweave cost estimates:", result);
return result.solana * anchor2.web3.LAMPORTS_PER_SOL;
});
}
function upload(data, manifest, index) {
return __async(this, null, function* () {
console.log(`trying to upload image ${index}: ${manifest.name}`);
const res = yield (yield fetch(ARWEAVE_UPLOAD_ENDPOINT, {
method: "POST",
body: data
})).json();
return res;
});
}
function estimateManifestSize(filenames) {
const paths = {};
for (const name of filenames) {
console.log("name", name);
paths[name] = {
id: "artestaC_testsEaEmAGFtestEGtestmMGmgMGAV438",
ext: getFileExtension(name)
};
}
const manifest = {
manifest: "arweave/paths",
version: "0.1.0",
paths,
index: {
path: "metadata.json"
}
};
const data = Buffer.from(JSON.stringify(manifest), "utf8");
console.log("Estimated manifest size:", data.length);
return data.length;
}
function arweaveUpload(walletKeyPair, anchorProgram, env, image, manifestBuffer, manifest, index) {
return __async(this, null, function* () {
var _a, _b;
const imageExt = image.type;
const estimatedManifestSize = estimateManifestSize([image.name, "metadata.json"]);
const storageCost = yield fetchAssetCostToStore([image.size, manifestBuffer.length, estimatedManifestSize]);
console.log(`lamport cost to store ${image.name}: ${storageCost}`);
const instructions = [
anchor2.web3.SystemProgram.transfer({
fromPubkey: walletKeyPair.publicKey,
toPubkey: ARWEAVE_PAYMENT_WALLET,
lamports: Math.round(storageCost)
})
];
const tx = yield sendTransactionWithRetryWithKeypair(
anchorProgram.provider.connection,
walletKeyPair,
instructions,
"confirmed"
);
console.log(`solana transaction (${env}) for arweave payment:`, tx);
const data = new FormData();
const manifestBlob = new Blob([manifestBuffer], { type: JSON_EXTENSION });
data.append("transaction", tx["txid"]);
data.append("env", env);
data.append("file[]", image, image.name);
data.append("file[]", manifestBlob, "metadata.json");
const result = yield upload(data, manifest, index);
console.log("result", result);
const metadataFile = (_a = result.messages) == null ? void 0 : _a.find((m) => m.filename === "manifest.json");
const imageFile = (_b = result.messages) == null ? void 0 : _b.find((m) => m.filename === image.name);
if (metadataFile == null ? void 0 : metadataFile.transactionId) {
const link = `https://arweave.net/${metadataFile.transactionId}`;
const imageLink = `https://arweave.net/${imageFile.transactionId}?ext=${imageExt.replace(".", "")}`;
console.log(`File uploaded: ${link}`);
console.log(`imageLink uploaded: ${imageLink}`);
return [link, imageLink];
} else {
throw new Error(`No transaction ID for upload: ${index}`);
}
});
}
// src/upload/config.ts
import * as anchor3 from "@project-serum/anchor";
import { getAccount, getMint, TOKEN_PROGRAM_ID as TOKEN_PROGRAM_ID2 } from "@solana/spl-token";
import { PublicKey as PublicKey4 } from "@solana/web3.js";
// src/enums.ts
var StorageType = /* @__PURE__ */ ((StorageType2) => {
StorageType2["ArweaveBundle"] = "arweave-bundle";
StorageType2["ArweaveSol"] = "arweave-sol";
StorageType2["Arweave"] = "arweave";
StorageType2["Ipfs"] = "ipfs";
StorageType2["Aws"] = "aws";
StorageType2["NftStorage"] = "nft-storage";
StorageType2["Pinata"] = "pinata";
return StorageType2;
})(StorageType || {});
// src/mint/helpers.ts
import { web3 as web33 } from "@project-serum/anchor";
var getAtaForMint = (mint, buyer) => __async(void 0, null, function* () {
return yield web33.PublicKey.findProgramAddress(
[buyer.toBuffer(), TOKEN_PROGRAM_ID.toBuffer(), mint.toBuffer()],
SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID
);
});
var getMetadata = (mint) => __async(void 0, null, function* () {
return (yield web33.PublicKey.findProgramAddress(
[Buffer.from("metadata"), TOKEN_METADATA_PROGRAM_ID.toBuffer(), mint.toBuffer()],
TOKEN_METADATA_PROGRAM_ID
))[0];
});
var getMasterEdition = (mint) => __async(void 0, null, function* () {
return (yield web33.PublicKey.findProgramAddress(
[Buffer.from("metadata"), TOKEN_METADATA_PROGRAM_ID.toBuffer(), mint.toBuffer(), Buffer.from("edition")],
TOKEN_METADATA_PROGRAM_ID
))[0];
});
var getCandyMachineCreator = (candyMachine) => __async(void 0, null, function* () {
return yield web33.PublicKey.findProgramAddress([Buffer.from("candy_machine"), candyMachine.toBuffer()], CANDY_MACHINE_PROGRAM_V2_ID);
});
// src/upload/config.ts
function loadCandyProgramV2(provider, customRpcUrl) {
return __async(this, null, function* () {
if (customRpcUrl)
console.log("USING CUSTOM URL", customRpcUrl);
const idl = yield anchor3.Program.fetchIdl(CANDY_MACHINE_PROGRAM_V2_ID, provider);
const program = new anchor3.Program(idl, CANDY_MACHINE_PROGRAM_V2_ID, provider);
console.log("program id from anchor", program.programId.toBase58());
return program;
});
}
function getCandyMachineV2Config(walletKeyPair, configForm, anchorProgram) {
return __async(this, null, function* () {
var _a, _b, _c;
if (configForm === void 0) {
throw new Error("The configForm is undefined");
}
const config = configForm;
const {
storage,
nftStorageKey,
ipfsInfuraProjectId,
number,
ipfsInfuraSecret,
pinataJwt,
pinataGateway,
awsS3Bucket,
noRetainAuthority,
noMutable,
batchSize,
price,
splToken,
splTokenAccount,
solTreasuryAccount,
gatekeeper,
endSettings,
hiddenSettings,
whitelistMintSettings,
goLiveDate,
uuid,
arweaveJwk
} = config;
let wallet;
let parsedPrice = price;
const splTokenAccountFigured = splTokenAccount ? splTokenAccount : splToken ? (yield getAtaForMint(new PublicKey4(splToken), walletKeyPair))[0] : null;
if (splToken) {
if (solTreasuryAccount) {
throw new Error("If spl-token-account or spl-token is set then sol-treasury-account cannot be set");
}
if (!splToken) {
throw new Error("If spl-token-account is set, spl-token must also be set");
}
const splTokenKey = new PublicKey4(splToken);
const splTokenAccountKey = new PublicKey4(splTokenAccountFigured);
if (!splTokenAccountFigured) {
throw new Error("If spl-token is set, spl-token-account must also be set");
}
console.log("anchor program loaded", anchorProgram);
const mintInfo = yield getMint(anchorProgram.provider.connection, splTokenKey, void 0, TOKEN_PROGRAM_ID2);
if (!mintInfo.isInitialized) {
throw new Error("The specified spl-token is not initialized");
}
const tokenAccount = yield getAccount(anchorProgram.provider.connection, splTokenAccountKey, void 0, TOKEN_PROGRAM_ID2);
if (!tokenAccount.isInitialized) {
throw new Error("The specified spl-token-account is not initialized");
}
if (!tokenAccount.mint.equals(splTokenKey)) {
throw new Error(
`The spl-token-account's mint (${tokenAccount.mint.toString()}) does not match specified spl-token ${splTokenKey.toString()}`
);
}
wallet = new PublicKey4(splTokenAccountKey);
parsedPrice = price * 10 ** mintInfo.decimals;
if (whitelistMintSettings && (whitelistMintSettings == null ? void 0 : whitelistMintSettings.discountPrice) || whitelistMintSettings && ((_a = whitelistMintSettings == null ? void 0 : whitelistMintSettings.discountPrice) == null ? void 0 : _a.toNumber()) === 0) {
;
whitelistMintSettings.discountPrice *= 10 ** mintInfo.decimals;
}
} else {
parsedPrice = price * 10 ** 9;
if ((whitelistMintSettings == null ? void 0 : whitelistMintSettings.discountPrice) || ((_b = whitelistMintSettings == null ? void 0 : whitelistMintSettings.discountPrice) == null ? void 0 : _b.toNumber()) === 0) {
;
whitelistMintSettings.discountPrice *= 10 ** 9;
}
wallet = solTreasuryAccount ? new PublicKey4(solTreasuryAccount) : walletKeyPair;
}
if (whitelistMintSettings) {
whitelistMintSettings.mint = new PublicKey4(whitelistMintSettings.mint);
if ((whitelistMintSettings == null ? void 0 : whitelistMintSettings.discountPrice) || ((_c = whitelistMintSettings == null ? void 0 : whitelistMintSettings.discountPrice) == null ? void 0 : _c.toNumber()) === 0) {
whitelistMintSettings.discountPrice = new anchor3.BN(whitelistMintSettings.discountPrice);
}
}
if (endSettings) {
if (endSettings.endSettingType.date) {
endSettings.number = new anchor3.BN(parseDate(endSettings.value));
} else if (endSettings.endSettingType.amount) {
endSettings.number = new anchor3.BN(endSettings.value);
}
delete endSettings.value;
}
if (hiddenSettings) {
const utf8Encode = new TextEncoder();
hiddenSettings.hash = utf8Encode.encode(hiddenSettings.hash.toString());
}
console.log("correct config");
return {
storage,
nftStorageKey,
ipfsInfuraProjectId,
number,
ipfsInfuraSecret,
pinataJwt,
pinataGateway: pinataGateway ? pinataGateway : null,
awsS3Bucket,
retainAuthority: !noRetainAuthority,
mutable: !noMutable,
batchSize,
price: new anchor3.BN(parsedPrice),
treasuryWallet: wallet,
splToken: splToken ? new PublicKey4(splToken) : null,
gatekeeper: gatekeeper ? {
gatekeeperNetwork: new PublicKey4(gatekeeper.gatekeeperNetwork),
expireOnUse: gatekeeper.expireOnUse
} : null,
endSettings,
hiddenSettings,
whitelistMintSettings,
goLiveDate: goLiveDate ? new anchor3.BN(parseDate(goLiveDate)) : null,
uuid,
arweaveJwk
};
});
}
function verifyAssets(files, storage, number) {
let imageFileCount = 0;
let animationFileCount = 0;
let jsonFileCount = 0;
const supportedFiles = files.filter((it) => {
if (SUPPORTED_IMAGE_TYPES.some((e) => e === it.type)) {
imageFileCount++;
} else if (SUPPORTED_ANIMATION_TYPES.some((e) => e === it.type)) {
animationFileCount++;
} else if (it.type == JSON_EXTENSION) {
jsonFileCount++;
} else {
console.warn(`WARNING: Skipping unsupported file type ${it}`);
return false;
}
return true;
});
if (animationFileCount !== 0 && storage === "arweave" /* Arweave */) {
throw new Error(
'The "arweave" storage option is incompatible with animation files. Please try again with another storage option using `--storage <option>`.'
);
}
if (animationFileCount !== 0 && animationFileCount !== jsonFileCount) {
throw new Error(
`number of animation files (${animationFileCount}) is different than the number of json files (${jsonFileCount})`
);
} else if (imageFileCount !== jsonFileCount) {
throw new Error(`number of img files (${imageFileCount}) is different than the number of json files (${jsonFileCount})`);
}
const elemCount = number ? number : imageFileCount;
if (elemCount < imageFileCount) {
throw new Error(
`max number (${elemCount}) cannot be smaller than the number of images in the source folder (${imageFileCount})`
);
}
if (animationFileCount === 0) {
console.info(`Beginning the upload for ${elemCount} (img+json) pairs`);
} else {
console.info(`Beginning the upload for ${elemCount} (img+animation+json) sets`);
}
console.log("supportedFiles", supportedFiles);
return { supportedFiles, elemCount };
}
// src/upload/upload.ts
import { BN as BN2 } from "@project-serum/anchor";
import { PublicKey as PublicKey5 } from "@solana/web3.js";
import { PromisePool } from "@supercharge/promise-pool";
function uploadV2(_0) {
return __async(this, arguments, function* ({
files,
cacheName,
env,
totalNFTs,
storage,
retainAuthority,
mutable,
batchSize,
price,
treasuryWallet,
gatekeeper,
goLiveDate,
endSettings,
whitelistMintSettings,
hiddenSettings,
walletKeyPair,
anchorProgram,
rateLimit
}) {
var _a, _b, _c;
const cacheContent = {
program: {
uuid: "",
candyMachine: ""
},
items: {}
};
const filesNames = files.map((file) => file.name);
const dedupedAssetKeys = getAssetKeysNeedingUpload(cacheContent.items, filesNames);
console.log("dedupedAssetKeys", dedupedAssetKeys);
let candyMachine = void 0;
if (!((_a = cacheContent == null ? void 0 : cacheContent.program) == null ? void 0 : _a.uuid)) {
const firstManifestFile = files.find((file) => file.name === "0.json");
if (!firstManifestFile)
throw new Error("0.json must be present");
const firstAssetManifest = JSON.parse(yield firstManifestFile.text());
try {
if (!((_c = (_b = firstAssetManifest.properties) == null ? void 0 : _b.creators) == null ? void 0 : _c.every((creator) => creator.address !== void 0))) {
throw new Error("Creator address is missing");
}
console.info("initializing candy machine");
const res = yield createCandyMachineV2(
anchorProgram,
walletKeyPair,
treasuryWallet,
{
itemsAvailable: new BN2(totalNFTs),
uuid: null,
symbol: firstAssetManifest.symbol,
sellerFeeBasisPoints: firstAssetManifest.seller_fee_basis_points,
isMutable: mutable,
maxSupply: new BN2(0),
retainAuthority,
gatekeeper,
goLiveDate,
price,
endSettings,
whitelistMintSettings,
hiddenSettings,
creators: firstAssetManifest.properties.creators.map((creator) => ({
address: new PublicKey5(creator.address),
verified: true,
share: creator.share
}))
}
);
console.log("res", res);
cacheContent.program.uuid = res.uuid;
cacheContent.program.candyMachine = res.candyMachine.toBase58();
cacheContent.startDate = goLiveDate;
candyMachine = res.candyMachine;
console.info(`initialized config for a candy machine with publickey: ${res.candyMachine.toBase58()}`);
} catch (exx) {
console.error("Error deploying config to Solana network.", exx);
throw exx;
}
} else {
console.info(
`config for a candy machine with publickey: ${cacheContent == null ? void 0 : cacheContent.program.candyMachine} has been already initialized`
);
}
const uploadedItems = Object.values(cacheContent.items).filter((f) => !!f.link).length;
console.info(`[${uploadedItems}] out of [${totalNFTs}] items have been uploaded`);
if (dedupedAssetKeys.length) {
console.info(`Starting upload for [${dedupedAssetKeys.length}] items, format ${JSON.stringify(dedupedAssetKeys[0])}`);
}
if (dedupedAssetKeys.length) {
yield PromisePool.withConcurrency(batchSize || 1).for(dedupedAssetKeys).handleError((err, asset) => __async(this, null, function* () {
console.error(`
Error uploading ${JSON.stringify(asset)} asset (skipping)`, err.message);
yield sleep(5e3);
})).process((asset) => __async(this, null, function* () {
console.log("processing asset: ", asset);
const jsonFile = files.find((file) => getFileName(file.name) === asset.index && file.type === JSON_EXTENSION);
const imageFile = files.find((file) => getFileName(file.name) === asset.index && file.type.startsWith("image/"));
if (!jsonFile) {
throw new Error(`JSON file ${asset.index}.json is missing`);
}
const manifest = getAssetManifest(asset.index, JSON.parse(yield jsonFile == null ? void 0 : jsonFile.text()));
const manifestBuffer = Buffer.from(JSON.stringify(manifest));
if (!imageFile)
throw new Error(`Image file ${asset.index} is missing`);
let link, imageLink, animationLink;
try {
switch (storage) {
case "arweave" /* Arweave */:
default:
;
[link, imageLink] = yield arweaveUpload(
walletKeyPair,
anchorProgram,
env,
imageFile,
manifestBuffer,
manifest,
Number(asset.index)
);
if (link && imageLink) {
cacheContent.items[asset.index] = {
link,
imageLink,
name: manifest.name,
onChain: false
};
}
}
} catch (err) {
saveCache(cacheName, env, cacheContent);
console.error(err);
}
}));
}
let uploadSuccessful = true;
try {
if (!hiddenSettings && candyMachine) {
uploadSuccessful = yield writeIndices({
anchorProgram,
cacheContent,
cacheName,
env,
candyMachine,
walletKeyPair,
rateLimit
});
console.log("cache content: ", cacheContent);
const uploadedItems2 = Object.values(cacheContent.items).filter((f) => !!f.link).length;
console.log("uploadedItems: ", uploadedItems2);
console.log("totalNFTs: ", totalNFTs);
console.log("uploadSuccessful: ", uploadSuccessful);
uploadSuccessful = uploadSuccessful && uploadedItems2 == totalNFTs;
} else {
console.log("Skipping upload to chain as this is a hidden Candy Machine");
}
console.log(`Done. Successful = ${uploadSuccessful}.`);
return cacheContent.program.candyMachine;
} catch (err) {
console.error(err);
return false;
}
});
}
function getAssetKeysNeedingUpload(items, files) {
const all = [.../* @__PURE__ */ new Set([...Object.keys(items), ...files])];
const keyMap = {};
const assets = all.filter((k) => !k.includes(".json")).reduce((acc, assetKey) => {
var _a;
const key = getFileName(assetKey);
const ext = getFileExtension(assetKey);
if (!((_a = items[key]) == null ? void 0 : _a.link) && !keyMap[key]) {
keyMap[key] = true;
acc.push({ mediaExt: ext, index: key });
}
return acc;
}, []).sort((a, b) => Number.parseInt(a.index, 10) - Number.parseInt(b.index, 10));
console.log(assets);
return assets;
}
function getAssetManifest(assetIndex, manifest) {
manifest.image = manifest.image.replace("image", assetIndex);
if ("animation_url" in manifest) {
manifest.animation_url = manifest.animation_url.replace("animation_url", assetIndex);
}
return manifest;
}
function writeIndices(_0) {
return __async(this, arguments, function* ({
anchorProgram,
cacheContent,
cacheName,
env,
candyMachine,
walletKeyPair,
rateLimit
}) {
let uploadSuccessful = true;
const keys = Object.keys(cacheContent.items);
const poolArray = [];
const allIndicesInSlice = Array.from(Array(keys.length).keys());
let offset = 0;
while (offset < allIndicesInSlice.length) {
let length = 0;
let lineSize = 0;
let configLines = allIndicesInSlice.slice(offset, offset + 16);
while (length < 850 && lineSize < 16 && configLines[lineSize] !== void 0) {
length += cacheContent.items[keys[configLines[lineSize]]].link.length + cacheContent.items[keys[configLines[lineSize]]].name.length;
if (length < 850)
lineSize++;
}
configLines = allIndicesInSlice.slice(offset, offset + lineSize);
offset += lineSize;
const onChain = configLines.filter((i) => {
var _a;
return ((_a = cacheContent.items[keys[i]]) == null ? void 0 : _a.onChain) || false;
});
const index = keys[configLines[0]];
if (onChain.length != configLines.length) {
poolArray.push({ index, configLines });
}
}
console.info(`Writing all indices in ${poolArray.length} transactions...`);
const addConfigLines = (_02) => __async(this, [_02], function* ({ index, configLines }) {
const response = yield anchorProgram.methods.addConfigLines(
index,
configLines.map((i) => ({
uri: cacheContent.items[keys[i]].link,
name: cacheContent.items[keys[i]].name
}))
).accounts({
candyMachine,
authority: walletKeyPair.publicKey
}).signers([]).rpc();
console.log(response);
configLines.forEach((i) => {
cacheContent.items[keys[i]] = __spreadProps(__spreadValues({}, cacheContent.items[keys[i]]), {
onChain: true,
verifyRun: false
});
});
});
yield PromisePool.withConcurrency(rateLimit || 5).for(poolArray).handleError((_02, _1) => __async(this, [_02, _1], function* (err, { index, configLines }) {
console.error(`
Failed writing indices ${index}-${keys[configLines[configLines.length - 1]]}: ${err.message}`);
yield sleep(5e3);
uploadSuccessful = false;
})).process((_02) => __async(this, [_02], function* ({ index, configLines }) {
yield addConfigLines({ index, configLines });
}));
saveCache(cacheName, env, cacheContent);
return uploadSuccessful;
});
}
// src/mint/mint.ts
import { web3 as web35 } from "@project-serum/anchor";
import { SYSVAR_SLOT_HASHES_PUBKEY, SystemProgram as SystemProgram2 } from "@solana/web3.js";
import {
createInitializeMintInstruction,
createMintToInstruction,
createAssociatedTokenAccountInstruction,
MintLayout
} from "@solana/spl-token";
var getCollectionPDA = (candyMachineAddress) => __async(void 0, null, function* () {
return yield web35.PublicKey.findProgramAddress(
[Buffer.from("collection"), candyMachineAddress.toBuffer()],
CANDY_MACHINE_PROGRAM_V2_ID
);
});
var getCollectionAuthorityRecordPDA = (mint, newAuthority) => __async(void 0, null, function* () {
return (yield web35.PublicKey.findProgramAddress(
[
Buffer.from("metadata"),
TOKEN_METADATA_PROGRAM_ID.toBuffer(),
mint.toBuffer(),
Buffer.from("collection_authority"),
newAuthority.toBuffer()
],
TOKEN_METADATA_PROGRAM_ID
))[0];
});
var createAccountsForMint = (candyMachine, payer) => __async(void 0, null, function* () {
const mint = web35.Keypair.generate();
const userTokenAccountAddress = (yield getAtaForMint(mint.publicKey, payer))[0];
const signers = [mint];
if (!candyMachine)
return;
const instructions = [
web35.SystemProgram.createAccount({
fromPubkey: payer,
newAccountPubkey: mint.publicKey,
space: MintLayout.span,
lamports: yield candyMachine.program.provider.connection.getMinimumBalanceForRentExemption(MintLayout.span),
programId: TOKEN_PROGRAM_ID
}),
createInitializeMintInstruction(mint.publicKey, 0, payer, payer, TOKEN_PROGRAM_ID),
createAssociatedTokenAccountInstruction(payer, userTokenAccountAddress, payer, mint.publicKey),
createMintToInstruction(mint.publicKey, userTokenAccountAddress, payer, 1, [], TOKEN_PROGRAM_ID)
];
const sendTx = yield sendTransactions(
candyMachine.program.provider.connection,
candyMachine.program.provider.wallet,
[instructions],
[signers],
2 /* StopOnFailure */,
"singleGossip",
() => {
},
() => false,
void 0,
[],
[]
);
console.log(sendTx);
const txid = sendTx.txs[0].txid;
return {
mint,
userTokenAccount: userTokenAccountAddress,
transaction: txid
};
});
var mintOneNft = (_0, _1, ..._2) => __async(void 0, [_0, _1, ..._2], function* (candyMachine, payer, beforeTransactions = [], afterTransactions = [], setupState) {
var _a;
const mint = (_a = setupState == null ? void 0 : setupState.mint) != null ? _a : web35.Keypair.generate();
const userTokenAccountAddress = (yield getAtaForMint(mint.publicKey, payer))[0];
if (!candyMachine)
return null;
const userPayingAccountAddress = candyMachine.state.tokenMint ? (yield getAtaForMint(candyMachine.state.tokenMint, payer))[0] : payer;
const candyMachineAddress = candyMachine.id;
const remainingAccounts = [];
const instructions = [];
const signers = [];
if (!setupState) {
signers.push(mint);
instructions.push(
...[
web35.SystemProgram.createAccount({
fromPubkey: payer,
newAccountPubkey: mint.publicKey,
space: MintLayout.span,
lamports: yield candyMachine.program.provider.connection.getMinimumBalanceForRentExemption(MintLayout.span),
programId: TOKEN_PROGRAM_ID
}),
createInitializeMintInstruction(mint.publicKey, 0, payer, payer, TOKEN_PROGRAM_ID),
createAssociatedTokenAccountInstruction(payer, userTokenAccountAddress, payer, mint.publicKey),
createMintToInstruction(mint.publicKey, userTokenAccountAddress, payer, 1, [], TOKEN_PROGRAM_ID)
]
);
}
if (candyMachine.state.whitelistMintSettings) {
const mint2 = new web35.PublicKey(candyMachine.state.whitelistMintSettings.mint);
const whitelistToken = (yield getAtaForMint(mint2, payer))[0];
remainingAccounts.push({
pubkey: whitelistToken,
isWritable: true,
isSigner: false
});
if (candyMachine.state.whitelistMintSettings.mode.burnEveryTime) {
remainingAccounts.push({
pubkey: mint2,
isWritable: true,
isSigner: false
});
remainingAccounts.push({
pubkey: payer,
isWritable: false,
isSigner: true
});
}
}
if (candyMachine.state.tokenMint) {
remainingAccounts.push({
pubkey: userPayingAccountAddress,
isWritable: true,
isSigner: false
});
remainingAccounts.push({
pubkey: payer,
isWritable: false,
isSigner: true
});
}
const metadataAddress = yield getMetadata(mint.publicKey);
const masterEdition = yield getMasterEdition(mint.publicKey);
const [candyMachineCreator, creatorBump] = yield getCandyMachineCreator(candyMachineAddress);
instructions.push(
yield candyMachine.program.instruction.mintNft(creatorBump, {
accounts: {
candyMachine: candyMachineAddress,
candyMachineCreator,
payer,
wallet: candyMachine.state.treasury,
mint: mint.publicKey,
metadata: metadataAddress,
masterEdition,
mintAuthority: payer,
updateAuthority: payer,
tokenMetadataProgram: TOKEN_METADATA_PROGRAM_ID,
tokenProgram: TOKEN_PROGRAM_ID,
systemProgram: SystemProgram2.programId,
rent: web35.SYSVAR_RENT_PUBKEY,
clock: web35.SYSVAR_CLOCK_PUBKEY,
recentBlockhashes: SYSVAR_SLOT_HASHES_PUBKEY,
instructionSysvarAccount: web35.SYSVAR_INSTRUCTIONS_PUBKEY
},
remainingAccounts: remainingAccounts.length > 0 ? remainingAccounts : void 0
})
);
const [collectionPDA] = yield getCollectionPDA(candyMachineAddress);
const collectionPDAAccount = yield candyMachine.program.provider.connection.getAccountInfo(collectionPDA);
if (collectionPDAAccount && candyMachine.state.retainAuthority) {
try {
const collectionData = yield candyMachine.program.account.collectionPda.fetch(collectionPDA);
const collectionMint = collectionData.mint;
const collectionAuthorityRecord = yield getCollectionAuthorityRecordPDA(collectionMint, collectionPDA);
if (collectionMint) {
const collectionMetadata = yield getMetadata(collectionMint);
const collectionMasterEdition = yield getMasterEdition(collectionMint);
instructions.push(
yield candyMachine.program.instruction.setCollectionDuringMint({
accounts: {
candyMachine: candyMachineAddress,
metadata: metadataAddress,
payer,
collectionPda: collectionPDA,
tokenMetadataProgram: TOKEN_METADATA_PROGRAM_ID,
instructions: web35.SYSVAR_INSTRUCTIONS_PUBKEY,
collectionMint,
collectionMetadata,
collectionMasterEdition,
authority: candyMachine.state.authority,
collectionAuthorityRecord
}
})
);
}
} catch (error) {
console.error(error);
}
}
try {
const txns = (yield sendTransactions(
candyMachine.program.provider.connection,
candyMachine.program.provider.wallet,
[instructions],
[signers],
2 /* StopOnFailure */,
"singleGossip",
() => {
},
() => false,
void 0,
beforeTransactions,
afterTransactions
)).txs.map((t) => t.txid);
const mintTxn = txns[0];
return {
mintTxId: mintTxn,
metadataKey: metadataAddress
};
} catch (e) {
console.log(e);
}
return null;
});
export {
A,
ARWEAVE_PAYMENT_WALLET,
ARWEAVE_UPLOAD_ENDPOINT,
AUCTION_HOUSE,
AUCTION_HOUSE_PROGRAM_ID,
B,
CACHE_PATH,
CANDY_MACHINE,
CANDY_MACHINE_PROGRAM_ID,
CANDY_MACHINE_PROGRAM_V2_ID,
CONFIG_ARRAY_START,
CONFIG_ARRAY_START_V2,
CONFIG_LINE_SIZE,
CONFIG_LINE_SIZE_V2,
DEFAULT_GATEKEEPER,
DEFAULT_TIMEOUT,
ESCROW,
FAIR_LAUNCH_PROGRAM_ID,
FEE_PAYER,
JSON_EXTENSION,
MAX_CREATOR_LEN,
MAX_CREATOR_LIMIT,
MAX_NAME_LENGTH,
MAX_SYMBOL_LENGTH,
MAX_URI_LENGTH,
NOTIFICATION_TIMEOUT_DEFAULT,
NOTIFICATION_TIMEOUT_NEVER,
SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID,
SUPPORTED_ANIMATION_TYPES,
SUPPORTED_IMAGE_TYPES,
SequenceType,
StorageType,
TOKEN_ENTANGLEMENT_PROGRAM_ID,
TOKEN_ENTANGLER,
TOKEN_METADATA_PROGRAM_ID,
TOKEN_PROGRAM_ID,
TREASURY,
WRAPPED_SOL_MINT,
arweaveUpload,
awaitTransactionSignatureConfirmation,
createAccountsForMint,
createCandyMachineV2,
createCandyMachineV2Account,
getAssetManifest,
getAtaForMint,
getCandyMachineCreator,
getCandyMachineV2Config,
getCollectionPDA,
getFileExtension,
getFileName,
getMasterEdition,
getMetadata,
getUnixTs,
loadCandyProgramV2,
mintOneNft,
parseDate,
saveCache,
sendSignedTransaction,
sendTransactionWithRetryWithKeypair,
sendTransactions,
sleep,
updateV2,
uploadV2,
uuidFromConfigPubkey,
verifyAssets
};
//# sourceMappingURL=index.mjs.map