Visión general

La acuñación es el proceso de crear nuevos tokens NFT dentro de tu colección. CreateKit proporciona un sistema de acuñación en dos fases único que optimiza la eficiencia del gas y la experiencia del usuario.

Fases de Acuñación

Fase 1: Despliegue y Primera Acuñación

La primera operación de acuñación es especial: despliega el contrato de la colección y acuña el primer token en una sola transacción:
Despliegue y Primera Acuñación
import { CollectionManager } from "@b3dotfun/basemint";

const collectionManager = new CollectionManager(publicClient);

// Supongamos que tenemos metadatos de la colección y firma del creador
const predictedAddress = collectionManager.predictCollectionAddress(collectionMetadata, creatorSignature);

// Generar firma del desplegador
const deployerSignature = await collectionManager.generateDeployerSignature(walletClient, predictedAddress);

// Crear instancia de la colección
const collection = collectionManager.createCollection(predictedAddress, collectionMetadata.tokenStandard);

// Desplegar y acuñar el primer NFT
const mintTx = await collection.mint(
  walletClient,
  1n, // cantidad
  undefined, // URI de metadatos (usa baseURI)
  collectionMetadata.mintPrice || 0n,
  [], // prueba de lista blanca (vacía para público)
  creatorSignature, // Requerido para la primera acuñación
  deployerSignature, // Requerido para la primera acuñación
);

console.log(`🚀 Colección desplegada y primer token acuñado: ${mintTx}`);

Fase 2: Acuñación Regular

Después del despliegue, las acuñaciones subsiguientes son más simples y solo requieren los parámetros estándar:
Acuñación Regular
// Para acuñaciones subsiguientes (después del despliegue)
const regularMintTx = await collection.mint(
  walletClient,
  1n, // cantidad
  undefined, // URI de metadatos
  collectionMetadata.mintPrice || 0n,
  [], // prueba de lista blanca
  // No se necesitan firmas para acuñaciones regulares
);

console.log(`✨ Token acuñado: ${regularMintTx}`);

Estándares de Token

CreateKit soporta tanto los estándares ERC721 como ERC1155 con diferentes comportamientos de acuñación:
Tokens Únicos ERC721
// ERC721 - Cada token es único
const erc721Collection = collectionManager.createCollection(
  predictedAddress,
  "ERC721"
)

// ERC721 siempre acuña cantidad de 1
await erc721Collection.mint(
walletClient,
1n, // Siempre 1 para ERC721
"https://example.com/metadata/1.json", // Metadatos únicos para este token
parseEther("0.01"),
[]
)

// Cada acuñación crea un nuevo ID de token único
// Los IDs de token incrementan: 1, 2, 3, etc.

Precios y Pagos

Precios Fijos

Acuñación a Precio Fijo
import { parseEther } from "viem";

const fixedPriceCollection = {
  name: "Colección a Precio Fijo",
  symbol: "FPC",
  creator: account.address,
  gameOwner: account.address,
  mintPrice: parseEther("0.01"), // 0.01 ETH por token
  maxPerWallet: 5n,
};

// Acuñar con precio fijo
await collection.mint(
  walletClient,
  2n, // cantidad
  undefined,
  parseEther("0.02"), // 2 * 0.01 ETH
  [],
);

Acuñación Gratuita

Acuñación Gratuita
const freeCollection = {
  name: "Colección Gratuita",
  symbol: "FREE",
  creator: account.address,
  gameOwner: account.address,
  mintPrice: 0n, // Acuñación gratuita
  maxPerWallet: 10n,
};

// Acuñar gratis (solo costos de gas)
await collection.mint(
  walletClient,
  1n,
  undefined,
  0n, // No se requiere pago
  [],
);

Precios Dinámicos

Lógica de Precios Dinámicos
// Lógica de precios personalizada en tu aplicación
function calculateMintPrice(totalSupply: bigint, basePrice: bigint): bigint {
  // El precio aumenta con la oferta
  const priceMultiplier = totalSupply / 1000n + 1n;
  return basePrice * priceMultiplier;
}

// Obtener oferta actual y calcular precio
const currentSupply = await collection.totalSupply();
const dynamicPrice = calculateMintPrice(currentSupply, parseEther("0.001"));

await collection.mint(walletClient, 1n, undefined, dynamicPrice, []);

Acuñación con Lista Blanca

CreateKit soporta listas blancas basadas en árboles de Merkle para acuñaciones exclusivas:

Configuración de Lista Blanca

Configuración de Lista Blanca
import { WhitelistManager } from "@b3dotfun/basemint";

// Crear lista blanca con direcciones
const whitelist = new WhitelistManager([
  { address: "0x1234567890123456789012345678901234567890" },
  { address: "0x2345678901234567890123456789012345678901" },
  { address: "0x3456789012345678901234567890123456789012" },
]);

// Obtener raíz de Merkle para el despliegue de la colección
const merkleRoot = whitelist.getRoot();

const whitelistCollection = {
  name: "Colección con Lista Blanca",
  symbol: "WLC",
  creator: account.address,
  gameOwner: account.address,
  isWhitelistEnabled: true,
  whitelistMerkleRoot: merkleRoot,
  mintPrice: parseEther("0.005"),
};

Acuñación con Prueba de Lista Blanca

Acuñación con Prueba de Lista Blanca
// Obtener prueba para la dirección de acuñación
const userAddress = account.address;
const proof = whitelist.getProof(userAddress);

// Verificar que el usuario está en la lista blanca (verificación opcional)
const isWhitelisted = whitelist.verify(userAddress, proof);
if (!isWhitelisted) {
  throw new Error("Dirección no está en la lista blanca");
}

// Acuñar con prueba de lista blanca
await collection.mint(
  walletClient,
  1n,
  undefined,
  parseEther("0.005"),
  proof, // Proporcionar prueba de lista blanca
);

Límites y Controles de Acuñación

Límites por Cartera

Límites de Cartera
// Establecer tokens máximos por cartera
const limitedCollection = {
  name: "Colección Limitada",
  symbol: "LTD",
  creator: account.address,
  gameOwner: account.address,
  maxPerWallet: 3n, // Máximo 3 tokens por cartera
  maxSupply: 1000n,
};

// Verificar saldo actual antes de acuñar
const currentBalance = await collection.balanceOf(account.address);
const maxPerWallet = await collection.maxPerWallet();

if (currentBalance >= maxPerWallet) {
  throw new Error("Límite de cartera excedido");
}

await collection.mint(walletClient, 1n, undefined, 0n, []);

Controles Basados en Tiempo

Controles de Tiempo
const timedCollection = {
  name: "Lanzamiento Programado",
  symbol: "TIME",
  creator: account.address,
  gameOwner: account.address,
  startTime: BigInt(Math.floor(Date.now() / 1000) + 3600), // Comienza en 1 hora
  endTime: BigInt(Math.floor(Date.now() / 1000) + 86400), // Termina en 24 horas
};

// Verificar si la acuñación está activa actualmente
const currentTime = BigInt(Math.floor(Date.now() / 1000));
const startTime = await collection.startTime();
const endTime = await collection.endTime();

const isMintingActive = currentTime >= startTime && currentTime <= endTime;

if (!isMintingActive) {
  throw new Error("Acuñación no activa actualmente");
}

Manejo de Metadatos

Metadatos Automáticos

CreateKit puede generar automáticamente metadatos basados en la configuración de la colección:
Metadatos Auto-Generados
// Usando baseURI para metadatos automáticos
const autoMetadataCollection = {
  name: "Colección de Metadatos Automáticos",
  symbol: "AMC",
  creator: account.address,
  gameOwner: account.address,
  // baseURI será generado automáticamente por BaseMint CDN
};

// Acuñar con metadatos automáticos (pasar undefined para URI)
await collection.mint(
  walletClient,
  1n,
  undefined, // Usa baseURI + tokenId
  0n,
  [],
);

// Los metadatos estarán disponibles en: {baseURI}/{tokenId}

Metadatos Personalizados

URIs de Metadatos Personalizados
// Proporcionar URI de metadatos específicos para cada token
const customMetadataURIs = [
  "https://myapi.com/metadata/special-sword.json",
  "https://myapi.com/metadata/rare-shield.json",
  "https://myapi.com/metadata/epic-helmet.json",
];

for (const metadataURI of customMetadataURIs) {
  await collection.mint(
    walletClient,
    1n,
    metadataURI, // Metadatos personalizados para este token
    parseEther("0.01"),
    [],
  );
}

Acuñación por Lotes

Para colecciones ERC1155, puedes acuñar múltiples tokens de manera eficiente:
Acuñación por Lotes
// Una transacción, múltiples tokens
await erc1155Collection.mint(
  walletClient,
  10n, // Acuñar 10 tokens del mismo tipo
  "https://example.com/metadata/resource.json",
  parseEther("0.001") * 10n, // Precio total por todos los tokens
  [],
);

// Para diferentes tipos de tokens, usa transacciones separadas
const tokenTypes = [
  { uri: "https://example.com/wood.json", quantity: 5n },
  { uri: "https://example.com/stone.json", quantity: 3n },
  { uri: "https://example.com/gold.json", quantity: 1n },
];

for (const tokenType of tokenTypes) {
  await erc1155Collection.mint(walletClient, tokenType.quantity, tokenType.uri, calculatePrice(tokenType.quantity), []);
}

Manejo de Errores

Manejo de Errores Integral
async function safeMint(
  collection: any,
  walletClient: any,
  quantity: bigint,
  metadataURI: string | undefined,
  mintPrice: bigint,
  proof: string[],
) {
  try {
    // Validaciones previas a la acuñación
    const isDeployed = await collection.isDeployed();
    if (!isDeployed) {
      throw new Error("Colección aún no desplegada");
    }

    const currentSupply = await collection.totalSupply();
    const maxSupply = await collection.maxSupply();
    if (currentSupply + quantity > maxSupply) {
      throw new Error("Se superaría el máximo de suministro");
    }

    const userBalance = await collection.balanceOf(walletClient.account.address);
    const maxPerWallet = await collection.maxPerWallet();
    if (userBalance + quantity > maxPerWallet) {
      throw new Error("Se superaría el límite de la cartera");
    }

    // Verificar cantidad de pago
    const requiredPayment = (await collection.mintPrice()) * quantity;
    if (mintPrice < requiredPayment) {
      throw new Error("Pago insuficiente");
    }

    // Intentar acuñar
    const tx = await collection.mint(walletClient, quantity, metadataURI, mintPrice, proof);

    console.log(`✅ Acuñación exitosa: ${tx}`);
    return tx;
  } catch (error: any) {
    if (error.message.includes("Invalid merkle proof")) {
      console.error("❌ Dirección no en lista blanca");
    } else if (error.message.includes("Insufficient payment")) {
      console.error("❌ Precio de acuñación incorrecto");
    } else if (error.message.includes("Max per wallet exceeded")) {
      console.error("❌ Límite de cartera alcanzado");
    } else {
      console.error("❌ Acuñación fallida:", error.message);
    }
    throw error;
  }
}

Optimización de Gas

Patrones de Acuñación Eficientes

Acuñación Eficiente de Gas
// Para ERC1155: Acuñar múltiples tokens en una transacción
await erc1155Collection.mint(
  walletClient,
  10n, // Más eficiente en gas que 10 transacciones separadas
  metadataURI,
  totalPrice,
  proof,
);

// Para ERC721: Considerar operaciones por lotes a nivel de aplicación
const mintPromises = [];
for (let i = 0; i < 5; i++) {
  mintPromises.push(collection.mint(walletClient, 1n, undefined, mintPrice, proof));
}

// Ejecutar acuñaciones concurrentemente (tener cuidado con la gestión de nonce)
const results = await Promise.all(mintPromises);

Gestión del Precio del Gas

Optimización del Precio del Gas
import { createWalletClient, http } from "viem";

// Configuración de gas personalizada
const optimizedWalletClient = createWalletClient({
  chain: b3Testnet,
  transport: http(),
  account,
  // Configuración de gas
  gasPrice: parseGwei("20"), // Precio de gas personalizado
});

// O usar precios de gas dinámicos
const gasPrice = await publicClient.getGasPrice();
const adjustedGasPrice = (gasPrice * 110n) / 100n; // 10% por encima del precio actual

await collection.mint(optimizedWalletClient, 1n, undefined, mintPrice, proof, {
  gasPrice: adjustedGasPrice,
});

Monitoreo y Análisis

Seguimiento de Eventos de Acuñación

Monitoreo de Eventos
import { getCollectionMintEvents } from "@b3dotfun/basemint";

// Seguir eventos de acuñación
const fromBlock = (await publicClient.getBlockNumber()) - 1000n;
const toBlock = await publicClient.getBlockNumber();

const mintEvents = await getCollectionMintEvents(publicClient, collection.address, "ERC721", fromBlock, toBlock);

console.log(
  "Acuñaciones recientes:",
  mintEvents.map(event => ({
    minter: event.args.minter,
    tokenId: event.args.tokenId?.toString(),
    quantity: event.args.quantity?.toString(),
    blockNumber: event.blockNumber,
  })),
);

Monitoreo en Tiempo Real

Monitoreo de Acuñaciones en Tiempo Real
// Observar nuevos eventos de acuñación
const unwatch = publicClient.watchContractEvent({
  address: collection.address,
  abi: collection.abi,
  eventName: "Transfer", // o 'TransferSingle' para ERC1155
  onLogs: logs => {
    logs.forEach(log => {
      console.log("Nueva acuñación detectada:", {
        from: log.args.from,
        to: log.args.to,
        tokenId: log.args.tokenId?.toString(),
      });
    });
  },
});

// Detener la observación cuando se termine
// unwatch()

Mejores Prácticas

Experiencia del Usuario

  • Proporcionar retroalimentación clara durante el proceso de acuñación - Mostrar costos estimados de gas por adelantado - Implementar estados de carga adecuados - Manejar errores de manera amigable para el usuario

Interacción con Contratos Inteligentes

  • Validar siempre los parámetros antes de las transacciones - Implementar manejo de errores adecuado - Usar límites y precios de gas apropiados - Probar exhaustivamente